@aztec/ethereum 0.0.1-commit.c80b6263 → 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 +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +5 -3
- 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 +1 -1
- package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
- package/dest/contracts/fee_asset_handler.js +2 -0
- 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 +1 -1
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +5 -3
- 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 +13 -1
- package/dest/contracts/index.d.ts +2 -1
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.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/rollup.d.ts +2 -1
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +9 -3
- 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 +2 -3
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +37 -22
- package/dest/deploy_l1_contract.js +3 -3
- package/dest/l1_artifacts.d.ts +1051 -42
- 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/p75_competitive.js +1 -1
- package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
- 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_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.js +3 -3
- 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/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/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 +5 -5
- package/src/config.ts +3 -2
- package/src/contracts/empire_base.ts +2 -0
- package/src/contracts/empire_slashing_proposer.ts +22 -27
- package/src/contracts/fee_asset_handler.ts +2 -0
- package/src/contracts/fee_asset_price_oracle.ts +280 -0
- package/src/contracts/governance.ts +5 -3
- package/src/contracts/governance_proposer.ts +10 -1
- package/src/contracts/index.ts +1 -0
- package/src/contracts/multicall.ts +5 -2
- package/src/contracts/rollup.ts +15 -7
- package/src/contracts/tally_slashing_proposer.ts +5 -1
- package/src/deploy_aztec_l1_contracts.ts +60 -29
- package/src/deploy_l1_contract.ts +3 -3
- 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/p75_competitive.ts +1 -1
- package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
- 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_tx_utils.ts +52 -17
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +3 -3
- package/src/{test → l1_tx_utils}/tx_delayer.ts +78 -50
- package/src/test/index.ts +0 -2
- 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<
|
|
@@ -194,7 +194,7 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
194
194
|
retries: number;
|
|
195
195
|
logger: Logger;
|
|
196
196
|
}) {
|
|
197
|
-
const l1TxUtils =
|
|
197
|
+
const l1TxUtils = createL1TxUtils(this.client, { logger });
|
|
198
198
|
const retryDelaySeconds = 12;
|
|
199
199
|
|
|
200
200
|
voteAmount = voteAmount ?? (await this.getPowerForProposal(proposalId));
|
|
@@ -211,6 +211,7 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
211
211
|
|
|
212
212
|
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
213
213
|
to: this.governanceContract.address,
|
|
214
|
+
abi: GovernanceAbi,
|
|
214
215
|
data: encodedVoteData,
|
|
215
216
|
});
|
|
216
217
|
|
|
@@ -251,7 +252,7 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
251
252
|
retries: number;
|
|
252
253
|
logger: Logger;
|
|
253
254
|
}) {
|
|
254
|
-
const l1TxUtils =
|
|
255
|
+
const l1TxUtils = createL1TxUtils(this.client, { logger });
|
|
255
256
|
const retryDelaySeconds = 12;
|
|
256
257
|
let success = false;
|
|
257
258
|
for (let i = 0; i < retries; i++) {
|
|
@@ -265,6 +266,7 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
|
|
|
265
266
|
|
|
266
267
|
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
267
268
|
to: this.governanceContract.address,
|
|
269
|
+
abi: GovernanceAbi,
|
|
268
270
|
data: encodedExecuteData,
|
|
269
271
|
});
|
|
270
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/index.ts
CHANGED
|
@@ -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
|
);
|
package/src/contracts/rollup.ts
CHANGED
|
@@ -391,20 +391,24 @@ export class RollupContract {
|
|
|
391
391
|
slotDuration: number;
|
|
392
392
|
epochDuration: number;
|
|
393
393
|
proofSubmissionEpochs: number;
|
|
394
|
+
targetCommitteeSize: number;
|
|
394
395
|
}> {
|
|
395
|
-
const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs] =
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
396
|
+
const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs, targetCommitteeSize] =
|
|
397
|
+
await Promise.all([
|
|
398
|
+
this.getL1StartBlock(),
|
|
399
|
+
this.getL1GenesisTime(),
|
|
400
|
+
this.getSlotDuration(),
|
|
401
|
+
this.getEpochDuration(),
|
|
402
|
+
this.getProofSubmissionEpochs(),
|
|
403
|
+
this.getTargetCommitteeSize(),
|
|
404
|
+
]);
|
|
402
405
|
return {
|
|
403
406
|
l1StartBlock,
|
|
404
407
|
l1GenesisTime,
|
|
405
408
|
slotDuration,
|
|
406
409
|
epochDuration: Number(epochDuration),
|
|
407
410
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
411
|
+
targetCommitteeSize,
|
|
408
412
|
};
|
|
409
413
|
}
|
|
410
414
|
|
|
@@ -805,6 +809,7 @@ export class RollupContract {
|
|
|
805
809
|
): L1TxRequest {
|
|
806
810
|
return {
|
|
807
811
|
to: this.address,
|
|
812
|
+
abi: RollupAbi,
|
|
808
813
|
data: encodeFunctionData({
|
|
809
814
|
abi: RollupAbi,
|
|
810
815
|
functionName: 'invalidateBadAttestation',
|
|
@@ -826,6 +831,7 @@ export class RollupContract {
|
|
|
826
831
|
): L1TxRequest {
|
|
827
832
|
return {
|
|
828
833
|
to: this.address,
|
|
834
|
+
abi: RollupAbi,
|
|
829
835
|
data: encodeFunctionData({
|
|
830
836
|
abi: RollupAbi,
|
|
831
837
|
functionName: 'invalidateInsufficientAttestations',
|
|
@@ -959,6 +965,7 @@ export class RollupContract {
|
|
|
959
965
|
setupEpoch(l1TxUtils: L1TxUtils) {
|
|
960
966
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
961
967
|
to: this.address,
|
|
968
|
+
abi: RollupAbi,
|
|
962
969
|
data: encodeFunctionData({
|
|
963
970
|
abi: RollupAbi,
|
|
964
971
|
functionName: 'setupEpoch',
|
|
@@ -970,6 +977,7 @@ export class RollupContract {
|
|
|
970
977
|
vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
|
|
971
978
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
972
979
|
to: this.address,
|
|
980
|
+
abi: RollupAbi,
|
|
973
981
|
data: encodeFunctionData({
|
|
974
982
|
abi: RollupAbi,
|
|
975
983
|
functionName: 'vote',
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { L1TxRequest } from '@aztec/ethereum/l1-tx-utils';
|
|
2
2
|
import type { ViemClient } from '@aztec/ethereum/types';
|
|
3
|
-
import { tryExtractEvent } from '@aztec/ethereum/utils';
|
|
3
|
+
import { mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
4
4
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
5
5
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
6
6
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
7
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
8
8
|
import { hexToBuffer } from '@aztec/foundation/string';
|
|
9
|
+
import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
|
|
9
10
|
import { TallySlashingProposerAbi } from '@aztec/l1-artifacts/TallySlashingProposerAbi';
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -160,6 +161,7 @@ export class TallySlashingProposerContract {
|
|
|
160
161
|
|
|
161
162
|
return {
|
|
162
163
|
to: this.contract.address,
|
|
164
|
+
abi: TallySlashingProposerAbi,
|
|
163
165
|
data: encodeFunctionData({
|
|
164
166
|
abi: TallySlashingProposerAbi,
|
|
165
167
|
functionName: 'vote',
|
|
@@ -207,6 +209,7 @@ export class TallySlashingProposerContract {
|
|
|
207
209
|
public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
|
|
208
210
|
return {
|
|
209
211
|
to: this.contract.address,
|
|
212
|
+
abi: TallySlashingProposerAbi,
|
|
210
213
|
data: encodeFunctionData({
|
|
211
214
|
abi: TallySlashingProposerAbi,
|
|
212
215
|
functionName: 'vote',
|
|
@@ -224,6 +227,7 @@ export class TallySlashingProposerContract {
|
|
|
224
227
|
public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
|
|
225
228
|
return {
|
|
226
229
|
to: this.contract.address,
|
|
230
|
+
abi: mergeAbis([TallySlashingProposerAbi, SlasherAbi]),
|
|
227
231
|
data: encodeFunctionData({
|
|
228
232
|
abi: TallySlashingProposerAbi,
|
|
229
233
|
functionName: 'executeRound',
|
|
@@ -15,7 +15,7 @@ import { tmpdir } from 'os';
|
|
|
15
15
|
import { dirname, join, resolve } from 'path';
|
|
16
16
|
import readline from 'readline';
|
|
17
17
|
import type { Hex } from 'viem';
|
|
18
|
-
import {
|
|
18
|
+
import { mainnet, sepolia } from 'viem/chains';
|
|
19
19
|
|
|
20
20
|
import { createEthereumChain, isAnvilTestChain } from './chain.js';
|
|
21
21
|
import { createExtendedL1Client } from './client.js';
|
|
@@ -23,7 +23,6 @@ import type { L1ContractsConfig } from './config.js';
|
|
|
23
23
|
import { deployMulticall3 } from './contracts/multicall.js';
|
|
24
24
|
import { RollupContract } from './contracts/rollup.js';
|
|
25
25
|
import type { L1ContractAddresses } from './l1_contract_addresses.js';
|
|
26
|
-
import type { L1TxUtilsConfig } from './l1_tx_utils/config.js';
|
|
27
26
|
import type { ExtendedViemWalletClient } from './types.js';
|
|
28
27
|
|
|
29
28
|
const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
|
|
@@ -31,9 +30,9 @@ const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
|
|
|
31
30
|
const JSON_DEPLOY_RESULT_PREFIX = 'JSON DEPLOY RESULT:';
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
|
-
* Runs a process
|
|
35
|
-
*
|
|
36
|
-
*
|
|
33
|
+
* Runs a process and parses JSON deploy results from stdout.
|
|
34
|
+
* Lines starting with JSON_DEPLOY_RESULT_PREFIX are parsed and returned.
|
|
35
|
+
* All other stdout goes to logger.info, stderr goes to logger.warn.
|
|
37
36
|
*/
|
|
38
37
|
function runProcess<T>(
|
|
39
38
|
command: string,
|
|
@@ -49,26 +48,41 @@ function runProcess<T>(
|
|
|
49
48
|
});
|
|
50
49
|
|
|
51
50
|
let result: T | undefined;
|
|
51
|
+
let parseError: Error | undefined;
|
|
52
|
+
let settled = false;
|
|
52
53
|
|
|
53
54
|
readline.createInterface({ input: proc.stdout }).on('line', line => {
|
|
54
55
|
const trimmedLine = line.trim();
|
|
55
56
|
if (trimmedLine.startsWith(JSON_DEPLOY_RESULT_PREFIX)) {
|
|
56
57
|
const jsonStr = trimmedLine.slice(JSON_DEPLOY_RESULT_PREFIX.length).trim();
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
try {
|
|
59
|
+
result = JSON.parse(jsonStr);
|
|
60
|
+
} catch {
|
|
61
|
+
parseError = new Error(`Failed to parse deploy result JSON: ${jsonStr.slice(0, 200)}`);
|
|
62
|
+
}
|
|
59
63
|
} else {
|
|
60
64
|
logger.info(line);
|
|
61
65
|
}
|
|
62
66
|
});
|
|
63
|
-
readline.createInterface({ input: proc.stderr }).on('line', logger.
|
|
67
|
+
readline.createInterface({ input: proc.stderr }).on('line', logger.warn.bind(logger));
|
|
64
68
|
|
|
65
69
|
proc.on('error', error => {
|
|
70
|
+
if (settled) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
settled = true;
|
|
66
74
|
reject(new Error(`Failed to spawn ${command}: ${error.message}`));
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
proc.on('close', code => {
|
|
78
|
+
if (settled) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
settled = true;
|
|
70
82
|
if (code !== 0) {
|
|
71
|
-
reject(new Error(`${command} exited with code ${code}
|
|
83
|
+
reject(new Error(`${command} exited with code ${code}`));
|
|
84
|
+
} else if (parseError) {
|
|
85
|
+
reject(parseError);
|
|
72
86
|
} else {
|
|
73
87
|
resolve(result);
|
|
74
88
|
}
|
|
@@ -168,6 +182,9 @@ export function prepareL1ContractsForDeployment(): string {
|
|
|
168
182
|
const foundryTomlPath = join(basePath, 'foundry.toml');
|
|
169
183
|
let foundryToml = readFileSync(foundryTomlPath, 'utf-8');
|
|
170
184
|
const solcPathMatch = foundryToml.match(/solc\s*=\s*"\.\/solc-([^"]+)"/);
|
|
185
|
+
// Did we find a hardcoded solc path that we need to make absolute?
|
|
186
|
+
// This code path happens in CI currently as we bundle solc there to avoid race conditions when
|
|
187
|
+
// downloading solc.
|
|
171
188
|
if (solcPathMatch) {
|
|
172
189
|
const solcVersion = solcPathMatch[1];
|
|
173
190
|
const absoluteSolcPath = join(basePath, `solc-${solcVersion}`);
|
|
@@ -318,11 +335,8 @@ export async function deployAztecL1Contracts(
|
|
|
318
335
|
);
|
|
319
336
|
}
|
|
320
337
|
|
|
321
|
-
|
|
322
|
-
const MAGIC_ANVIL_BATCH_SIZE = 8;
|
|
323
|
-
// Anvil seems to stall with unbounded batch size. Otherwise no max batch size is desirable.
|
|
338
|
+
const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
|
|
324
339
|
const forgeArgs = [
|
|
325
|
-
'script',
|
|
326
340
|
FORGE_SCRIPT,
|
|
327
341
|
'--sig',
|
|
328
342
|
'run()',
|
|
@@ -330,8 +344,6 @@ export async function deployAztecL1Contracts(
|
|
|
330
344
|
privateKey,
|
|
331
345
|
'--rpc-url',
|
|
332
346
|
rpcUrl,
|
|
333
|
-
'--broadcast',
|
|
334
|
-
...(chainId === foundry.id ? ['--batch-size', MAGIC_ANVIL_BATCH_SIZE.toString()] : []),
|
|
335
347
|
...(shouldVerify ? ['--verify'] : []),
|
|
336
348
|
];
|
|
337
349
|
const forgeEnv = {
|
|
@@ -340,7 +352,12 @@ export async function deployAztecL1Contracts(
|
|
|
340
352
|
FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
|
|
341
353
|
...getDeployAztecL1ContractsEnvVars(args),
|
|
342
354
|
};
|
|
343
|
-
const result = await runProcess<ForgeL1ContractsDeployResult>(
|
|
355
|
+
const result = await runProcess<ForgeL1ContractsDeployResult>(
|
|
356
|
+
process.execPath,
|
|
357
|
+
[scriptPath, ...forgeArgs],
|
|
358
|
+
forgeEnv,
|
|
359
|
+
l1ContractsPath,
|
|
360
|
+
);
|
|
344
361
|
if (!result) {
|
|
345
362
|
throw new Error('Forge script did not output deployment result');
|
|
346
363
|
}
|
|
@@ -473,7 +490,25 @@ export type VerificationRecord = {
|
|
|
473
490
|
libraries: VerificationLibraryEntry[];
|
|
474
491
|
};
|
|
475
492
|
|
|
476
|
-
export interface DeployAztecL1ContractsArgs
|
|
493
|
+
export interface DeployAztecL1ContractsArgs
|
|
494
|
+
extends Omit<
|
|
495
|
+
L1ContractsConfig,
|
|
496
|
+
| 'gasLimitBufferPercentage'
|
|
497
|
+
| 'maxGwei'
|
|
498
|
+
| 'maxBlobGwei'
|
|
499
|
+
| 'priorityFeeBumpPercentage'
|
|
500
|
+
| 'priorityFeeRetryBumpPercentage'
|
|
501
|
+
| 'minimumPriorityFeePerGas'
|
|
502
|
+
| 'maxSpeedUpAttempts'
|
|
503
|
+
| 'checkIntervalMs'
|
|
504
|
+
| 'stallTimeMs'
|
|
505
|
+
| 'txTimeoutMs'
|
|
506
|
+
| 'cancelTxOnTimeout'
|
|
507
|
+
| 'txCancellationFinalTimeoutMs'
|
|
508
|
+
| 'txUnseenConsideredDroppedMs'
|
|
509
|
+
| 'enableDelayer'
|
|
510
|
+
| 'txDelayerMaxInclusionTimeIntoSlot'
|
|
511
|
+
> {
|
|
477
512
|
/** The vk tree root. */
|
|
478
513
|
vkTreeRoot: Fr;
|
|
479
514
|
/** The hash of the protocol contracts. */
|
|
@@ -583,17 +618,8 @@ export const deployRollupForUpgrade = async (
|
|
|
583
618
|
const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
|
|
584
619
|
await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
585
620
|
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
FORGE_SCRIPT,
|
|
589
|
-
'--sig',
|
|
590
|
-
'run()',
|
|
591
|
-
'--private-key',
|
|
592
|
-
privateKey,
|
|
593
|
-
'--rpc-url',
|
|
594
|
-
rpcUrl,
|
|
595
|
-
'--broadcast',
|
|
596
|
-
];
|
|
621
|
+
const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
|
|
622
|
+
const forgeArgs = [FORGE_SCRIPT, '--sig', 'run()', '--private-key', privateKey, '--rpc-url', rpcUrl];
|
|
597
623
|
const forgeEnv = {
|
|
598
624
|
FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
|
|
599
625
|
// Env vars required by l1-contracts/script/deploy/RollupConfiguration.sol.
|
|
@@ -602,7 +628,12 @@ export const deployRollupForUpgrade = async (
|
|
|
602
628
|
...getDeployRollupForUpgradeEnvVars(args),
|
|
603
629
|
};
|
|
604
630
|
|
|
605
|
-
const result = await runProcess<ForgeRollupUpgradeResult>(
|
|
631
|
+
const result = await runProcess<ForgeRollupUpgradeResult>(
|
|
632
|
+
process.execPath,
|
|
633
|
+
[scriptPath, ...forgeArgs],
|
|
634
|
+
forgeEnv,
|
|
635
|
+
l1ContractsPath,
|
|
636
|
+
);
|
|
606
637
|
if (!result) {
|
|
607
638
|
throw new Error('Forge script did not output deployment result');
|
|
608
639
|
}
|