@aztec/ethereum 0.0.1-commit.96bb3f7 → 0.0.1-commit.993d240
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/client.d.ts +16 -2
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +27 -7
- package/dest/config.d.ts +22 -33
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +59 -64
- package/dest/contracts/chain_state_override.d.ts +78 -0
- package/dest/contracts/chain_state_override.d.ts.map +1 -0
- package/dest/contracts/chain_state_override.js +137 -0
- 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 +105 -0
- package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
- package/dest/contracts/fee_asset_price_oracle.js +656 -0
- package/dest/contracts/governance.d.ts +110 -33
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +207 -16
- package/dest/contracts/governance_proposer.d.ts +33 -1
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +57 -3
- package/dest/contracts/inbox.d.ts +18 -1
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +38 -2
- package/dest/contracts/index.d.ts +5 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +4 -2
- 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 +127 -12
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +201 -93
- package/dest/contracts/outbox.d.ts +21 -4
- package/dest/contracts/outbox.d.ts.map +1 -1
- package/dest/contracts/outbox.js +29 -6
- package/dest/contracts/registry.d.ts +3 -1
- package/dest/contracts/registry.d.ts.map +1 -1
- package/dest/contracts/registry.js +30 -1
- package/dest/contracts/rollup.d.ts +5288 -27
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +432 -69
- package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +3 -4
- package/dest/contracts/slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +18 -13
- package/dest/deploy_aztec_l1_contracts.d.ts +9 -8
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +58 -32
- 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 +15699 -16241
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_artifacts.js +9 -24
- package/dest/l1_contract_addresses.d.ts +61 -65
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +101 -82
- package/dest/l1_reader.d.ts +5 -5
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +9 -7
- 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 +26 -7
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +101 -66
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +11 -7
- package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
- package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
- package/dest/{test → l1_tx_utils}/tx_delayer.js +67 -37
- package/dest/publisher_manager.d.ts +21 -6
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +81 -7
- package/dest/queries.d.ts +14 -3
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +66 -4
- package/dest/test/chain_monitor.d.ts +23 -3
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +38 -2
- package/dest/test/eth_cheat_codes.d.ts +24 -4
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +17 -4
- 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 +34 -4
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +69 -8
- package/dest/test/start_anvil.d.ts +23 -3
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +143 -29
- package/dest/test/upgrade_utils.js +2 -2
- package/dest/utils.d.ts +9 -1
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +81 -12
- package/package.json +9 -10
- package/src/client.ts +28 -2
- package/src/config.ts +75 -65
- package/src/contracts/README.md +157 -0
- package/src/contracts/chain_state_override.ts +200 -0
- package/src/contracts/fee_asset_handler.ts +2 -0
- package/src/contracts/fee_asset_price_oracle.ts +285 -0
- package/src/contracts/governance.ts +279 -13
- package/src/contracts/governance_proposer.ts +58 -2
- package/src/contracts/inbox.ts +57 -2
- package/src/contracts/index.ts +4 -2
- package/src/contracts/log.ts +13 -0
- package/src/contracts/multicall.ts +243 -102
- package/src/contracts/outbox.ts +38 -8
- package/src/contracts/registry.ts +31 -1
- package/src/contracts/rollup.ts +500 -68
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +16 -14
- package/src/deploy_aztec_l1_contracts.ts +81 -40
- package/src/deploy_l1_contract.ts +3 -3
- package/src/generated/l1-contracts-defaults.ts +32 -0
- package/src/l1_artifacts.ts +12 -35
- package/src/l1_contract_addresses.ts +120 -94
- package/src/l1_reader.ts +17 -10
- 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 +99 -48
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +11 -7
- package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
- package/src/publisher_manager.ts +107 -10
- package/src/queries.ts +73 -5
- package/src/test/chain_monitor.ts +65 -3
- package/src/test/eth_cheat_codes.ts +16 -4
- package/src/test/index.ts +0 -2
- package/src/test/rollup_cheat_codes.ts +75 -8
- package/src/test/start_anvil.ts +177 -29
- package/src/test/upgrade_utils.ts +2 -2
- package/src/utils.ts +88 -14
- package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
- package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/empire_slashing_proposer.js +0 -209
- package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
- 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/contracts/empire_slashing_proposer.ts +0 -270
- package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
- package/src/test/delayed_tx_utils.ts +0 -52
package/src/contracts/rollup.ts
CHANGED
|
@@ -4,7 +4,9 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
4
4
|
import { memoize } from '@aztec/foundation/decorators';
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
import type { ViemSignature } from '@aztec/foundation/eth-signature';
|
|
7
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
7
8
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
9
|
+
import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
|
|
8
10
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
9
11
|
import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
|
|
10
12
|
|
|
@@ -13,8 +15,10 @@ import {
|
|
|
13
15
|
type Account,
|
|
14
16
|
type GetContractReturnType,
|
|
15
17
|
type Hex,
|
|
18
|
+
type Log,
|
|
16
19
|
type StateOverride,
|
|
17
20
|
type WatchContractEventReturnType,
|
|
21
|
+
encodeAbiParameters,
|
|
18
22
|
encodeFunctionData,
|
|
19
23
|
getContract,
|
|
20
24
|
hexToBigInt,
|
|
@@ -28,10 +32,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
|
|
|
28
32
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
29
33
|
import type { ViemClient } from '../types.js';
|
|
30
34
|
import { formatViemError } from '../utils.js';
|
|
31
|
-
import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
|
|
32
35
|
import { GSEContract } from './gse.js';
|
|
36
|
+
import type { L1EventLog } from './log.js';
|
|
33
37
|
import { SlasherContract } from './slasher_contract.js';
|
|
34
|
-
import {
|
|
38
|
+
import { SlashingProposerContract } from './slashing_proposer.js';
|
|
35
39
|
import { checkBlockTag } from './utils.js';
|
|
36
40
|
|
|
37
41
|
export type ViemCommitteeAttestation = {
|
|
@@ -53,7 +57,6 @@ export type L1RollupContractAddresses = Pick<
|
|
|
53
57
|
| 'feeJuiceAddress'
|
|
54
58
|
| 'stakingAssetAddress'
|
|
55
59
|
| 'rewardDistributorAddress'
|
|
56
|
-
| 'slashFactoryAddress'
|
|
57
60
|
| 'gseAddress'
|
|
58
61
|
>;
|
|
59
62
|
|
|
@@ -69,6 +72,7 @@ export type ViemHeader = {
|
|
|
69
72
|
blockHeadersHash: `0x${string}`;
|
|
70
73
|
blobsHash: `0x${string}`;
|
|
71
74
|
inHash: `0x${string}`;
|
|
75
|
+
outHash: `0x${string}`;
|
|
72
76
|
slotNumber: bigint;
|
|
73
77
|
timestamp: bigint;
|
|
74
78
|
coinbase: `0x${string}`;
|
|
@@ -82,12 +86,6 @@ export type ViemGasFees = {
|
|
|
82
86
|
feePerL2Gas: bigint;
|
|
83
87
|
};
|
|
84
88
|
|
|
85
|
-
export enum SlashingProposerType {
|
|
86
|
-
None = 0,
|
|
87
|
-
Tally = 1,
|
|
88
|
-
Empire = 2,
|
|
89
|
-
}
|
|
90
|
-
|
|
91
89
|
/**
|
|
92
90
|
* Status of a validator/attester in the staking system.
|
|
93
91
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -105,7 +103,7 @@ export enum AttesterStatus {
|
|
|
105
103
|
export type FeeHeader = {
|
|
106
104
|
excessMana: bigint;
|
|
107
105
|
manaUsed: bigint;
|
|
108
|
-
|
|
106
|
+
ethPerFeeAsset: bigint;
|
|
109
107
|
congestionCost: bigint;
|
|
110
108
|
proverCost: bigint;
|
|
111
109
|
};
|
|
@@ -131,6 +129,39 @@ export type L1FeeData = {
|
|
|
131
129
|
blobFee: bigint;
|
|
132
130
|
};
|
|
133
131
|
|
|
132
|
+
/** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */
|
|
133
|
+
export enum TempCheckpointLogField {
|
|
134
|
+
HeaderHash = 0,
|
|
135
|
+
BlobCommitmentsHash = 1,
|
|
136
|
+
OutHash = 2,
|
|
137
|
+
AttestationsHash = 3,
|
|
138
|
+
PayloadDigest = 4,
|
|
139
|
+
SlotNumber = 5,
|
|
140
|
+
FeeHeader = 6,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Field-level override input for `tempCheckpointLogs[checkpointNumber]`. Covers the fields the
|
|
145
|
+
* `propose()` path actually reads back. `payloadDigest` is `Buffer32` because it carries an
|
|
146
|
+
* arbitrary `bytes32` value rather than a BN254 scalar. `slotNumber` carries the uint32 portion
|
|
147
|
+
* of the on-chain `CompressedSlot`.
|
|
148
|
+
*/
|
|
149
|
+
export type TempCheckpointLogOverrideFields = {
|
|
150
|
+
headerHash?: Fr;
|
|
151
|
+
outHash?: Fr;
|
|
152
|
+
payloadDigest?: Buffer32;
|
|
153
|
+
slotNumber?: SlotNumber;
|
|
154
|
+
feeHeader?: FeeHeader;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
158
|
+
export type ManaMinFeeComponents = {
|
|
159
|
+
sequencerCost: bigint;
|
|
160
|
+
proverCost: bigint;
|
|
161
|
+
congestionCost: bigint;
|
|
162
|
+
congestionMultiplier: bigint;
|
|
163
|
+
};
|
|
164
|
+
|
|
134
165
|
/**
|
|
135
166
|
* Reward configuration for the rollup
|
|
136
167
|
*/
|
|
@@ -185,10 +216,29 @@ export type RollupStatusResponse = {
|
|
|
185
216
|
archiveOfMyCheckpoint: Fr;
|
|
186
217
|
};
|
|
187
218
|
|
|
219
|
+
/** Arguments for the CheckpointProposed event. */
|
|
220
|
+
export type CheckpointProposedArgs = {
|
|
221
|
+
checkpointNumber: CheckpointNumber;
|
|
222
|
+
archive: Fr;
|
|
223
|
+
versionedBlobHashes: Buffer[];
|
|
224
|
+
/** Hash of attestations emitted in the CheckpointProposed event. */
|
|
225
|
+
attestationsHash: Buffer32;
|
|
226
|
+
/** Digest of the payload emitted in the CheckpointProposed event. */
|
|
227
|
+
payloadDigest: Buffer32;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/** Log type for CheckpointProposed events. */
|
|
231
|
+
export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
|
|
232
|
+
|
|
188
233
|
export class RollupContract {
|
|
189
234
|
private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
|
|
235
|
+
private readonly logger = createLogger('ethereum:rollup');
|
|
190
236
|
|
|
191
237
|
private static cachedStfStorageSlot: Hex | undefined;
|
|
238
|
+
private cachedEscapeHatch?: {
|
|
239
|
+
address: EthAddress;
|
|
240
|
+
contract: GetContractReturnType<typeof EscapeHatchAbi, ViemClient>;
|
|
241
|
+
};
|
|
192
242
|
|
|
193
243
|
static get checkBlobStorageSlot(): bigint {
|
|
194
244
|
const asString = RollupStorage.find(storage => storage.label === 'checkBlob')?.slot;
|
|
@@ -212,7 +262,7 @@ export class RollupContract {
|
|
|
212
262
|
|
|
213
263
|
static getFromConfig(config: L1ReaderConfig) {
|
|
214
264
|
const client = getPublicClient(config);
|
|
215
|
-
const address = config.
|
|
265
|
+
const address = config.rollupAddress.toString();
|
|
216
266
|
return new RollupContract(client, address);
|
|
217
267
|
}
|
|
218
268
|
|
|
@@ -238,34 +288,18 @@ export class RollupContract {
|
|
|
238
288
|
return this.rollup;
|
|
239
289
|
}
|
|
240
290
|
|
|
241
|
-
public async getSlashingProposer(): Promise<
|
|
242
|
-
EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
|
|
243
|
-
> {
|
|
291
|
+
public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
|
|
244
292
|
const slasher = await this.getSlasherContract();
|
|
245
293
|
if (!slasher) {
|
|
246
294
|
return undefined;
|
|
247
295
|
}
|
|
248
296
|
|
|
249
297
|
const proposerAddress = await slasher.getProposer();
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
type: 'function',
|
|
253
|
-
name: 'SLASHING_PROPOSER_TYPE',
|
|
254
|
-
inputs: [],
|
|
255
|
-
outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
|
|
256
|
-
stateMutability: 'view',
|
|
257
|
-
},
|
|
258
|
-
] as const;
|
|
259
|
-
|
|
260
|
-
const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
|
|
261
|
-
const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
|
|
262
|
-
if (proposerType === SlashingProposerType.Tally.valueOf()) {
|
|
263
|
-
return new TallySlashingProposerContract(this.client, proposerAddress);
|
|
264
|
-
} else if (proposerType === SlashingProposerType.Empire.valueOf()) {
|
|
265
|
-
return new EmpireSlashingProposerContract(this.client, proposerAddress);
|
|
266
|
-
} else {
|
|
267
|
-
throw new Error(`Unknown slashing proposer type: ${proposerType}`);
|
|
298
|
+
if (proposerAddress.isZero()) {
|
|
299
|
+
return undefined;
|
|
268
300
|
}
|
|
301
|
+
|
|
302
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
269
303
|
}
|
|
270
304
|
|
|
271
305
|
@memoize
|
|
@@ -358,6 +392,20 @@ export class RollupContract {
|
|
|
358
392
|
return Fr.fromString(await this.rollup.read.archiveAt([0n]));
|
|
359
393
|
}
|
|
360
394
|
|
|
395
|
+
@memoize
|
|
396
|
+
async getVkTreeRoot(): Promise<Fr> {
|
|
397
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
|
|
398
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
399
|
+
return Fr.fromString(value ?? '0x0');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
@memoize
|
|
403
|
+
async getProtocolContractsHash(): Promise<Fr> {
|
|
404
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
|
|
405
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
406
|
+
return Fr.fromString(value ?? '0x0');
|
|
407
|
+
}
|
|
408
|
+
|
|
361
409
|
/**
|
|
362
410
|
* Returns rollup constants used for epoch queries.
|
|
363
411
|
* Return type is `L1RollupConstants` which is defined in stdlib,
|
|
@@ -370,13 +418,25 @@ export class RollupContract {
|
|
|
370
418
|
slotDuration: number;
|
|
371
419
|
epochDuration: number;
|
|
372
420
|
proofSubmissionEpochs: number;
|
|
421
|
+
targetCommitteeSize: number;
|
|
422
|
+
rollupManaLimit: number;
|
|
373
423
|
}> {
|
|
374
|
-
const [
|
|
424
|
+
const [
|
|
425
|
+
l1StartBlock,
|
|
426
|
+
l1GenesisTime,
|
|
427
|
+
slotDuration,
|
|
428
|
+
epochDuration,
|
|
429
|
+
proofSubmissionEpochs,
|
|
430
|
+
targetCommitteeSize,
|
|
431
|
+
rollupManaLimit,
|
|
432
|
+
] = await Promise.all([
|
|
375
433
|
this.getL1StartBlock(),
|
|
376
434
|
this.getL1GenesisTime(),
|
|
377
435
|
this.getSlotDuration(),
|
|
378
436
|
this.getEpochDuration(),
|
|
379
437
|
this.getProofSubmissionEpochs(),
|
|
438
|
+
this.getTargetCommitteeSize(),
|
|
439
|
+
this.getManaLimit(),
|
|
380
440
|
]);
|
|
381
441
|
return {
|
|
382
442
|
l1StartBlock,
|
|
@@ -384,6 +444,8 @@ export class RollupContract {
|
|
|
384
444
|
slotDuration,
|
|
385
445
|
epochDuration: Number(epochDuration),
|
|
386
446
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
447
|
+
targetCommitteeSize,
|
|
448
|
+
rollupManaLimit: Number(rollupManaLimit),
|
|
387
449
|
};
|
|
388
450
|
}
|
|
389
451
|
|
|
@@ -391,6 +453,62 @@ export class RollupContract {
|
|
|
391
453
|
return EthAddress.fromString(await this.rollup.read.getSlasher());
|
|
392
454
|
}
|
|
393
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Returns the configured escape hatch contract address, or zero if disabled.
|
|
458
|
+
*/
|
|
459
|
+
async getEscapeHatchAddress(): Promise<EthAddress> {
|
|
460
|
+
return EthAddress.fromString(await this.rollup.read.getEscapeHatch());
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private async getEscapeHatchContract(): Promise<
|
|
464
|
+
GetContractReturnType<typeof EscapeHatchAbi, ViemClient> | undefined
|
|
465
|
+
> {
|
|
466
|
+
const escapeHatchAddress = await this.getEscapeHatchAddress();
|
|
467
|
+
if (escapeHatchAddress.isZero()) {
|
|
468
|
+
return undefined;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Cache the viem contract wrapper since it will be used frequently.
|
|
472
|
+
if (!this.cachedEscapeHatch || !this.cachedEscapeHatch.address.equals(escapeHatchAddress)) {
|
|
473
|
+
this.cachedEscapeHatch = {
|
|
474
|
+
address: escapeHatchAddress,
|
|
475
|
+
contract: getContract({
|
|
476
|
+
address: escapeHatchAddress.toString(),
|
|
477
|
+
abi: EscapeHatchAbi,
|
|
478
|
+
client: this.client,
|
|
479
|
+
}),
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return this.cachedEscapeHatch.contract;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Returns whether the escape hatch is open for the given epoch.
|
|
488
|
+
* If escape hatch is not configured, returns false.
|
|
489
|
+
*
|
|
490
|
+
* This function is intentionally defensive: any failure to query the escape hatch
|
|
491
|
+
* (RPC issues, transient errors, etc.) is treated as "closed" to avoid callers
|
|
492
|
+
* needing to sprinkle try/catch everywhere.
|
|
493
|
+
*/
|
|
494
|
+
async isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean> {
|
|
495
|
+
try {
|
|
496
|
+
const escapeHatch = await this.getEscapeHatchContract();
|
|
497
|
+
if (!escapeHatch) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
|
|
502
|
+
return isOpen;
|
|
503
|
+
} catch (err) {
|
|
504
|
+
this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
|
|
505
|
+
epoch: Number(epoch),
|
|
506
|
+
error: err,
|
|
507
|
+
});
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
394
512
|
/**
|
|
395
513
|
* Returns a SlasherContract instance for interacting with the slasher contract.
|
|
396
514
|
*/
|
|
@@ -422,28 +540,43 @@ export class RollupContract {
|
|
|
422
540
|
return this.rollup.read.getCheckpointReward();
|
|
423
541
|
}
|
|
424
542
|
|
|
425
|
-
async getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
426
|
-
|
|
543
|
+
async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
544
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
545
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
|
|
427
546
|
}
|
|
428
547
|
|
|
429
|
-
async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
430
|
-
|
|
548
|
+
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
549
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
550
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
431
551
|
}
|
|
432
552
|
|
|
433
|
-
async getSlotNumber(): Promise<SlotNumber> {
|
|
434
|
-
|
|
553
|
+
async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
|
|
554
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
555
|
+
return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
|
|
435
556
|
}
|
|
436
557
|
|
|
437
|
-
async getL1FeesAt(timestamp: bigint): Promise<L1FeeData> {
|
|
438
|
-
|
|
558
|
+
async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
|
|
559
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
560
|
+
const result = await this.rollup.read.getL1FeesAt([timestamp], options);
|
|
439
561
|
return {
|
|
440
562
|
baseFee: result.baseFee,
|
|
441
563
|
blobFee: result.blobFee,
|
|
442
564
|
};
|
|
443
565
|
}
|
|
444
566
|
|
|
445
|
-
|
|
446
|
-
|
|
567
|
+
async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
|
|
568
|
+
const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
|
|
569
|
+
return {
|
|
570
|
+
excessMana: result.excessMana,
|
|
571
|
+
manaUsed: result.manaUsed,
|
|
572
|
+
ethPerFeeAsset: result.ethPerFeeAsset,
|
|
573
|
+
congestionCost: result.congestionCost,
|
|
574
|
+
proverCost: result.proverCost,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
getEthPerFeeAsset(): Promise<bigint> {
|
|
579
|
+
return this.rollup.read.getEthPerFeeAsset();
|
|
447
580
|
}
|
|
448
581
|
|
|
449
582
|
async getCommitteeAt(timestamp: bigint): Promise<EthAddress[] | undefined> {
|
|
@@ -516,8 +649,9 @@ export class RollupContract {
|
|
|
516
649
|
return EthAddress.fromString(result);
|
|
517
650
|
}
|
|
518
651
|
|
|
519
|
-
async getCheckpoint(checkpointNumber: CheckpointNumber): Promise<CheckpointLog> {
|
|
520
|
-
|
|
652
|
+
async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
|
|
653
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
654
|
+
const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
|
|
521
655
|
return {
|
|
522
656
|
archive: Fr.fromString(result.archive),
|
|
523
657
|
headerHash: Buffer32.fromString(result.headerHash),
|
|
@@ -528,7 +662,7 @@ export class RollupContract {
|
|
|
528
662
|
feeHeader: {
|
|
529
663
|
excessMana: result.feeHeader.excessMana,
|
|
530
664
|
manaUsed: result.feeHeader.manaUsed,
|
|
531
|
-
|
|
665
|
+
ethPerFeeAsset: result.feeHeader.ethPerFeeAsset,
|
|
532
666
|
congestionCost: result.feeHeader.congestionCost,
|
|
533
667
|
proverCost: result.feeHeader.proverCost,
|
|
534
668
|
},
|
|
@@ -550,6 +684,30 @@ export class RollupContract {
|
|
|
550
684
|
);
|
|
551
685
|
}
|
|
552
686
|
|
|
687
|
+
/**
|
|
688
|
+
* Returns the effective pending checkpoint, accounting for potential prunes.
|
|
689
|
+
* When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
|
|
690
|
+
* This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
|
|
691
|
+
* @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
|
|
692
|
+
* @param options - Optional L1 block number to pin the queries to.
|
|
693
|
+
*/
|
|
694
|
+
getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
|
|
695
|
+
return retry(
|
|
696
|
+
async () => {
|
|
697
|
+
const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
|
|
698
|
+
const canPrune = await this.canPruneAtTime(timestamp, options);
|
|
699
|
+
if (canPrune) {
|
|
700
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
|
|
701
|
+
return await this.getCheckpoint(provenCheckpointNumber, options);
|
|
702
|
+
}
|
|
703
|
+
const pendingCheckpointNumber = await this.getCheckpointNumber(options);
|
|
704
|
+
return await this.getCheckpoint(pendingCheckpointNumber, options);
|
|
705
|
+
},
|
|
706
|
+
'getting effective pending checkpoint',
|
|
707
|
+
makeBackoff([0.5, 0.5, 0.5]),
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
553
711
|
async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
|
|
554
712
|
const { pending, proven } = await this.rollup.read.getTips();
|
|
555
713
|
return {
|
|
@@ -668,14 +826,13 @@ export class RollupContract {
|
|
|
668
826
|
* timestamp of the next L1 block
|
|
669
827
|
* @throws otherwise
|
|
670
828
|
*/
|
|
671
|
-
public async
|
|
829
|
+
public async canProposeAt(
|
|
672
830
|
archive: Buffer,
|
|
673
831
|
account: `0x${string}` | Account,
|
|
674
|
-
|
|
675
|
-
|
|
832
|
+
timestamp: bigint,
|
|
833
|
+
stateOverride: StateOverride = [],
|
|
676
834
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
677
|
-
const
|
|
678
|
-
const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
|
|
835
|
+
const timeOfNextL1Slot = timestamp;
|
|
679
836
|
const who = typeof account === 'string' ? account : account.address;
|
|
680
837
|
|
|
681
838
|
try {
|
|
@@ -687,7 +844,7 @@ export class RollupContract {
|
|
|
687
844
|
functionName: 'canProposeAtTime',
|
|
688
845
|
args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
|
|
689
846
|
account,
|
|
690
|
-
stateOverride
|
|
847
|
+
stateOverride,
|
|
691
848
|
});
|
|
692
849
|
|
|
693
850
|
return {
|
|
@@ -701,20 +858,32 @@ export class RollupContract {
|
|
|
701
858
|
}
|
|
702
859
|
|
|
703
860
|
/**
|
|
704
|
-
* Returns a state override that sets the pending
|
|
705
|
-
*
|
|
706
|
-
*
|
|
861
|
+
* Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
|
|
862
|
+
* Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
|
|
863
|
+
* a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
|
|
864
|
+
* value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
|
|
865
|
+
*
|
|
866
|
+
* Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
|
|
867
|
+
* `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
|
|
707
868
|
*/
|
|
708
|
-
public async
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
869
|
+
public async makeChainTipsOverride(override: {
|
|
870
|
+
pending?: CheckpointNumber;
|
|
871
|
+
proven?: CheckpointNumber;
|
|
872
|
+
}): Promise<StateOverride> {
|
|
873
|
+
if (override.pending === undefined && override.proven === undefined) {
|
|
712
874
|
return [];
|
|
713
875
|
}
|
|
714
876
|
const slot = RollupContract.stfStorageSlot;
|
|
715
877
|
const currentValue = await this.client.getStorageAt({ address: this.address, slot });
|
|
716
|
-
const
|
|
717
|
-
const
|
|
878
|
+
const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
|
|
879
|
+
const currentPending = currentRaw >> 128n;
|
|
880
|
+
const currentProven = currentRaw & ((1n << 128n) - 1n);
|
|
881
|
+
const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
|
|
882
|
+
const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
|
|
883
|
+
if (newProven > newPending) {
|
|
884
|
+
throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
|
|
885
|
+
}
|
|
886
|
+
const newValue = (newPending << 128n) | newProven;
|
|
718
887
|
return [
|
|
719
888
|
{
|
|
720
889
|
address: this.address,
|
|
@@ -723,6 +892,168 @@ export class RollupContract {
|
|
|
723
892
|
];
|
|
724
893
|
}
|
|
725
894
|
|
|
895
|
+
/**
|
|
896
|
+
* Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
|
|
897
|
+
* the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
|
|
898
|
+
* pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
|
|
899
|
+
*
|
|
900
|
+
* `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
|
|
901
|
+
* never asserts against them, so leaving them at storage zero is harmless.
|
|
902
|
+
*/
|
|
903
|
+
public async makeTempCheckpointLogOverride(
|
|
904
|
+
checkpointNumber: CheckpointNumber,
|
|
905
|
+
fields: TempCheckpointLogOverrideFields,
|
|
906
|
+
): Promise<StateOverride> {
|
|
907
|
+
const constants = await this.getRollupConstants();
|
|
908
|
+
const slotAt = (field: TempCheckpointLogField) =>
|
|
909
|
+
`0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}` as const;
|
|
910
|
+
const word = (v: bigint) => `0x${v.toString(16).padStart(64, '0')}` as const;
|
|
911
|
+
|
|
912
|
+
const stateDiff: { slot: `0x${string}`; value: `0x${string}` }[] = [];
|
|
913
|
+
|
|
914
|
+
if (fields.headerHash) {
|
|
915
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.HeaderHash), value: fields.headerHash.toString() });
|
|
916
|
+
}
|
|
917
|
+
if (fields.outHash) {
|
|
918
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.OutHash), value: fields.outHash.toString() });
|
|
919
|
+
}
|
|
920
|
+
if (fields.payloadDigest) {
|
|
921
|
+
stateDiff.push({
|
|
922
|
+
slot: slotAt(TempCheckpointLogField.PayloadDigest),
|
|
923
|
+
value: fields.payloadDigest.toString() as `0x${string}`,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
if (fields.slotNumber !== undefined) {
|
|
927
|
+
// CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here
|
|
928
|
+
// so a malformed override surfaces immediately rather than silently truncating into a wrong slot.
|
|
929
|
+
const slotNumber = BigInt(fields.slotNumber);
|
|
930
|
+
if (slotNumber < 0n || slotNumber > 0xffffffffn) {
|
|
931
|
+
throw new Error(`slotNumber ${slotNumber} does not fit in uint32`);
|
|
932
|
+
}
|
|
933
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.SlotNumber), value: word(slotNumber) });
|
|
934
|
+
}
|
|
935
|
+
if (fields.feeHeader) {
|
|
936
|
+
stateDiff.push({
|
|
937
|
+
slot: slotAt(TempCheckpointLogField.FeeHeader),
|
|
938
|
+
value: word(RollupContract.compressFeeHeader(fields.feeHeader)),
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (stateDiff.length === 0) {
|
|
943
|
+
return [];
|
|
944
|
+
}
|
|
945
|
+
return [{ address: this.address, stateDiff }];
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Returns a state override that sets archives[checkpointNumber] to the given archive value.
|
|
950
|
+
* Used when simulating a canProposeAtTime call where the local archive differs from L1
|
|
951
|
+
* (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
|
|
952
|
+
*/
|
|
953
|
+
public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
|
|
954
|
+
const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
|
|
955
|
+
const archiveSlot = hexToBigInt(
|
|
956
|
+
keccak256(
|
|
957
|
+
encodeAbiParameters(
|
|
958
|
+
[{ type: 'uint256' }, { type: 'uint256' }],
|
|
959
|
+
[BigInt(checkpointNumber), archivesMappingBase],
|
|
960
|
+
),
|
|
961
|
+
),
|
|
962
|
+
);
|
|
963
|
+
return [
|
|
964
|
+
{
|
|
965
|
+
address: this.address,
|
|
966
|
+
stateDiff: [
|
|
967
|
+
{
|
|
968
|
+
slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
|
|
969
|
+
value: archive.toString(),
|
|
970
|
+
},
|
|
971
|
+
],
|
|
972
|
+
},
|
|
973
|
+
];
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
|
|
977
|
+
public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
|
|
978
|
+
type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
|
|
979
|
+
const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
|
|
980
|
+
for (const override of overrides) {
|
|
981
|
+
for (const entry of override) {
|
|
982
|
+
const key = entry.address.toLowerCase();
|
|
983
|
+
const existing = byAddress.get(key);
|
|
984
|
+
if (existing) {
|
|
985
|
+
existing.stateDiff.push(...(entry.stateDiff ?? []));
|
|
986
|
+
if (entry.balance !== undefined) {
|
|
987
|
+
existing.balance = entry.balance;
|
|
988
|
+
}
|
|
989
|
+
} else {
|
|
990
|
+
byAddress.set(key, {
|
|
991
|
+
address: entry.address,
|
|
992
|
+
balance: entry.balance,
|
|
993
|
+
stateDiff: [...(entry.stateDiff ?? [])],
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return [...byAddress.values()];
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
|
|
1002
|
+
public static compressFeeHeader(feeHeader: FeeHeader): bigint {
|
|
1003
|
+
const MASK_48_BITS = (1n << 48n) - 1n;
|
|
1004
|
+
const MASK_64_BITS = (1n << 64n) - 1n;
|
|
1005
|
+
const MASK_63_BITS = (1n << 63n) - 1n;
|
|
1006
|
+
|
|
1007
|
+
let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
|
|
1008
|
+
value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
|
|
1009
|
+
value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
|
|
1010
|
+
value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
|
|
1011
|
+
value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
|
|
1012
|
+
value |= 1n << 255n; // preheat flag
|
|
1013
|
+
return value;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/** Computes the fee header for a child checkpoint given parent fee header and child data.
|
|
1017
|
+
* Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
|
|
1018
|
+
public static computeChildFeeHeader(
|
|
1019
|
+
parentFeeHeader: FeeHeader,
|
|
1020
|
+
childManaUsed: bigint,
|
|
1021
|
+
feeAssetPriceModifier: bigint,
|
|
1022
|
+
manaTarget: bigint,
|
|
1023
|
+
): FeeHeader {
|
|
1024
|
+
const MIN_ETH_PER_FEE_ASSET = 100n;
|
|
1025
|
+
const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
|
|
1026
|
+
|
|
1027
|
+
// excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
|
|
1028
|
+
const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
|
|
1029
|
+
const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
|
|
1030
|
+
|
|
1031
|
+
// ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
|
|
1032
|
+
const parentPrice =
|
|
1033
|
+
parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
|
|
1034
|
+
let newPrice: bigint;
|
|
1035
|
+
if (feeAssetPriceModifier >= 0n) {
|
|
1036
|
+
newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
|
|
1037
|
+
} else {
|
|
1038
|
+
const absMod = -feeAssetPriceModifier;
|
|
1039
|
+
newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
|
|
1040
|
+
}
|
|
1041
|
+
if (newPrice < MIN_ETH_PER_FEE_ASSET) {
|
|
1042
|
+
newPrice = MIN_ETH_PER_FEE_ASSET;
|
|
1043
|
+
}
|
|
1044
|
+
if (newPrice > MAX_ETH_PER_FEE_ASSET) {
|
|
1045
|
+
newPrice = MAX_ETH_PER_FEE_ASSET;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
return {
|
|
1049
|
+
excessMana,
|
|
1050
|
+
manaUsed: childManaUsed,
|
|
1051
|
+
ethPerFeeAsset: newPrice,
|
|
1052
|
+
congestionCost: 0n,
|
|
1053
|
+
proverCost: 0n,
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
|
|
726
1057
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
|
|
727
1058
|
public buildInvalidateBadAttestationRequest(
|
|
728
1059
|
checkpointNumber: CheckpointNumber,
|
|
@@ -732,6 +1063,7 @@ export class RollupContract {
|
|
|
732
1063
|
): L1TxRequest {
|
|
733
1064
|
return {
|
|
734
1065
|
to: this.address,
|
|
1066
|
+
abi: RollupAbi,
|
|
735
1067
|
data: encodeFunctionData({
|
|
736
1068
|
abi: RollupAbi,
|
|
737
1069
|
functionName: 'invalidateBadAttestation',
|
|
@@ -753,6 +1085,7 @@ export class RollupContract {
|
|
|
753
1085
|
): L1TxRequest {
|
|
754
1086
|
return {
|
|
755
1087
|
to: this.address,
|
|
1088
|
+
abi: RollupAbi,
|
|
756
1089
|
data: encodeFunctionData({
|
|
757
1090
|
abi: RollupAbi,
|
|
758
1091
|
functionName: 'invalidateInsufficientAttestations',
|
|
@@ -769,8 +1102,18 @@ export class RollupContract {
|
|
|
769
1102
|
return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
|
|
770
1103
|
}
|
|
771
1104
|
|
|
772
|
-
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
|
|
773
|
-
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
|
|
1105
|
+
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
|
|
1106
|
+
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
1110
|
+
const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
|
|
1111
|
+
return {
|
|
1112
|
+
sequencerCost: result.sequencerCost,
|
|
1113
|
+
proverCost: result.proverCost,
|
|
1114
|
+
congestionCost: result.congestionCost,
|
|
1115
|
+
congestionMultiplier: result.congestionMultiplier,
|
|
1116
|
+
};
|
|
774
1117
|
}
|
|
775
1118
|
|
|
776
1119
|
async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
|
|
@@ -816,11 +1159,10 @@ export class RollupContract {
|
|
|
816
1159
|
return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
|
|
817
1160
|
}
|
|
818
1161
|
|
|
819
|
-
async getAttesters(): Promise<EthAddress[]> {
|
|
1162
|
+
async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
|
|
820
1163
|
const attesterSize = await this.getActiveAttesterCount();
|
|
821
1164
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
822
|
-
const ts = (await this.client.getBlock()).timestamp;
|
|
823
|
-
|
|
1165
|
+
const ts = timestamp ?? (await this.client.getBlock()).timestamp;
|
|
824
1166
|
const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
|
|
825
1167
|
const chunks = chunk(indices, 1000);
|
|
826
1168
|
|
|
@@ -886,6 +1228,7 @@ export class RollupContract {
|
|
|
886
1228
|
setupEpoch(l1TxUtils: L1TxUtils) {
|
|
887
1229
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
888
1230
|
to: this.address,
|
|
1231
|
+
abi: RollupAbi,
|
|
889
1232
|
data: encodeFunctionData({
|
|
890
1233
|
abi: RollupAbi,
|
|
891
1234
|
functionName: 'setupEpoch',
|
|
@@ -897,6 +1240,7 @@ export class RollupContract {
|
|
|
897
1240
|
vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
|
|
898
1241
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
899
1242
|
to: this.address,
|
|
1243
|
+
abi: RollupAbi,
|
|
900
1244
|
data: encodeFunctionData({
|
|
901
1245
|
abi: RollupAbi,
|
|
902
1246
|
functionName: 'vote',
|
|
@@ -924,7 +1268,7 @@ export class RollupContract {
|
|
|
924
1268
|
}
|
|
925
1269
|
|
|
926
1270
|
public listenToCheckpointInvalidated(
|
|
927
|
-
callback: (args: { checkpointNumber: CheckpointNumber }) => unknown,
|
|
1271
|
+
callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
|
|
928
1272
|
): WatchContractEventReturnType {
|
|
929
1273
|
return this.rollup.watchEvent.CheckpointInvalidated(
|
|
930
1274
|
{},
|
|
@@ -933,7 +1277,7 @@ export class RollupContract {
|
|
|
933
1277
|
for (const log of logs) {
|
|
934
1278
|
const args = log.args;
|
|
935
1279
|
if (args.checkpointNumber !== undefined) {
|
|
936
|
-
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber) });
|
|
1280
|
+
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
|
|
937
1281
|
}
|
|
938
1282
|
}
|
|
939
1283
|
},
|
|
@@ -965,4 +1309,92 @@ export class RollupContract {
|
|
|
965
1309
|
},
|
|
966
1310
|
);
|
|
967
1311
|
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
|
|
1315
|
+
* The Rollup inherits from Ownable and emits this event in its constructor, so the event
|
|
1316
|
+
* is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
|
|
1317
|
+
* probe to detect RPC nodes that prune historical logs.
|
|
1318
|
+
*/
|
|
1319
|
+
async getOwnershipTransferredEventsAtDeploy() {
|
|
1320
|
+
const l1StartBlock = await this.getL1StartBlock();
|
|
1321
|
+
return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/** Fetches CheckpointProposed events within the given block range. */
|
|
1325
|
+
async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
|
|
1326
|
+
const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
|
|
1327
|
+
return logs
|
|
1328
|
+
.filter(log => log.blockNumber! >= fromBlock && log.blockNumber! <= toBlock)
|
|
1329
|
+
.map(log => ({
|
|
1330
|
+
l1BlockNumber: log.blockNumber!,
|
|
1331
|
+
l1BlockHash: Buffer32.fromString(log.blockHash!),
|
|
1332
|
+
l1TransactionHash: log.transactionHash!,
|
|
1333
|
+
args: {
|
|
1334
|
+
checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
|
|
1335
|
+
archive: Fr.fromString(log.args.archive!),
|
|
1336
|
+
versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
|
|
1337
|
+
attestationsHash: (() => {
|
|
1338
|
+
if (!log.args.attestationsHash) {
|
|
1339
|
+
throw new Error(
|
|
1340
|
+
`CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
return Buffer32.fromString(log.args.attestationsHash);
|
|
1344
|
+
})(),
|
|
1345
|
+
payloadDigest: (() => {
|
|
1346
|
+
if (!log.args.payloadDigest) {
|
|
1347
|
+
throw new Error(
|
|
1348
|
+
`CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
return Buffer32.fromString(log.args.payloadDigest);
|
|
1352
|
+
})(),
|
|
1353
|
+
},
|
|
1354
|
+
}));
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
/** Packs pending and proven checkpoint numbers into the chain tips storage format. */
|
|
1358
|
+
static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
|
|
1359
|
+
return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
/** Storage slot for the chain tips (offset 0 within the STF storage struct). */
|
|
1363
|
+
static get chainTipsStorageSlot(): bigint {
|
|
1364
|
+
return BigInt(RollupContract.stfStorageSlot);
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* Computes the storage slot for a field within a tempCheckpointLog entry.
|
|
1369
|
+
* @param checkpointNumber - The checkpoint number
|
|
1370
|
+
* @param field - The field within the CompressedTempCheckpointLog struct
|
|
1371
|
+
*/
|
|
1372
|
+
async getTempCheckpointLogStorageSlot(
|
|
1373
|
+
checkpointNumber: CheckpointNumber,
|
|
1374
|
+
field: TempCheckpointLogField,
|
|
1375
|
+
): Promise<bigint> {
|
|
1376
|
+
const [epochDuration, proofSubmissionEpochs] = await Promise.all([
|
|
1377
|
+
this.getEpochDuration(),
|
|
1378
|
+
this.getProofSubmissionEpochs(),
|
|
1379
|
+
]);
|
|
1380
|
+
return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, { epochDuration, proofSubmissionEpochs });
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
private computeTempCheckpointLogStorageSlot(
|
|
1384
|
+
checkpointNumber: CheckpointNumber,
|
|
1385
|
+
field: TempCheckpointLogField,
|
|
1386
|
+
constants: { epochDuration: number; proofSubmissionEpochs: number },
|
|
1387
|
+
): bigint {
|
|
1388
|
+
const fieldOffset = BigInt(field);
|
|
1389
|
+
const { epochDuration, proofSubmissionEpochs } = constants;
|
|
1390
|
+
const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
|
|
1391
|
+
const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
|
|
1392
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
1393
|
+
const entryBase = BigInt(
|
|
1394
|
+
keccak256(
|
|
1395
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
|
|
1396
|
+
),
|
|
1397
|
+
);
|
|
1398
|
+
return entryBase + fieldOffset;
|
|
1399
|
+
}
|
|
968
1400
|
}
|