@aztec/ethereum 0.0.1-commit.c7c42ec → 0.0.1-commit.cd76b27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/config.d.ts +15 -28
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +50 -57
- package/dest/contracts/empire_base.d.ts +3 -1
- package/dest/contracts/empire_base.d.ts.map +1 -1
- package/dest/contracts/empire_slashing_proposer.d.ts +3 -1
- package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
- package/dest/contracts/empire_slashing_proposer.js +22 -15
- package/dest/contracts/fee_asset_handler.d.ts +6 -5
- package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
- package/dest/contracts/fee_asset_handler.js +11 -9
- package/dest/contracts/fee_asset_price_oracle.d.ts +101 -0
- package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
- package/dest/contracts/fee_asset_price_oracle.js +651 -0
- package/dest/contracts/governance.d.ts +3 -1
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +14 -4
- package/dest/contracts/governance_proposer.d.ts +3 -1
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +395 -9
- package/dest/contracts/inbox.d.ts +18 -1
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +32 -1
- package/dest/contracts/index.d.ts +4 -1
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +3 -0
- package/dest/contracts/log.d.ts +13 -0
- package/dest/contracts/log.d.ts.map +1 -0
- package/dest/contracts/log.js +1 -0
- package/dest/contracts/multicall.d.ts +1 -1
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +2 -1
- package/dest/contracts/outbox.d.ts +41 -0
- package/dest/contracts/outbox.d.ts.map +1 -0
- package/dest/contracts/outbox.js +86 -0
- package/dest/contracts/rollup.d.ts +161 -96
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +677 -132
- package/dest/contracts/tally_slashing_proposer.d.ts +1 -1
- package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
- package/dest/contracts/tally_slashing_proposer.js +8 -1
- package/dest/deploy_aztec_l1_contracts.d.ts +16 -4
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +110 -33
- package/dest/deploy_l1_contract.js +3 -3
- package/dest/generated/l1-contracts-defaults.d.ts +30 -0
- package/dest/generated/l1-contracts-defaults.d.ts.map +1 -0
- package/dest/generated/l1-contracts-defaults.js +30 -0
- package/dest/l1_artifacts.d.ts +5877 -1515
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_tx_utils/config.d.ts +7 -1
- package/dest/l1_tx_utils/config.d.ts.map +1 -1
- package/dest/l1_tx_utils/config.js +14 -1
- package/dest/l1_tx_utils/constants.d.ts +1 -1
- package/dest/l1_tx_utils/constants.js +2 -2
- package/dest/l1_tx_utils/factory.d.ts +18 -10
- package/dest/l1_tx_utils/factory.d.ts.map +1 -1
- package/dest/l1_tx_utils/factory.js +17 -7
- package/dest/l1_tx_utils/fee-strategies/index.d.ts +3 -2
- package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -1
- package/dest/l1_tx_utils/fee-strategies/index.js +2 -1
- package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +2 -12
- package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -1
- package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +36 -18
- package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +2 -11
- package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -1
- package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +37 -19
- package/dest/l1_tx_utils/fee-strategies/types.d.ts +14 -27
- package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -1
- package/dest/l1_tx_utils/fee-strategies/types.js +0 -21
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
- package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
- package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
- package/dest/l1_tx_utils/index-blobs.js +2 -2
- package/dest/l1_tx_utils/index.d.ts +2 -1
- package/dest/l1_tx_utils/index.d.ts.map +1 -1
- package/dest/l1_tx_utils/index.js +1 -0
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +2 -2
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_fee_analyzer.js +3 -3
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +15 -5
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +47 -13
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -5
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +17 -54
- package/dest/l1_tx_utils/tx_delayer.d.ts +56 -0
- package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
- package/dest/{test → l1_tx_utils}/tx_delayer.js +62 -34
- package/dest/publisher_manager.d.ts +3 -2
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +2 -2
- package/dest/queries.d.ts +2 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +6 -3
- package/dest/test/chain_monitor.js +1 -2
- package/dest/test/eth_cheat_codes.d.ts +13 -1
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/index.d.ts +1 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +0 -2
- package/dest/test/rollup_cheat_codes.d.ts +5 -2
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +19 -2
- package/dest/test/start_anvil.js +1 -1
- package/dest/test/upgrade_utils.js +2 -2
- package/dest/utils.d.ts +2 -1
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +46 -0
- package/package.json +8 -7
- package/src/config.ts +58 -56
- package/src/contracts/README.md +157 -0
- package/src/contracts/empire_base.ts +2 -0
- package/src/contracts/empire_slashing_proposer.ts +22 -27
- package/src/contracts/fee_asset_handler.ts +10 -7
- package/src/contracts/fee_asset_price_oracle.ts +280 -0
- package/src/contracts/governance.ts +13 -4
- package/src/contracts/governance_proposer.ts +10 -1
- package/src/contracts/inbox.ts +48 -1
- package/src/contracts/index.ts +3 -0
- package/src/contracts/log.ts +13 -0
- package/src/contracts/multicall.ts +5 -2
- package/src/contracts/outbox.ts +98 -0
- package/src/contracts/rollup.ts +348 -100
- package/src/contracts/tally_slashing_proposer.ts +5 -1
- package/src/deploy_aztec_l1_contracts.ts +135 -42
- package/src/deploy_l1_contract.ts +3 -3
- package/src/generated/l1-contracts-defaults.ts +32 -0
- package/src/l1_tx_utils/config.ts +20 -0
- package/src/l1_tx_utils/constants.ts +2 -2
- package/src/l1_tx_utils/factory.ts +31 -31
- package/src/l1_tx_utils/fee-strategies/index.ts +1 -1
- package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +46 -42
- package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +49 -45
- package/src/l1_tx_utils/fee-strategies/types.ts +14 -46
- package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
- package/src/l1_tx_utils/index-blobs.ts +2 -2
- package/src/l1_tx_utils/index.ts +1 -0
- package/src/l1_tx_utils/l1_fee_analyzer.ts +2 -3
- package/src/l1_tx_utils/l1_tx_utils.ts +52 -17
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +23 -62
- package/src/{test → l1_tx_utils}/tx_delayer.ts +78 -50
- package/src/publisher_manager.ts +4 -2
- package/src/queries.ts +5 -3
- package/src/test/chain_monitor.ts +1 -1
- package/src/test/index.ts +0 -2
- package/src/test/rollup_cheat_codes.ts +21 -3
- package/src/test/start_anvil.ts +1 -1
- package/src/test/upgrade_utils.ts +2 -2
- package/src/utils.ts +53 -0
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
- package/dest/test/delayed_tx_utils.d.ts +0 -13
- package/dest/test/delayed_tx_utils.d.ts.map +0 -1
- package/dest/test/delayed_tx_utils.js +0 -28
- package/dest/test/tx_delayer.d.ts +0 -36
- package/dest/test/tx_delayer.d.ts.map +0 -1
- package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
- package/src/test/delayed_tx_utils.ts +0 -52
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { memoize } from '@aztec/foundation/decorators';
|
|
2
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
|
+
|
|
5
|
+
import { type Hex, encodeAbiParameters, getContract, keccak256, parseAbiParameters } from 'viem';
|
|
6
|
+
|
|
7
|
+
import type { ViemClient } from '../types.js';
|
|
8
|
+
import { RollupContract } from './rollup.js';
|
|
9
|
+
|
|
10
|
+
/** Maximum price modifier per checkpoint in basis points. ±100 bps = ±1% */
|
|
11
|
+
export const MAX_FEE_ASSET_PRICE_MODIFIER_BPS = 100n;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validates that a fee asset price modifier is within the allowed range.
|
|
15
|
+
* Validators should call this before attesting to a checkpoint proposal.
|
|
16
|
+
*
|
|
17
|
+
* @param modifier - The fee asset price modifier in basis points
|
|
18
|
+
* @returns true if the modifier is valid (between -100 and +100 bps)
|
|
19
|
+
*/
|
|
20
|
+
export function validateFeeAssetPriceModifier(modifier: bigint): boolean {
|
|
21
|
+
return modifier >= -MAX_FEE_ASSET_PRICE_MODIFIER_BPS && modifier <= MAX_FEE_ASSET_PRICE_MODIFIER_BPS;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Oracle for computing fee asset price modifiers based on Uniswap V4 pool prices.
|
|
26
|
+
* Only active on Ethereum mainnet - returns 0 on other chains.
|
|
27
|
+
*/
|
|
28
|
+
export class FeeAssetPriceOracle {
|
|
29
|
+
constructor(
|
|
30
|
+
private client: ViemClient,
|
|
31
|
+
private readonly rollupContract: RollupContract,
|
|
32
|
+
private log: Logger = createLogger('fee-asset-price-oracle'),
|
|
33
|
+
) {}
|
|
34
|
+
|
|
35
|
+
@memoize
|
|
36
|
+
async getUniswapOracle(): Promise<UniswapPriceOracle | undefined> {
|
|
37
|
+
const code = await this.client.getCode({ address: STATE_VIEW_ADDRESS.toString() });
|
|
38
|
+
if (code === undefined || code === '0x') {
|
|
39
|
+
this.log.warn('Uniswap V4 StateView contract not found, skipping fee asset price oracle');
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
this.log.info('Uniswap V4 StateView contract found, initializing fee asset price oracle');
|
|
43
|
+
const oracle = new UniswapPriceOracle(this.client, this.log);
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
if (!(await oracle.isPoolInitialized())) {
|
|
47
|
+
this.log.warn('Uniswap V4 pool not initialized, skipping fee asset price oracle');
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
this.log.warn(`Failed to check if Uniswap V4 pool is initialized: ${err}`);
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return oracle;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Computes the fee asset price modifier to be used in the next checkpoint proposal.
|
|
60
|
+
*
|
|
61
|
+
* The modifier adjusts the on-chain fee asset price toward the oracle price,
|
|
62
|
+
* clamped to ±1% (±100 basis points) per checkpoint.
|
|
63
|
+
*
|
|
64
|
+
* Returns 0 if not on mainnet or if the oracle query fails.
|
|
65
|
+
*
|
|
66
|
+
* @returns The price modifier in basis points (positive to increase price, negative to decrease)
|
|
67
|
+
*/
|
|
68
|
+
async computePriceModifier(): Promise<bigint> {
|
|
69
|
+
const uniswapOracle = await this.getUniswapOracle();
|
|
70
|
+
if (!uniswapOracle) {
|
|
71
|
+
return 0n;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// Get current on-chain price (ETH per fee asset, E12)
|
|
76
|
+
const currentPriceE12 = await this.rollupContract.getEthPerFeeAsset();
|
|
77
|
+
|
|
78
|
+
// Get oracle price (median of last N blocks, ETH per fee asset, E12)
|
|
79
|
+
const oraclePriceE12 = await uniswapOracle.getMeanEthPerFeeAssetE12();
|
|
80
|
+
|
|
81
|
+
// Compute modifier in basis points
|
|
82
|
+
const modifier = this.computePriceModifierBps(currentPriceE12, oraclePriceE12);
|
|
83
|
+
|
|
84
|
+
this.log.debug('Computed price modifier', {
|
|
85
|
+
currentPriceE12: currentPriceE12.toString(),
|
|
86
|
+
oraclePriceE12: oraclePriceE12.toString(),
|
|
87
|
+
modifierBps: modifier.toString(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return modifier;
|
|
91
|
+
} catch (err) {
|
|
92
|
+
this.log.warn(`Failed to compute price modifier, using 0: ${err}`);
|
|
93
|
+
return 0n;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Gets the current oracle price (ETH per fee asset, scaled by 1e12).
|
|
99
|
+
* Returns undefined if not on mainnet or if the oracle query fails.
|
|
100
|
+
*/
|
|
101
|
+
async getOraclePrice(): Promise<bigint | undefined> {
|
|
102
|
+
const uniswapOracle = await this.getUniswapOracle();
|
|
103
|
+
if (!uniswapOracle) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
return await uniswapOracle.getMeanEthPerFeeAssetE12();
|
|
109
|
+
} catch (err) {
|
|
110
|
+
this.log.warn(`Failed to get oracle price: ${err}`);
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Computes the basis points modifier needed to move from current price toward target price.
|
|
117
|
+
*
|
|
118
|
+
* @param currentPrice - Current ETH per fee asset (E12 scale)
|
|
119
|
+
* @param targetPrice - Target ETH per fee asset (E12 scale)
|
|
120
|
+
* @returns Basis points modifier clamped to ±100 (±1%)
|
|
121
|
+
*/
|
|
122
|
+
computePriceModifierBps(currentPrice: bigint, targetPrice: bigint): bigint {
|
|
123
|
+
if (currentPrice === 0n) {
|
|
124
|
+
return MAX_FEE_ASSET_PRICE_MODIFIER_BPS;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Calculate percentage difference in basis points
|
|
128
|
+
// modifierBps = ((targetPrice - currentPrice) / currentPrice) * 10000
|
|
129
|
+
const diff = targetPrice - currentPrice;
|
|
130
|
+
const rawModifierBps = (diff * 10_000n) / currentPrice;
|
|
131
|
+
|
|
132
|
+
// Clamp to ±MAX_FEE_ASSET_PRICE_MODIFIER_BPS
|
|
133
|
+
if (rawModifierBps > MAX_FEE_ASSET_PRICE_MODIFIER_BPS) {
|
|
134
|
+
return MAX_FEE_ASSET_PRICE_MODIFIER_BPS;
|
|
135
|
+
}
|
|
136
|
+
if (rawModifierBps < -MAX_FEE_ASSET_PRICE_MODIFIER_BPS) {
|
|
137
|
+
return -MAX_FEE_ASSET_PRICE_MODIFIER_BPS;
|
|
138
|
+
}
|
|
139
|
+
return rawModifierBps;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Mainnet Uniswap V4 StateView contract address */
|
|
144
|
+
export const STATE_VIEW_ADDRESS = EthAddress.fromString('0x7ffe42c4a5deea5b0fec41c94c136cf115597227');
|
|
145
|
+
|
|
146
|
+
const PRECISION_Q192 = 10n ** 12n * 2n ** 192n;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Converts Uniswap's sqrtPriceX96 directly to ETH per FeeAsset (E12).
|
|
150
|
+
*
|
|
151
|
+
* For an ETH/FeeAsset pool where ETH is currency0 and FeeAsset is currency1:
|
|
152
|
+
* - Uniswap's sqrtPriceX96 = sqrt(FeeAsset/ETH) * 2^96
|
|
153
|
+
* - We need: ETH/FeeAsset with 1e12 precision
|
|
154
|
+
*
|
|
155
|
+
* Math:
|
|
156
|
+
* price = (sqrtPriceX96 / 2^96)^2 = sqrtPriceX96^2 / 2^192 (FeeAsset per ETH)
|
|
157
|
+
* ethPerFeeAsset = 1 / price = 2^192 / sqrtPriceX96^2
|
|
158
|
+
* ethPerFeeAssetE12 = ethPerFeeAsset * 1e12 = 1e12 * 2^192 / sqrtPriceX96^2
|
|
159
|
+
*/
|
|
160
|
+
export function sqrtPriceX96ToEthPerFeeAssetE12(sqrtPriceX96: bigint): bigint {
|
|
161
|
+
if (sqrtPriceX96 === 0n) {
|
|
162
|
+
throw new Error('Cannot convert zero sqrtPriceX96');
|
|
163
|
+
}
|
|
164
|
+
return PRECISION_Q192 / (sqrtPriceX96 * sqrtPriceX96);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Uniswap V4 StateView ABI - only the functions we need
|
|
168
|
+
*/
|
|
169
|
+
const StateViewAbi = [
|
|
170
|
+
{
|
|
171
|
+
type: 'function',
|
|
172
|
+
name: 'getSlot0',
|
|
173
|
+
inputs: [{ name: 'poolId', type: 'bytes32', internalType: 'PoolId' }],
|
|
174
|
+
outputs: [
|
|
175
|
+
{ name: 'sqrtPriceX96', type: 'uint160', internalType: 'uint160' },
|
|
176
|
+
{ name: 'tick', type: 'int24', internalType: 'int24' },
|
|
177
|
+
{ name: 'protocolFee', type: 'uint24', internalType: 'uint24' },
|
|
178
|
+
{ name: 'lpFee', type: 'uint24', internalType: 'uint24' },
|
|
179
|
+
],
|
|
180
|
+
stateMutability: 'view',
|
|
181
|
+
},
|
|
182
|
+
] as const;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Client for querying the ETH/FeeAsset price from Uniswap V4.
|
|
186
|
+
* Returns prices in ETH per FeeAsset format (E12) to match the rollup contract.
|
|
187
|
+
*/
|
|
188
|
+
class UniswapPriceOracle {
|
|
189
|
+
private readonly stateView;
|
|
190
|
+
private readonly poolId: Hex;
|
|
191
|
+
private readonly log: Logger;
|
|
192
|
+
|
|
193
|
+
constructor(
|
|
194
|
+
private readonly client: ViemClient,
|
|
195
|
+
log?: Logger,
|
|
196
|
+
) {
|
|
197
|
+
this.log = log ?? createLogger('uniswap-price-oracle');
|
|
198
|
+
this.stateView = getContract({
|
|
199
|
+
address: STATE_VIEW_ADDRESS.toString(),
|
|
200
|
+
abi: StateViewAbi,
|
|
201
|
+
client,
|
|
202
|
+
});
|
|
203
|
+
this.poolId = this.computePoolId();
|
|
204
|
+
this.log.debug(`Initialized UniswapPriceOracle with poolId: ${this.poolId}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Computes the PoolId from the pool configuration by hashing its components.
|
|
209
|
+
* PoolId = keccak256(abi.encode(currency0, currency1, fee, tickSpacing, hooks))
|
|
210
|
+
* For mainnet, the value is expected to be: 0xce2899b16743cfd5a954d8122d5e07f410305b1aebee39fd73d9f3b9ebf10c2f
|
|
211
|
+
* Derived anyway to make it simpler to change if needed.
|
|
212
|
+
*/
|
|
213
|
+
@memoize
|
|
214
|
+
computePoolId(): Hex {
|
|
215
|
+
/** ETH/FeeAsset pool configuration (hardcoded for mainnet) */
|
|
216
|
+
const encoded = encodeAbiParameters(parseAbiParameters('address, address, uint24, int24, address'), [
|
|
217
|
+
EthAddress.ZERO.toString(),
|
|
218
|
+
EthAddress.fromString('0xA27EC0006e59f245217Ff08CD52A7E8b169E62D2').toString(),
|
|
219
|
+
500, // 0.05%
|
|
220
|
+
10,
|
|
221
|
+
EthAddress.fromString('0xd53006d1e3110fD319a79AEEc4c527a0d265E080').toString(),
|
|
222
|
+
]);
|
|
223
|
+
return keccak256(encoded);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async isPoolInitialized(): Promise<boolean> {
|
|
227
|
+
const [sqrtPriceX96] = await this.stateView.read.getSlot0([this.poolId], undefined);
|
|
228
|
+
return sqrtPriceX96 !== 0n;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Gets the price as ETH per FeeAsset, scaled by 1e12.
|
|
233
|
+
* This is the format expected by the rollup contract.
|
|
234
|
+
*
|
|
235
|
+
* @param blockNumber - Optional block number to query at (defaults to latest)
|
|
236
|
+
*/
|
|
237
|
+
async getEthPerFeeAssetE12(blockNumber?: bigint): Promise<bigint> {
|
|
238
|
+
const [sqrtPriceX96] = await this.stateView.read.getSlot0(
|
|
239
|
+
[this.poolId],
|
|
240
|
+
blockNumber !== undefined ? { blockNumber } : undefined,
|
|
241
|
+
);
|
|
242
|
+
return sqrtPriceX96ToEthPerFeeAssetE12(sqrtPriceX96);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Gets the median price over the last N blocks as ETH per FeeAsset (E12).
|
|
247
|
+
* Using median helps protect against single-block manipulation.
|
|
248
|
+
*
|
|
249
|
+
* @param numBlocks - Number of recent blocks to sample (default: 5)
|
|
250
|
+
* @returns Median price as ETH per FeeAsset, scaled by 1e12
|
|
251
|
+
*/
|
|
252
|
+
async getMeanEthPerFeeAssetE12(numBlocks: number = 5): Promise<bigint> {
|
|
253
|
+
const currentBlock = await this.client.getBlockNumber();
|
|
254
|
+
const prices: bigint[] = [];
|
|
255
|
+
|
|
256
|
+
for (let i = 0; i < numBlocks; i++) {
|
|
257
|
+
const blockNumber = currentBlock - BigInt(i);
|
|
258
|
+
if (blockNumber < 0n) {
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const price = await this.getEthPerFeeAssetE12(blockNumber);
|
|
264
|
+
prices.push(price);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
this.log.warn(`Failed to get price at block ${blockNumber}: ${err}`);
|
|
267
|
+
// Continue with fewer samples
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const filteredPrices = prices.filter(price => price !== 0n);
|
|
272
|
+
|
|
273
|
+
if (filteredPrices.length === 0) {
|
|
274
|
+
throw new Error('Failed to get any price samples from Uniswap oracle');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const mean = filteredPrices.reduce((a, b) => a + b, 0n) / BigInt(filteredPrices.length);
|
|
278
|
+
return mean;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from 'viem';
|
|
15
15
|
|
|
16
16
|
import type { L1ContractAddresses } from '../l1_contract_addresses.js';
|
|
17
|
-
import {
|
|
17
|
+
import { createL1TxUtils } from '../l1_tx_utils/index.js';
|
|
18
18
|
import { type ExtendedViemWalletClient, type ViemClient, isExtendedClient } from '../types.js';
|
|
19
19
|
|
|
20
20
|
export type L1GovernanceContractAddresses = Pick<
|
|
@@ -174,6 +174,13 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
174
174
|
return this.governanceContract.read.powerAt([this.client.account.address, now.timestamp]);
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/** Returns the user's voting power for a specific proposal, checked at pendingThrough timestamp. */
|
|
178
|
+
public async getPowerForProposal(proposalId: bigint): Promise<bigint> {
|
|
179
|
+
const proposal = await this.getProposal(proposalId);
|
|
180
|
+
const pendingThrough = proposal.creation + proposal.config.votingDelay;
|
|
181
|
+
return this.governanceContract.read.powerAt([this.client.account.address, pendingThrough]);
|
|
182
|
+
}
|
|
183
|
+
|
|
177
184
|
public async vote({
|
|
178
185
|
proposalId,
|
|
179
186
|
voteAmount,
|
|
@@ -187,10 +194,10 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
187
194
|
retries: number;
|
|
188
195
|
logger: Logger;
|
|
189
196
|
}) {
|
|
190
|
-
const l1TxUtils =
|
|
197
|
+
const l1TxUtils = createL1TxUtils(this.client, { logger });
|
|
191
198
|
const retryDelaySeconds = 12;
|
|
192
199
|
|
|
193
|
-
voteAmount = voteAmount ?? (await this.
|
|
200
|
+
voteAmount = voteAmount ?? (await this.getPowerForProposal(proposalId));
|
|
194
201
|
|
|
195
202
|
let success = false;
|
|
196
203
|
for (let i = 0; i < retries; i++) {
|
|
@@ -204,6 +211,7 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
204
211
|
|
|
205
212
|
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
206
213
|
to: this.governanceContract.address,
|
|
214
|
+
abi: GovernanceAbi,
|
|
207
215
|
data: encodedVoteData,
|
|
208
216
|
});
|
|
209
217
|
|
|
@@ -244,7 +252,7 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
244
252
|
retries: number;
|
|
245
253
|
logger: Logger;
|
|
246
254
|
}) {
|
|
247
|
-
const l1TxUtils =
|
|
255
|
+
const l1TxUtils = createL1TxUtils(this.client, { logger });
|
|
248
256
|
const retryDelaySeconds = 12;
|
|
249
257
|
let success = false;
|
|
250
258
|
for (let i = 0; i < retries; i++) {
|
|
@@ -258,6 +266,7 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
258
266
|
|
|
259
267
|
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
260
268
|
to: this.governanceContract.address,
|
|
269
|
+
abi: GovernanceAbi,
|
|
261
270
|
data: encodedExecuteData,
|
|
262
271
|
});
|
|
263
272
|
|
|
@@ -83,6 +83,7 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
83
83
|
public createSignalRequest(payload: Hex): L1TxRequest {
|
|
84
84
|
return {
|
|
85
85
|
to: this.address.toString(),
|
|
86
|
+
abi: GovernanceProposerAbi,
|
|
86
87
|
data: encodeSignal(payload),
|
|
87
88
|
};
|
|
88
89
|
}
|
|
@@ -104,10 +105,17 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
104
105
|
);
|
|
105
106
|
return {
|
|
106
107
|
to: this.address.toString(),
|
|
108
|
+
abi: GovernanceProposerAbi,
|
|
107
109
|
data: encodeSignalWithSignature(payload, signature),
|
|
108
110
|
};
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
/** Checks if a payload was ever submitted to governance via submitRoundWinner. */
|
|
114
|
+
public async hasPayloadBeenProposed(payload: Hex, fromBlock: bigint): Promise<boolean> {
|
|
115
|
+
const events = await this.proposer.getEvents.PayloadSubmitted({ payload }, { fromBlock, strict: true });
|
|
116
|
+
return events.length > 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
111
119
|
public async submitRoundWinner(
|
|
112
120
|
round: bigint,
|
|
113
121
|
l1TxUtils: L1TxUtils,
|
|
@@ -117,8 +125,9 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
117
125
|
}> {
|
|
118
126
|
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
119
127
|
to: this.address.toString(),
|
|
128
|
+
abi: GovernanceProposerAbi,
|
|
120
129
|
data: encodeFunctionData({
|
|
121
|
-
abi:
|
|
130
|
+
abi: GovernanceProposerAbi,
|
|
122
131
|
functionName: 'submitRoundWinner',
|
|
123
132
|
args: [round],
|
|
124
133
|
}),
|
package/src/contracts/inbox.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { Buffer16, Buffer32 } from '@aztec/foundation/buffer';
|
|
3
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
4
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
5
|
import { InboxAbi } from '@aztec/l1-artifacts/InboxAbi';
|
|
4
6
|
|
|
@@ -8,8 +10,20 @@ import { getPublicClient } from '../client.js';
|
|
|
8
10
|
import type { DeployAztecL1ContractsReturnType } from '../deploy_aztec_l1_contracts.js';
|
|
9
11
|
import type { L1ReaderConfig } from '../l1_reader.js';
|
|
10
12
|
import type { ViemClient } from '../types.js';
|
|
13
|
+
import type { L1EventLog } from './log.js';
|
|
11
14
|
import { checkBlockTag } from './utils.js';
|
|
12
15
|
|
|
16
|
+
/** Arguments for the MessageSent event. */
|
|
17
|
+
export type MessageSentArgs = {
|
|
18
|
+
index: bigint;
|
|
19
|
+
leaf: Fr;
|
|
20
|
+
checkpointNumber: CheckpointNumber;
|
|
21
|
+
rollingHash: Buffer16;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Log type for MessageSent events. */
|
|
25
|
+
export type MessageSentLog = L1EventLog<MessageSentArgs>;
|
|
26
|
+
|
|
13
27
|
export class InboxContract {
|
|
14
28
|
private readonly inbox: GetContractReturnType<typeof InboxAbi, ViemClient>;
|
|
15
29
|
|
|
@@ -59,6 +73,39 @@ export class InboxContract {
|
|
|
59
73
|
treeInProgress: state.inProgress,
|
|
60
74
|
};
|
|
61
75
|
}
|
|
76
|
+
|
|
77
|
+
/** Fetches MessageSent events within the given block range. */
|
|
78
|
+
async getMessageSentEvents(fromBlock: bigint, toBlock: bigint): Promise<MessageSentLog[]> {
|
|
79
|
+
const logs = await this.inbox.getEvents.MessageSent({}, { fromBlock, toBlock });
|
|
80
|
+
return logs
|
|
81
|
+
.filter(log => log.blockNumber! >= fromBlock && log.blockNumber! <= toBlock)
|
|
82
|
+
.map(log => this.mapMessageSentLog(log));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Fetches MessageSent events for a specific message hash within the given block range. */
|
|
86
|
+
async getMessageSentEventByHash(hash: Hex, fromBlock: bigint, toBlock: bigint): Promise<MessageSentLog[]> {
|
|
87
|
+
const logs = await this.inbox.getEvents.MessageSent({ hash }, { fromBlock, toBlock });
|
|
88
|
+
return logs.map(log => this.mapMessageSentLog(log));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private mapMessageSentLog(log: {
|
|
92
|
+
blockNumber: bigint | null;
|
|
93
|
+
blockHash: `0x${string}` | null;
|
|
94
|
+
transactionHash: `0x${string}` | null;
|
|
95
|
+
args: { index?: bigint; hash?: `0x${string}`; checkpointNumber?: bigint; rollingHash?: `0x${string}` };
|
|
96
|
+
}): MessageSentLog {
|
|
97
|
+
return {
|
|
98
|
+
l1BlockNumber: log.blockNumber!,
|
|
99
|
+
l1BlockHash: Buffer32.fromString(log.blockHash!),
|
|
100
|
+
l1TransactionHash: log.transactionHash!,
|
|
101
|
+
args: {
|
|
102
|
+
index: log.args.index!,
|
|
103
|
+
leaf: Fr.fromString(log.args.hash!),
|
|
104
|
+
checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
|
|
105
|
+
rollingHash: Buffer16.fromString(log.args.rollingHash!),
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
62
109
|
}
|
|
63
110
|
|
|
64
111
|
export type InboxContractState = {
|
package/src/contracts/index.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export * from './empire_base.js';
|
|
2
2
|
export * from './errors.js';
|
|
3
3
|
export * from './fee_asset_handler.js';
|
|
4
|
+
export * from './fee_asset_price_oracle.js';
|
|
4
5
|
export * from './fee_juice.js';
|
|
5
6
|
export * from './governance.js';
|
|
6
7
|
export * from './governance_proposer.js';
|
|
7
8
|
export * from './gse.js';
|
|
8
9
|
export * from './inbox.js';
|
|
10
|
+
export * from './log.js';
|
|
9
11
|
export * from './multicall.js';
|
|
12
|
+
export * from './outbox.js';
|
|
10
13
|
export * from './registry.js';
|
|
11
14
|
export * from './rollup.js';
|
|
12
15
|
export * from './empire_slashing_proposer.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Buffer32 } from '@aztec/foundation/buffer';
|
|
2
|
+
|
|
3
|
+
/** Base L1 event log with common fields. */
|
|
4
|
+
export type L1EventLog<T> = {
|
|
5
|
+
/** L1 block number where the event was emitted. */
|
|
6
|
+
l1BlockNumber: bigint;
|
|
7
|
+
/** L1 block hash. */
|
|
8
|
+
l1BlockHash: Buffer32;
|
|
9
|
+
/** L1 transaction hash that emitted the event. */
|
|
10
|
+
l1TransactionHash: `0x${string}`;
|
|
11
|
+
/** Event-specific arguments. */
|
|
12
|
+
args: T;
|
|
13
|
+
};
|
|
@@ -34,10 +34,13 @@ export class Multicall3 {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
const encodedForwarderData = encodeFunctionData(forwarderFunctionData);
|
|
37
|
-
|
|
38
37
|
try {
|
|
39
38
|
const { receipt, state } = await l1TxUtils.sendAndMonitorTransaction(
|
|
40
|
-
{
|
|
39
|
+
{
|
|
40
|
+
to: MULTI_CALL_3_ADDRESS,
|
|
41
|
+
data: encodedForwarderData,
|
|
42
|
+
abi: multicall3Abi,
|
|
43
|
+
},
|
|
41
44
|
gasConfig,
|
|
42
45
|
blobConfig,
|
|
43
46
|
);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { EpochNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
|
+
import { OutboxAbi } from '@aztec/l1-artifacts/OutboxAbi';
|
|
4
|
+
|
|
5
|
+
import { type GetContractReturnType, type Hex, encodeAbiParameters, getContract, hexToBigInt, keccak256 } from 'viem';
|
|
6
|
+
|
|
7
|
+
import { getPublicClient } from '../client.js';
|
|
8
|
+
import type { DeployAztecL1ContractsReturnType } from '../deploy_aztec_l1_contracts.js';
|
|
9
|
+
import type { L1ReaderConfig } from '../l1_reader.js';
|
|
10
|
+
import { type ExtendedViemWalletClient, type ViemClient, isExtendedClient } from '../types.js';
|
|
11
|
+
|
|
12
|
+
export type ViemL1Actor = {
|
|
13
|
+
actor: Hex;
|
|
14
|
+
chainId: bigint;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ViemL2Actor = {
|
|
18
|
+
actor: Hex;
|
|
19
|
+
version: bigint;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ViemL2ToL1Msg = {
|
|
23
|
+
sender: ViemL2Actor;
|
|
24
|
+
recipient: ViemL1Actor;
|
|
25
|
+
content: Hex;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export class OutboxContract {
|
|
29
|
+
private readonly outbox: GetContractReturnType<typeof OutboxAbi, ViemClient>;
|
|
30
|
+
|
|
31
|
+
static getFromL1ContractsValues(deployL1ContractsValues: DeployAztecL1ContractsReturnType) {
|
|
32
|
+
const {
|
|
33
|
+
l1Client,
|
|
34
|
+
l1ContractAddresses: { outboxAddress },
|
|
35
|
+
} = deployL1ContractsValues;
|
|
36
|
+
return new OutboxContract(l1Client, outboxAddress.toString());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static getFromConfig(config: L1ReaderConfig) {
|
|
40
|
+
const client = getPublicClient(config);
|
|
41
|
+
const address = config.l1Contracts.outboxAddress.toString();
|
|
42
|
+
return new OutboxContract(client, address);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static getEpochRootStorageSlot(epoch: EpochNumber) {
|
|
46
|
+
return hexToBigInt(keccak256(encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [BigInt(epoch), 0n])));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
public readonly client: ViemClient,
|
|
51
|
+
address: Hex | EthAddress,
|
|
52
|
+
) {
|
|
53
|
+
if (address instanceof EthAddress) {
|
|
54
|
+
address = address.toString();
|
|
55
|
+
}
|
|
56
|
+
this.outbox = getContract({ address, abi: OutboxAbi, client });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public get address() {
|
|
60
|
+
return this.outbox.address;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public getContract(): GetContractReturnType<typeof OutboxAbi, ViemClient> {
|
|
64
|
+
return this.outbox;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public hasMessageBeenConsumedAtEpoch(epoch: EpochNumber, leafId: bigint) {
|
|
68
|
+
return this.outbox.read.hasMessageBeenConsumedAtEpoch([BigInt(epoch), leafId]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public getRootData(epoch: EpochNumber) {
|
|
72
|
+
return this.outbox.read.getRootData([BigInt(epoch)]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public consume(message: ViemL2ToL1Msg, epoch: EpochNumber, leafIndex: bigint, path: Hex[]) {
|
|
76
|
+
const wallet = this.assertWallet();
|
|
77
|
+
return wallet.write.consume([message, BigInt(epoch), leafIndex, path]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public async getMessageConsumedEvents(
|
|
81
|
+
l1BlockHash: Hex,
|
|
82
|
+
): Promise<{ epoch: bigint; root: Hex; messageHash: Hex; leafId: bigint }[]> {
|
|
83
|
+
const events = await this.outbox.getEvents.MessageConsumed({}, { blockHash: l1BlockHash, strict: true });
|
|
84
|
+
return events.map(event => ({
|
|
85
|
+
epoch: event.args.epoch!,
|
|
86
|
+
root: event.args.root!,
|
|
87
|
+
messageHash: event.args.messageHash!,
|
|
88
|
+
leafId: event.args.leafId!,
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private assertWallet(): GetContractReturnType<typeof OutboxAbi, ExtendedViemWalletClient> {
|
|
93
|
+
if (!isExtendedClient(this.client)) {
|
|
94
|
+
throw new Error('Wallet client is required for this operation');
|
|
95
|
+
}
|
|
96
|
+
return this.outbox as GetContractReturnType<typeof OutboxAbi, ExtendedViemWalletClient>;
|
|
97
|
+
}
|
|
98
|
+
}
|