Skip to content

Commit d868542

Browse files
committed
feat: update fee asset price calculation
1 parent 4c3ff2c commit d868542

File tree

36 files changed

+12439
-12250
lines changed

36 files changed

+12439
-12250
lines changed

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,31 @@ Aztec is in full-speed development. Literally every version breaks compatibility
99

1010
## TBD
1111

12+
### [L1 Contracts] Fee asset pricing direction inverted
13+
14+
The fee model now uses `ethPerFeeAsset` instead of the previous `feeAssetPerEth`. This change inverts how the exchange rate is represented: values now express how much ETH one fee asset (AZTEC) is worth, with 1e12 precision.
15+
16+
**Key changes:**
17+
18+
- `FeeHeader.feeAssetPerEth``FeeHeader.ethPerFeeAsset`
19+
- `RollupConfigInput` now requires `initialEthPerFeeAsset` parameter at deployment
20+
- Default value: `1e7` (0.00001 ETH per AZTEC)
21+
- Valid range: `100` (1e-10 ETH/AZTEC) to `1e11` (0.1 ETH/AZTEC)
22+
23+
**New environment variable for node operators:**
24+
25+
- `AZTEC_INITIAL_ETH_PER_FEE_ASSET` - Sets the initial ETH per fee asset price with 1e12 precision
26+
27+
### [L1 Contracts] Fee asset price modifier now in basis points
28+
29+
The `OracleInput.feeAssetPriceModifier` field now expects values in basis points (BPS) instead of the previous representation. The modifier is applied as a percentage change to the ETH/AZTEC price each checkpoint.
30+
31+
**Key changes:**
32+
33+
- Valid range: `-100` to `+100` BPS (±1% max change per checkpoint)
34+
- A value of `+100` increases the price by 1%, `-100` decreases by 1%
35+
- Validated by `MAX_FEE_ASSET_PRICE_MODIFIER_BPS = 100`
36+
1237
### [Aztec.js] Wallet batching now supports all methods
1338

1439
The `BatchedMethod` type is now a discriminated union that ensures type safety: the `args` must match the specific method `name`. This prevents runtime errors from mismatched arguments.

docs/docs-network/operators/reference/changelog/v4.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,20 @@ The `getL2Tips()` RPC endpoint now returns a restructured response with addition
7474

7575
## New features
7676

77+
### Initial ETH per fee asset configuration
78+
79+
A new environment variable `AZTEC_INITIAL_ETH_PER_FEE_ASSET` has been added to configure the initial exchange rate between ETH and the fee asset (AZTEC) at contract deployment. This value uses 1e12 precision.
80+
81+
**Default**: `10000000` (0.00001 ETH per AZTEC)
82+
83+
**Configuration:**
84+
85+
```bash
86+
--initialEthPerFeeAsset <value> ($AZTEC_INITIAL_ETH_PER_FEE_ASSET)
87+
```
88+
89+
This replaces the previous hardcoded default and allows network operators to set the starting price point for the fee asset.
90+
7791
## Changed defaults
7892

7993
## Troubleshooting

l1-contracts/script/deploy/RollupConfiguration.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {CheatDepositArgs} from "@aztec/mock/MultiAdder.sol";
99
import {IRewardDistributor} from "@aztec/governance/interfaces/IRewardDistributor.sol";
1010
import {IBoosterCore} from "@aztec/core/reward-boost/RewardBooster.sol";
1111
import {SlasherFlavor} from "@aztec/core/interfaces/ISlasher.sol";
12-
import {EthValue} from "@aztec/core/libraries/rollup/FeeLib.sol";
12+
import {EthValue, EthPerFeeAssetE12} from "@aztec/core/libraries/rollup/FeeLib.sol";
1313
import {GenesisState, RollupConfigInput} from "@aztec/core/interfaces/IRollup.sol";
1414
import {Timestamp} from "@aztec/core/libraries/TimeLib.sol";
1515
import {RewardBoostConfig} from "@aztec/core/reward-boost/RewardBooster.sol";
@@ -133,6 +133,7 @@ contract RollupConfiguration is IRollupConfiguration, Test {
133133
exitDelaySeconds: vm.envUint("AZTEC_EXIT_DELAY_SECONDS"),
134134
version: 0, // Computed below
135135
provingCostPerMana: EthValue.wrap(vm.envUint("AZTEC_PROVING_COST_PER_MANA")),
136+
initialEthPerFeeAsset: EthPerFeeAssetE12.wrap(vm.envUint("AZTEC_INITIAL_ETH_PER_FEE_ASSET")),
136137
rewardConfig: this.getRewardConfiguration(_rewardDistributor),
137138
rewardBoostConfig: this.getRewardBoostConfiguration(),
138139
stakingQueueConfig: this.getStakingQueueConfiguration(),

l1-contracts/src/core/Rollup.sol

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
PublicInputArgs,
1111
L1FeeData,
1212
ManaMinFeeComponents,
13-
FeeAssetPerEthE9,
13+
EthPerFeeAssetE12,
1414
CheckpointHeaderValidationFlags,
1515
FeeHeader,
1616
RollupConfigInput
@@ -519,7 +519,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
519519
}
520520

521521
function getProvingCostPerManaInFeeAsset() external view override(IRollup) returns (FeeAssetValue) {
522-
return FeeLib.getProvingCostPerMana().toFeeAsset(getFeeAssetPerEth());
522+
return FeeLib.getProvingCostPerMana().toFeeAsset(getEthPerFeeAsset());
523523
}
524524

525525
function getVersion() external view override(IHaveVersion) returns (uint256) {
@@ -656,12 +656,13 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
656656
}
657657

658658
/**
659-
* @notice Gets the fee asset price as fee_asset / eth with 1e9 precision
659+
* @notice Gets the fee asset price as eth / fee_asset with 1e12 precision
660+
* Higher value = more expensive fee asset
660661
*
661662
* @return The fee asset price
662663
*/
663-
function getFeeAssetPerEth() public view override(IRollup) returns (FeeAssetPerEthE9) {
664-
return FeeLib.getFeeAssetPerEthAtCheckpoint(STFLib.getStorage().tips.getPending());
664+
function getEthPerFeeAsset() public view override(IRollup) returns (EthPerFeeAssetE12) {
665+
return FeeLib.getEthPerFeeAssetAtCheckpoint(STFLib.getStorage().tips.getPending());
665666
}
666667

667668
function getEpochForCheckpoint(uint256 _checkpointNumber) public view override(IRollup) returns (Epoch) {

l1-contracts/src/core/RollupCore.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
311311

312312
rollupStore.config.feeAssetPortal = IFeeJuicePortal(inbox.getFeeAssetPortal());
313313

314-
FeeLib.initialize(_config.manaTarget, _config.provingCostPerMana);
314+
FeeLib.initialize(_config.manaTarget, _config.provingCostPerMana, _config.initialEthPerFeeAsset);
315315
}
316316

317317
/**

l1-contracts/src/core/interfaces/IRollup.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {IVerifier} from "@aztec/core/interfaces/IVerifier.sol";
88
import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol";
99
import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol";
1010
import {CheckpointLog, CompressedTempCheckpointLog} from "@aztec/core/libraries/compressed-data/CheckpointLog.sol";
11-
import {FeeAssetPerEthE9, EthValue, FeeAssetValue} from "@aztec/core/libraries/compressed-data/fees/FeeConfig.sol";
11+
import {EthPerFeeAssetE12, EthValue, FeeAssetValue} from "@aztec/core/libraries/compressed-data/fees/FeeConfig.sol";
1212
import {FeeHeader, L1FeeData} from "@aztec/core/libraries/compressed-data/fees/FeeStructs.sol";
1313
import {StakingQueueConfig} from "@aztec/core/libraries/compressed-data/StakingQueueConfig.sol";
1414
import {CompressedChainTips, ChainTips} from "@aztec/core/libraries/compressed-data/Tips.sol";
@@ -75,6 +75,7 @@ struct RollupConfigInput {
7575
uint256 exitDelaySeconds;
7676
uint32 version;
7777
EthValue provingCostPerMana;
78+
EthPerFeeAssetE12 initialEthPerFeeAsset;
7879
RewardConfig rewardConfig;
7980
RewardBoostConfig rewardBoostConfig;
8081
StakingQueueConfig stakingQueueConfig;
@@ -199,7 +200,7 @@ interface IRollup is IRollupCore, IHaveVersion {
199200
returns (ManaMinFeeComponents memory);
200201
function getManaMinFeeAt(Timestamp _timestamp, bool _inFeeAsset) external view returns (uint256);
201202
function getL1FeesAt(Timestamp _timestamp) external view returns (L1FeeData memory);
202-
function getFeeAssetPerEth() external view returns (FeeAssetPerEthE9);
203+
function getEthPerFeeAsset() external view returns (EthPerFeeAssetE12);
203204

204205
function getEpochForCheckpoint(uint256 _checkpointNumber) external view returns (Epoch);
205206
function canPruneAtTime(Timestamp _ts) external view returns (bool);

l1-contracts/src/core/libraries/Errors.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ library Errors {
184184
error FeeLib__InvalidFeeAssetPriceModifier(); // 0xf2fb32ad
185185
error FeeLib__AlreadyPreheated();
186186
error FeeLib__InvalidManaLimit(uint256 maximum, uint256 provided);
187+
error FeeLib__InvalidInitialEthPerFeeAsset(uint256 provided, uint256 minimum, uint256 maximum);
187188

188189
// SignatureLib (duplicated)
189190
error SignatureLib__InvalidSignature(address, address); // 0xd9cbae6c

l1-contracts/src/core/libraries/compressed-data/fees/FeeConfig.sol

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,28 @@ pragma solidity >=0.8.27;
55
import {Math} from "@oz/utils/math/Math.sol";
66
import {SafeCast} from "@oz/utils/math/SafeCast.sol";
77

8+
// Represents a value denominated in ETH (wei).
89
type EthValue is uint256;
910

11+
// Represents a value denominated in the fee asset (e.g., AZTEC token).
1012
type FeeAssetValue is uint256;
1113

12-
// Precision of 1e9
13-
type FeeAssetPerEthE9 is uint256;
14+
/*
15+
* ETH per fee asset price with 1e12 precision.
16+
* Higher stored value = more expensive fee asset (more ETH needed per 1 fee asset).
17+
* actual_eth_per_fee_asset = stored_value / ETH_PER_FEE_ASSET_PRECISION
18+
*
19+
* We use 1e12 precision because:
20+
* 1. The value must fit in 48 bits when compressed in FeeHeader (max ~2.8e14)
21+
* 2. Higher precision allows representing very low prices (down to 1e-10 ETH)
22+
* 3. Reduces rounding errors during ETH <-> FeeAsset conversions
23+
*
24+
* See FeeLib.sol for the MIN/MAX bounds and detailed documentation.
25+
*/
26+
type EthPerFeeAssetE12 is uint256;
27+
28+
// Precision multiplier for ETH per fee asset. Divide stored value by this to get actual ratio.
29+
uint256 constant ETH_PER_FEE_ASSET_PRECISION = 1e12;
1430

1531
function addEthValue(EthValue _a, EthValue _b) pure returns (EthValue) {
1632
return EthValue.wrap(EthValue.unwrap(_a) + EthValue.unwrap(_b));
@@ -31,18 +47,41 @@ struct FeeConfig {
3147
EthValue provingCostPerMana;
3248
}
3349

50+
/// @notice Library for converting between ETH and fee asset values using the price oracle.
3451
library PriceLib {
35-
function toEth(FeeAssetValue _feeAssetValue, FeeAssetPerEthE9 _feeAssetPerEth) internal pure returns (EthValue) {
52+
/**
53+
* @notice Converts a fee asset amount to its ETH equivalent.
54+
* @dev ethValue = feeAssetAmount * ethPerFeeAsset / precision
55+
* @param _feeAssetValue The amount in fee asset units
56+
* @param _ethPerFeeAsset The current price (ETH per fee asset with 1e12 precision)
57+
* @return The equivalent value in ETH (wei), rounded up
58+
*/
59+
function toEth(FeeAssetValue _feeAssetValue, EthPerFeeAssetE12 _ethPerFeeAsset) internal pure returns (EthValue) {
3660
return EthValue.wrap(
3761
Math.mulDiv(
38-
FeeAssetValue.unwrap(_feeAssetValue), 1e9, FeeAssetPerEthE9.unwrap(_feeAssetPerEth), Math.Rounding.Ceil
62+
FeeAssetValue.unwrap(_feeAssetValue),
63+
EthPerFeeAssetE12.unwrap(_ethPerFeeAsset),
64+
ETH_PER_FEE_ASSET_PRECISION,
65+
Math.Rounding.Ceil
3966
)
4067
);
4168
}
4269

43-
function toFeeAsset(EthValue _ethValue, FeeAssetPerEthE9 _feeAssetPerEth) internal pure returns (FeeAssetValue) {
70+
/**
71+
* @notice Converts an ETH amount to its fee asset equivalent.
72+
* @dev feeAssetAmount = ethValue * precision / ethPerFeeAsset
73+
* @param _ethValue The amount in ETH (wei)
74+
* @param _ethPerFeeAsset The current price (ETH per fee asset with 1e12 precision)
75+
* @return The equivalent value in fee asset units, rounded up
76+
*/
77+
function toFeeAsset(EthValue _ethValue, EthPerFeeAssetE12 _ethPerFeeAsset) internal pure returns (FeeAssetValue) {
4478
return FeeAssetValue.wrap(
45-
Math.mulDiv(EthValue.unwrap(_ethValue), FeeAssetPerEthE9.unwrap(_feeAssetPerEth), 1e9, Math.Rounding.Ceil)
79+
Math.mulDiv(
80+
EthValue.unwrap(_ethValue),
81+
ETH_PER_FEE_ASSET_PRECISION,
82+
EthPerFeeAssetE12.unwrap(_ethPerFeeAsset),
83+
Math.Rounding.Ceil
84+
)
4685
);
4786
}
4887
}

l1-contracts/src/core/libraries/compressed-data/fees/FeeStructs.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {SafeCast} from "@oz/utils/math/SafeCast.sol";
1111
uint1 preHeat;
1212
uint63 proverCost; Max value: 9.2233720369E18
1313
uint64 congestionCost;
14-
uint48 feeAssetPriceNumerator;
14+
uint48 ethPerFeeAsset;
1515
uint48 excessMana;
1616
uint32 manaUsed;
1717
}*/
@@ -20,7 +20,7 @@ type CompressedFeeHeader is uint256;
2020
struct FeeHeader {
2121
uint256 excessMana;
2222
uint256 manaUsed;
23-
uint256 feeAssetPriceNumerator;
23+
uint256 ethPerFeeAsset;
2424
uint256 congestionCost;
2525
uint256 proverCost;
2626
}
@@ -86,7 +86,7 @@ library FeeHeaderLib {
8686
return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 32) & MASK_48_BITS;
8787
}
8888

89-
function getFeeAssetPriceNumerator(CompressedFeeHeader _compressedFeeHeader) internal pure returns (uint256) {
89+
function getEthPerFeeAsset(CompressedFeeHeader _compressedFeeHeader) internal pure returns (uint256) {
9090
return (CompressedFeeHeader.unwrap(_compressedFeeHeader) >> 80) & MASK_48_BITS;
9191
}
9292

@@ -103,7 +103,7 @@ library FeeHeaderLib {
103103
uint256 value = 0;
104104
value |= uint256(_feeHeader.manaUsed.toUint32());
105105
value |= uint256(_feeHeader.excessMana.toUint48()) << 32;
106-
value |= uint256(_feeHeader.feeAssetPriceNumerator.toUint48()) << 80;
106+
value |= uint256(_feeHeader.ethPerFeeAsset.toUint48()) << 80;
107107
value |= uint256(_feeHeader.congestionCost.toUint64()) << 128;
108108

109109
uint256 proverCost = uint256(_feeHeader.proverCost.toUint64());
@@ -123,7 +123,7 @@ library FeeHeaderLib {
123123
value >>= 32;
124124
uint256 excessMana = value & MASK_48_BITS;
125125
value >>= 48;
126-
uint256 feeAssetPriceNumerator = value & MASK_48_BITS;
126+
uint256 ethPerFeeAsset = value & MASK_48_BITS;
127127
value >>= 48;
128128
uint256 congestionCost = value & MASK_64_BITS;
129129
value >>= 64;
@@ -132,7 +132,7 @@ library FeeHeaderLib {
132132
return FeeHeader({
133133
manaUsed: uint256(manaUsed),
134134
excessMana: uint256(excessMana),
135-
feeAssetPriceNumerator: uint256(feeAssetPriceNumerator),
135+
ethPerFeeAsset: uint256(ethPerFeeAsset),
136136
congestionCost: uint256(congestionCost),
137137
proverCost: uint256(proverCost)
138138
});

0 commit comments

Comments
 (0)