@aztec/ethereum 0.0.1-commit.2ed92850 → 0.0.1-commit.2f68f620
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 +25 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +60 -8
- package/dest/config.d.ts +8 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +15 -11
- 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 +108 -33
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +198 -15
- 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 +3 -3
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +12 -7
- package/dest/contracts/index.d.ts +4 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +3 -2
- 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 +27 -4
- package/dest/contracts/outbox.d.ts.map +1 -1
- package/dest/contracts/outbox.js +34 -7
- 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 +5259 -29
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +392 -71
- 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 +8 -11
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +46 -34
- package/dest/deploy_l1_contract.js +3 -3
- package/dest/generated/l1-contracts-defaults.d.ts +2 -2
- package/dest/generated/l1-contracts-defaults.js +2 -2
- package/dest/l1_artifacts.d.ts +14259 -15724
- 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 +13 -8
- 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 +13 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +62 -3
- 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 +12 -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 +33 -3
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +68 -7
- 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 +10 -2
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +94 -16
- package/package.json +6 -8
- package/src/client.ts +66 -2
- package/src/config.ts +24 -15
- 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 +271 -12
- package/src/contracts/governance_proposer.ts +58 -2
- package/src/contracts/inbox.ts +13 -5
- package/src/contracts/index.ts +3 -2
- package/src/contracts/multicall.ts +243 -102
- package/src/contracts/outbox.ts +42 -8
- package/src/contracts/registry.ts +31 -1
- package/src/contracts/rollup.ts +443 -73
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +16 -14
- package/src/deploy_aztec_l1_contracts.ts +69 -42
- package/src/deploy_l1_contract.ts +3 -3
- package/src/generated/l1-contracts-defaults.ts +2 -2
- 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 +13 -8
- package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
- package/src/publisher_manager.ts +107 -10
- package/src/queries.ts +70 -4
- 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 +73 -6
- package/src/test/start_anvil.ts +177 -29
- package/src/test/upgrade_utils.ts +2 -2
- package/src/utils.ts +98 -18
- 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 { getErrorCause } from '@aztec/foundation/types';
|
|
8
10
|
import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
|
|
9
11
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
10
12
|
import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
|
|
@@ -12,10 +14,15 @@ import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
|
|
|
12
14
|
import chunk from 'lodash.chunk';
|
|
13
15
|
import {
|
|
14
16
|
type Account,
|
|
17
|
+
ContractFunctionRevertedError,
|
|
15
18
|
type GetContractReturnType,
|
|
16
19
|
type Hex,
|
|
20
|
+
type Log,
|
|
21
|
+
RpcRequestError,
|
|
17
22
|
type StateOverride,
|
|
18
23
|
type WatchContractEventReturnType,
|
|
24
|
+
decodeErrorResult,
|
|
25
|
+
encodeAbiParameters,
|
|
19
26
|
encodeFunctionData,
|
|
20
27
|
getContract,
|
|
21
28
|
hexToBigInt,
|
|
@@ -29,11 +36,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
|
|
|
29
36
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
30
37
|
import type { ViemClient } from '../types.js';
|
|
31
38
|
import { formatViemError } from '../utils.js';
|
|
32
|
-
import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
|
|
33
39
|
import { GSEContract } from './gse.js';
|
|
34
40
|
import type { L1EventLog } from './log.js';
|
|
35
41
|
import { SlasherContract } from './slasher_contract.js';
|
|
36
|
-
import {
|
|
42
|
+
import { SlashingProposerContract } from './slashing_proposer.js';
|
|
37
43
|
import { checkBlockTag } from './utils.js';
|
|
38
44
|
|
|
39
45
|
export type ViemCommitteeAttestation = {
|
|
@@ -55,7 +61,6 @@ export type L1RollupContractAddresses = Pick<
|
|
|
55
61
|
| 'feeJuiceAddress'
|
|
56
62
|
| 'stakingAssetAddress'
|
|
57
63
|
| 'rewardDistributorAddress'
|
|
58
|
-
| 'slashFactoryAddress'
|
|
59
64
|
| 'gseAddress'
|
|
60
65
|
>;
|
|
61
66
|
|
|
@@ -85,12 +90,6 @@ export type ViemGasFees = {
|
|
|
85
90
|
feePerL2Gas: bigint;
|
|
86
91
|
};
|
|
87
92
|
|
|
88
|
-
export enum SlashingProposerType {
|
|
89
|
-
None = 0,
|
|
90
|
-
Tally = 1,
|
|
91
|
-
Empire = 2,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
93
|
/**
|
|
95
94
|
* Status of a validator/attester in the staking system.
|
|
96
95
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -134,6 +133,39 @@ export type L1FeeData = {
|
|
|
134
133
|
blobFee: bigint;
|
|
135
134
|
};
|
|
136
135
|
|
|
136
|
+
/** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */
|
|
137
|
+
export enum TempCheckpointLogField {
|
|
138
|
+
HeaderHash = 0,
|
|
139
|
+
BlobCommitmentsHash = 1,
|
|
140
|
+
OutHash = 2,
|
|
141
|
+
AttestationsHash = 3,
|
|
142
|
+
PayloadDigest = 4,
|
|
143
|
+
SlotNumber = 5,
|
|
144
|
+
FeeHeader = 6,
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Field-level override input for `tempCheckpointLogs[checkpointNumber]`. Covers the fields the
|
|
149
|
+
* `propose()` path actually reads back. `payloadDigest` is `Buffer32` because it carries an
|
|
150
|
+
* arbitrary `bytes32` value rather than a BN254 scalar. `slotNumber` carries the uint32 portion
|
|
151
|
+
* of the on-chain `CompressedSlot`.
|
|
152
|
+
*/
|
|
153
|
+
export type TempCheckpointLogOverrideFields = {
|
|
154
|
+
headerHash?: Fr;
|
|
155
|
+
outHash?: Fr;
|
|
156
|
+
payloadDigest?: Buffer32;
|
|
157
|
+
slotNumber?: SlotNumber;
|
|
158
|
+
feeHeader?: FeeHeader;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
162
|
+
export type ManaMinFeeComponents = {
|
|
163
|
+
sequencerCost: bigint;
|
|
164
|
+
proverCost: bigint;
|
|
165
|
+
congestionCost: bigint;
|
|
166
|
+
congestionMultiplier: bigint;
|
|
167
|
+
};
|
|
168
|
+
|
|
137
169
|
/**
|
|
138
170
|
* Reward configuration for the rollup
|
|
139
171
|
*/
|
|
@@ -193,17 +225,44 @@ export type CheckpointProposedArgs = {
|
|
|
193
225
|
checkpointNumber: CheckpointNumber;
|
|
194
226
|
archive: Fr;
|
|
195
227
|
versionedBlobHashes: Buffer[];
|
|
196
|
-
/** Hash of attestations
|
|
197
|
-
attestationsHash
|
|
198
|
-
/** Digest of the payload
|
|
199
|
-
payloadDigest
|
|
228
|
+
/** Hash of attestations emitted in the CheckpointProposed event. */
|
|
229
|
+
attestationsHash: Buffer32;
|
|
230
|
+
/** Digest of the payload emitted in the CheckpointProposed event. */
|
|
231
|
+
payloadDigest: Buffer32;
|
|
200
232
|
};
|
|
201
233
|
|
|
202
234
|
/** Log type for CheckpointProposed events. */
|
|
203
235
|
export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
|
|
204
236
|
|
|
237
|
+
const INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR = 'ValidatorSelection__InsufficientValidatorSetSize';
|
|
238
|
+
|
|
239
|
+
function isValidatorSelectionError(err: unknown, errorName: string): boolean {
|
|
240
|
+
return (
|
|
241
|
+
getErrorCause(err, ContractFunctionRevertedError)?.data?.errorName === errorName ||
|
|
242
|
+
decodeRpcRequestErrorName(err) === errorName
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function decodeRpcRequestErrorName(err: unknown): string | undefined {
|
|
247
|
+
const data = getErrorCause(err, RpcRequestError)?.data;
|
|
248
|
+
if (!isHexString(data)) {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
return decodeErrorResult({ abi: RollupAbi, data }).errorName;
|
|
254
|
+
} catch {
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function isHexString(value: unknown): value is Hex {
|
|
260
|
+
return typeof value === 'string' && value.startsWith('0x');
|
|
261
|
+
}
|
|
262
|
+
|
|
205
263
|
export class RollupContract {
|
|
206
264
|
private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
|
|
265
|
+
private readonly logger = createLogger('ethereum:rollup');
|
|
207
266
|
|
|
208
267
|
private static cachedStfStorageSlot: Hex | undefined;
|
|
209
268
|
private cachedEscapeHatch?: {
|
|
@@ -233,7 +292,7 @@ export class RollupContract {
|
|
|
233
292
|
|
|
234
293
|
static getFromConfig(config: L1ReaderConfig) {
|
|
235
294
|
const client = getPublicClient(config);
|
|
236
|
-
const address = config.
|
|
295
|
+
const address = config.rollupAddress.toString();
|
|
237
296
|
return new RollupContract(client, address);
|
|
238
297
|
}
|
|
239
298
|
|
|
@@ -259,34 +318,18 @@ export class RollupContract {
|
|
|
259
318
|
return this.rollup;
|
|
260
319
|
}
|
|
261
320
|
|
|
262
|
-
public async getSlashingProposer(): Promise<
|
|
263
|
-
EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
|
|
264
|
-
> {
|
|
321
|
+
public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
|
|
265
322
|
const slasher = await this.getSlasherContract();
|
|
266
323
|
if (!slasher) {
|
|
267
324
|
return undefined;
|
|
268
325
|
}
|
|
269
326
|
|
|
270
327
|
const proposerAddress = await slasher.getProposer();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
type: 'function',
|
|
274
|
-
name: 'SLASHING_PROPOSER_TYPE',
|
|
275
|
-
inputs: [],
|
|
276
|
-
outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
|
|
277
|
-
stateMutability: 'view',
|
|
278
|
-
},
|
|
279
|
-
] as const;
|
|
280
|
-
|
|
281
|
-
const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
|
|
282
|
-
const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
|
|
283
|
-
if (proposerType === SlashingProposerType.Tally.valueOf()) {
|
|
284
|
-
return new TallySlashingProposerContract(this.client, proposerAddress);
|
|
285
|
-
} else if (proposerType === SlashingProposerType.Empire.valueOf()) {
|
|
286
|
-
return new EmpireSlashingProposerContract(this.client, proposerAddress);
|
|
287
|
-
} else {
|
|
288
|
-
throw new Error(`Unknown slashing proposer type: ${proposerType}`);
|
|
328
|
+
if (proposerAddress.isZero()) {
|
|
329
|
+
return undefined;
|
|
289
330
|
}
|
|
331
|
+
|
|
332
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
290
333
|
}
|
|
291
334
|
|
|
292
335
|
@memoize
|
|
@@ -379,6 +422,20 @@ export class RollupContract {
|
|
|
379
422
|
return Fr.fromString(await this.rollup.read.archiveAt([0n]));
|
|
380
423
|
}
|
|
381
424
|
|
|
425
|
+
@memoize
|
|
426
|
+
async getVkTreeRoot(): Promise<Fr> {
|
|
427
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
|
|
428
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
429
|
+
return Fr.fromString(value ?? '0x0');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
@memoize
|
|
433
|
+
async getProtocolContractsHash(): Promise<Fr> {
|
|
434
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
|
|
435
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
436
|
+
return Fr.fromString(value ?? '0x0');
|
|
437
|
+
}
|
|
438
|
+
|
|
382
439
|
/**
|
|
383
440
|
* Returns rollup constants used for epoch queries.
|
|
384
441
|
* Return type is `L1RollupConstants` which is defined in stdlib,
|
|
@@ -391,13 +448,25 @@ export class RollupContract {
|
|
|
391
448
|
slotDuration: number;
|
|
392
449
|
epochDuration: number;
|
|
393
450
|
proofSubmissionEpochs: number;
|
|
451
|
+
targetCommitteeSize: number;
|
|
452
|
+
rollupManaLimit: number;
|
|
394
453
|
}> {
|
|
395
|
-
const [
|
|
454
|
+
const [
|
|
455
|
+
l1StartBlock,
|
|
456
|
+
l1GenesisTime,
|
|
457
|
+
slotDuration,
|
|
458
|
+
epochDuration,
|
|
459
|
+
proofSubmissionEpochs,
|
|
460
|
+
targetCommitteeSize,
|
|
461
|
+
rollupManaLimit,
|
|
462
|
+
] = await Promise.all([
|
|
396
463
|
this.getL1StartBlock(),
|
|
397
464
|
this.getL1GenesisTime(),
|
|
398
465
|
this.getSlotDuration(),
|
|
399
466
|
this.getEpochDuration(),
|
|
400
467
|
this.getProofSubmissionEpochs(),
|
|
468
|
+
this.getTargetCommitteeSize(),
|
|
469
|
+
this.getManaLimit(),
|
|
401
470
|
]);
|
|
402
471
|
return {
|
|
403
472
|
l1StartBlock,
|
|
@@ -405,6 +474,8 @@ export class RollupContract {
|
|
|
405
474
|
slotDuration,
|
|
406
475
|
epochDuration: Number(epochDuration),
|
|
407
476
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
477
|
+
targetCommitteeSize,
|
|
478
|
+
rollupManaLimit: Number(rollupManaLimit),
|
|
408
479
|
};
|
|
409
480
|
}
|
|
410
481
|
|
|
@@ -459,7 +530,11 @@ export class RollupContract {
|
|
|
459
530
|
|
|
460
531
|
const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
|
|
461
532
|
return isOpen;
|
|
462
|
-
} catch {
|
|
533
|
+
} catch (err) {
|
|
534
|
+
this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
|
|
535
|
+
epoch: Number(epoch),
|
|
536
|
+
error: err,
|
|
537
|
+
});
|
|
463
538
|
return false;
|
|
464
539
|
}
|
|
465
540
|
}
|
|
@@ -495,26 +570,41 @@ export class RollupContract {
|
|
|
495
570
|
return this.rollup.read.getCheckpointReward();
|
|
496
571
|
}
|
|
497
572
|
|
|
498
|
-
async getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
499
|
-
|
|
573
|
+
async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
574
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
575
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
|
|
500
576
|
}
|
|
501
577
|
|
|
502
|
-
async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
503
|
-
|
|
578
|
+
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
579
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
580
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
504
581
|
}
|
|
505
582
|
|
|
506
|
-
async getSlotNumber(): Promise<SlotNumber> {
|
|
507
|
-
|
|
583
|
+
async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
|
|
584
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
585
|
+
return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
|
|
508
586
|
}
|
|
509
587
|
|
|
510
|
-
async getL1FeesAt(timestamp: bigint): Promise<L1FeeData> {
|
|
511
|
-
|
|
588
|
+
async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
|
|
589
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
590
|
+
const result = await this.rollup.read.getL1FeesAt([timestamp], options);
|
|
512
591
|
return {
|
|
513
592
|
baseFee: result.baseFee,
|
|
514
593
|
blobFee: result.blobFee,
|
|
515
594
|
};
|
|
516
595
|
}
|
|
517
596
|
|
|
597
|
+
async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
|
|
598
|
+
const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
|
|
599
|
+
return {
|
|
600
|
+
excessMana: result.excessMana,
|
|
601
|
+
manaUsed: result.manaUsed,
|
|
602
|
+
ethPerFeeAsset: result.ethPerFeeAsset,
|
|
603
|
+
congestionCost: result.congestionCost,
|
|
604
|
+
proverCost: result.proverCost,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
518
608
|
getEthPerFeeAsset(): Promise<bigint> {
|
|
519
609
|
return this.rollup.read.getEthPerFeeAsset();
|
|
520
610
|
}
|
|
@@ -528,7 +618,7 @@ export class RollupContract {
|
|
|
528
618
|
args: [timestamp],
|
|
529
619
|
})
|
|
530
620
|
.catch(e => {
|
|
531
|
-
if (e
|
|
621
|
+
if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
|
|
532
622
|
return { result: undefined };
|
|
533
623
|
}
|
|
534
624
|
throw e;
|
|
@@ -558,7 +648,7 @@ export class RollupContract {
|
|
|
558
648
|
args: [],
|
|
559
649
|
})
|
|
560
650
|
.catch(e => {
|
|
561
|
-
if (e
|
|
651
|
+
if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
|
|
562
652
|
return { result: undefined };
|
|
563
653
|
}
|
|
564
654
|
throw e;
|
|
@@ -589,8 +679,9 @@ export class RollupContract {
|
|
|
589
679
|
return EthAddress.fromString(result);
|
|
590
680
|
}
|
|
591
681
|
|
|
592
|
-
async getCheckpoint(checkpointNumber: CheckpointNumber): Promise<CheckpointLog> {
|
|
593
|
-
|
|
682
|
+
async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
|
|
683
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
684
|
+
const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
|
|
594
685
|
return {
|
|
595
686
|
archive: Fr.fromString(result.archive),
|
|
596
687
|
headerHash: Buffer32.fromString(result.headerHash),
|
|
@@ -623,6 +714,30 @@ export class RollupContract {
|
|
|
623
714
|
);
|
|
624
715
|
}
|
|
625
716
|
|
|
717
|
+
/**
|
|
718
|
+
* Returns the effective pending checkpoint, accounting for potential prunes.
|
|
719
|
+
* When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
|
|
720
|
+
* This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
|
|
721
|
+
* @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
|
|
722
|
+
* @param options - Optional L1 block number to pin the queries to.
|
|
723
|
+
*/
|
|
724
|
+
getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
|
|
725
|
+
return retry(
|
|
726
|
+
async () => {
|
|
727
|
+
const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
|
|
728
|
+
const canPrune = await this.canPruneAtTime(timestamp, options);
|
|
729
|
+
if (canPrune) {
|
|
730
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
|
|
731
|
+
return await this.getCheckpoint(provenCheckpointNumber, options);
|
|
732
|
+
}
|
|
733
|
+
const pendingCheckpointNumber = await this.getCheckpointNumber(options);
|
|
734
|
+
return await this.getCheckpoint(pendingCheckpointNumber, options);
|
|
735
|
+
},
|
|
736
|
+
'getting effective pending checkpoint',
|
|
737
|
+
makeBackoff([0.5, 0.5, 0.5]),
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
|
|
626
741
|
async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
|
|
627
742
|
const { pending, proven } = await this.rollup.read.getTips();
|
|
628
743
|
return {
|
|
@@ -741,14 +856,13 @@ export class RollupContract {
|
|
|
741
856
|
* timestamp of the next L1 block
|
|
742
857
|
* @throws otherwise
|
|
743
858
|
*/
|
|
744
|
-
public async
|
|
859
|
+
public async canProposeAt(
|
|
745
860
|
archive: Buffer,
|
|
746
861
|
account: `0x${string}` | Account,
|
|
747
|
-
|
|
748
|
-
|
|
862
|
+
timestamp: bigint,
|
|
863
|
+
stateOverride: StateOverride = [],
|
|
749
864
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
750
|
-
const
|
|
751
|
-
const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
|
|
865
|
+
const timeOfNextL1Slot = timestamp;
|
|
752
866
|
const who = typeof account === 'string' ? account : account.address;
|
|
753
867
|
|
|
754
868
|
try {
|
|
@@ -760,7 +874,7 @@ export class RollupContract {
|
|
|
760
874
|
functionName: 'canProposeAtTime',
|
|
761
875
|
args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
|
|
762
876
|
account,
|
|
763
|
-
stateOverride
|
|
877
|
+
stateOverride,
|
|
764
878
|
});
|
|
765
879
|
|
|
766
880
|
return {
|
|
@@ -774,20 +888,32 @@ export class RollupContract {
|
|
|
774
888
|
}
|
|
775
889
|
|
|
776
890
|
/**
|
|
777
|
-
* Returns a state override that sets the pending
|
|
778
|
-
*
|
|
779
|
-
*
|
|
891
|
+
* Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
|
|
892
|
+
* Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
|
|
893
|
+
* a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
|
|
894
|
+
* value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
|
|
895
|
+
*
|
|
896
|
+
* Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
|
|
897
|
+
* `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
|
|
780
898
|
*/
|
|
781
|
-
public async
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
899
|
+
public async makeChainTipsOverride(override: {
|
|
900
|
+
pending?: CheckpointNumber;
|
|
901
|
+
proven?: CheckpointNumber;
|
|
902
|
+
}): Promise<StateOverride> {
|
|
903
|
+
if (override.pending === undefined && override.proven === undefined) {
|
|
785
904
|
return [];
|
|
786
905
|
}
|
|
787
906
|
const slot = RollupContract.stfStorageSlot;
|
|
788
907
|
const currentValue = await this.client.getStorageAt({ address: this.address, slot });
|
|
789
|
-
const
|
|
790
|
-
const
|
|
908
|
+
const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
|
|
909
|
+
const currentPending = currentRaw >> 128n;
|
|
910
|
+
const currentProven = currentRaw & ((1n << 128n) - 1n);
|
|
911
|
+
const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
|
|
912
|
+
const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
|
|
913
|
+
if (newProven > newPending) {
|
|
914
|
+
throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
|
|
915
|
+
}
|
|
916
|
+
const newValue = (newPending << 128n) | newProven;
|
|
791
917
|
return [
|
|
792
918
|
{
|
|
793
919
|
address: this.address,
|
|
@@ -796,6 +922,168 @@ export class RollupContract {
|
|
|
796
922
|
];
|
|
797
923
|
}
|
|
798
924
|
|
|
925
|
+
/**
|
|
926
|
+
* Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
|
|
927
|
+
* the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
|
|
928
|
+
* pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
|
|
929
|
+
*
|
|
930
|
+
* `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
|
|
931
|
+
* never asserts against them, so leaving them at storage zero is harmless.
|
|
932
|
+
*/
|
|
933
|
+
public async makeTempCheckpointLogOverride(
|
|
934
|
+
checkpointNumber: CheckpointNumber,
|
|
935
|
+
fields: TempCheckpointLogOverrideFields,
|
|
936
|
+
): Promise<StateOverride> {
|
|
937
|
+
const constants = await this.getRollupConstants();
|
|
938
|
+
const slotAt = (field: TempCheckpointLogField) =>
|
|
939
|
+
`0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}` as const;
|
|
940
|
+
const word = (v: bigint) => `0x${v.toString(16).padStart(64, '0')}` as const;
|
|
941
|
+
|
|
942
|
+
const stateDiff: { slot: `0x${string}`; value: `0x${string}` }[] = [];
|
|
943
|
+
|
|
944
|
+
if (fields.headerHash) {
|
|
945
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.HeaderHash), value: fields.headerHash.toString() });
|
|
946
|
+
}
|
|
947
|
+
if (fields.outHash) {
|
|
948
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.OutHash), value: fields.outHash.toString() });
|
|
949
|
+
}
|
|
950
|
+
if (fields.payloadDigest) {
|
|
951
|
+
stateDiff.push({
|
|
952
|
+
slot: slotAt(TempCheckpointLogField.PayloadDigest),
|
|
953
|
+
value: fields.payloadDigest.toString() as `0x${string}`,
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
if (fields.slotNumber !== undefined) {
|
|
957
|
+
// CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here
|
|
958
|
+
// so a malformed override surfaces immediately rather than silently truncating into a wrong slot.
|
|
959
|
+
const slotNumber = BigInt(fields.slotNumber);
|
|
960
|
+
if (slotNumber < 0n || slotNumber > 0xffffffffn) {
|
|
961
|
+
throw new Error(`slotNumber ${slotNumber} does not fit in uint32`);
|
|
962
|
+
}
|
|
963
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.SlotNumber), value: word(slotNumber) });
|
|
964
|
+
}
|
|
965
|
+
if (fields.feeHeader) {
|
|
966
|
+
stateDiff.push({
|
|
967
|
+
slot: slotAt(TempCheckpointLogField.FeeHeader),
|
|
968
|
+
value: word(RollupContract.compressFeeHeader(fields.feeHeader)),
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
if (stateDiff.length === 0) {
|
|
973
|
+
return [];
|
|
974
|
+
}
|
|
975
|
+
return [{ address: this.address, stateDiff }];
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Returns a state override that sets archives[checkpointNumber] to the given archive value.
|
|
980
|
+
* Used when simulating a canProposeAtTime call where the local archive differs from L1
|
|
981
|
+
* (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
|
|
982
|
+
*/
|
|
983
|
+
public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
|
|
984
|
+
const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
|
|
985
|
+
const archiveSlot = hexToBigInt(
|
|
986
|
+
keccak256(
|
|
987
|
+
encodeAbiParameters(
|
|
988
|
+
[{ type: 'uint256' }, { type: 'uint256' }],
|
|
989
|
+
[BigInt(checkpointNumber), archivesMappingBase],
|
|
990
|
+
),
|
|
991
|
+
),
|
|
992
|
+
);
|
|
993
|
+
return [
|
|
994
|
+
{
|
|
995
|
+
address: this.address,
|
|
996
|
+
stateDiff: [
|
|
997
|
+
{
|
|
998
|
+
slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
|
|
999
|
+
value: archive.toString(),
|
|
1000
|
+
},
|
|
1001
|
+
],
|
|
1002
|
+
},
|
|
1003
|
+
];
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
|
|
1007
|
+
public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
|
|
1008
|
+
type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
|
|
1009
|
+
const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
|
|
1010
|
+
for (const override of overrides) {
|
|
1011
|
+
for (const entry of override) {
|
|
1012
|
+
const key = entry.address.toLowerCase();
|
|
1013
|
+
const existing = byAddress.get(key);
|
|
1014
|
+
if (existing) {
|
|
1015
|
+
existing.stateDiff.push(...(entry.stateDiff ?? []));
|
|
1016
|
+
if (entry.balance !== undefined) {
|
|
1017
|
+
existing.balance = entry.balance;
|
|
1018
|
+
}
|
|
1019
|
+
} else {
|
|
1020
|
+
byAddress.set(key, {
|
|
1021
|
+
address: entry.address,
|
|
1022
|
+
balance: entry.balance,
|
|
1023
|
+
stateDiff: [...(entry.stateDiff ?? [])],
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
return [...byAddress.values()];
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
|
|
1032
|
+
public static compressFeeHeader(feeHeader: FeeHeader): bigint {
|
|
1033
|
+
const MASK_48_BITS = (1n << 48n) - 1n;
|
|
1034
|
+
const MASK_64_BITS = (1n << 64n) - 1n;
|
|
1035
|
+
const MASK_63_BITS = (1n << 63n) - 1n;
|
|
1036
|
+
|
|
1037
|
+
let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
|
|
1038
|
+
value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
|
|
1039
|
+
value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
|
|
1040
|
+
value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
|
|
1041
|
+
value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
|
|
1042
|
+
value |= 1n << 255n; // preheat flag
|
|
1043
|
+
return value;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/** Computes the fee header for a child checkpoint given parent fee header and child data.
|
|
1047
|
+
* Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
|
|
1048
|
+
public static computeChildFeeHeader(
|
|
1049
|
+
parentFeeHeader: FeeHeader,
|
|
1050
|
+
childManaUsed: bigint,
|
|
1051
|
+
feeAssetPriceModifier: bigint,
|
|
1052
|
+
manaTarget: bigint,
|
|
1053
|
+
): FeeHeader {
|
|
1054
|
+
const MIN_ETH_PER_FEE_ASSET = 100n;
|
|
1055
|
+
const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
|
|
1056
|
+
|
|
1057
|
+
// excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
|
|
1058
|
+
const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
|
|
1059
|
+
const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
|
|
1060
|
+
|
|
1061
|
+
// ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
|
|
1062
|
+
const parentPrice =
|
|
1063
|
+
parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
|
|
1064
|
+
let newPrice: bigint;
|
|
1065
|
+
if (feeAssetPriceModifier >= 0n) {
|
|
1066
|
+
newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
|
|
1067
|
+
} else {
|
|
1068
|
+
const absMod = -feeAssetPriceModifier;
|
|
1069
|
+
newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
|
|
1070
|
+
}
|
|
1071
|
+
if (newPrice < MIN_ETH_PER_FEE_ASSET) {
|
|
1072
|
+
newPrice = MIN_ETH_PER_FEE_ASSET;
|
|
1073
|
+
}
|
|
1074
|
+
if (newPrice > MAX_ETH_PER_FEE_ASSET) {
|
|
1075
|
+
newPrice = MAX_ETH_PER_FEE_ASSET;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
return {
|
|
1079
|
+
excessMana,
|
|
1080
|
+
manaUsed: childManaUsed,
|
|
1081
|
+
ethPerFeeAsset: newPrice,
|
|
1082
|
+
congestionCost: 0n,
|
|
1083
|
+
proverCost: 0n,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
|
|
799
1087
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
|
|
800
1088
|
public buildInvalidateBadAttestationRequest(
|
|
801
1089
|
checkpointNumber: CheckpointNumber,
|
|
@@ -805,6 +1093,7 @@ export class RollupContract {
|
|
|
805
1093
|
): L1TxRequest {
|
|
806
1094
|
return {
|
|
807
1095
|
to: this.address,
|
|
1096
|
+
abi: RollupAbi,
|
|
808
1097
|
data: encodeFunctionData({
|
|
809
1098
|
abi: RollupAbi,
|
|
810
1099
|
functionName: 'invalidateBadAttestation',
|
|
@@ -826,6 +1115,7 @@ export class RollupContract {
|
|
|
826
1115
|
): L1TxRequest {
|
|
827
1116
|
return {
|
|
828
1117
|
to: this.address,
|
|
1118
|
+
abi: RollupAbi,
|
|
829
1119
|
data: encodeFunctionData({
|
|
830
1120
|
abi: RollupAbi,
|
|
831
1121
|
functionName: 'invalidateInsufficientAttestations',
|
|
@@ -842,8 +1132,18 @@ export class RollupContract {
|
|
|
842
1132
|
return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
|
|
843
1133
|
}
|
|
844
1134
|
|
|
845
|
-
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
|
|
846
|
-
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
|
|
1135
|
+
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
|
|
1136
|
+
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
1140
|
+
const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
|
|
1141
|
+
return {
|
|
1142
|
+
sequencerCost: result.sequencerCost,
|
|
1143
|
+
proverCost: result.proverCost,
|
|
1144
|
+
congestionCost: result.congestionCost,
|
|
1145
|
+
congestionMultiplier: result.congestionMultiplier,
|
|
1146
|
+
};
|
|
847
1147
|
}
|
|
848
1148
|
|
|
849
1149
|
async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
|
|
@@ -889,11 +1189,10 @@ export class RollupContract {
|
|
|
889
1189
|
return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
|
|
890
1190
|
}
|
|
891
1191
|
|
|
892
|
-
async getAttesters(): Promise<EthAddress[]> {
|
|
1192
|
+
async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
|
|
893
1193
|
const attesterSize = await this.getActiveAttesterCount();
|
|
894
1194
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
895
|
-
const ts = (await this.client.getBlock()).timestamp;
|
|
896
|
-
|
|
1195
|
+
const ts = timestamp ?? (await this.client.getBlock()).timestamp;
|
|
897
1196
|
const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
|
|
898
1197
|
const chunks = chunk(indices, 1000);
|
|
899
1198
|
|
|
@@ -959,6 +1258,7 @@ export class RollupContract {
|
|
|
959
1258
|
setupEpoch(l1TxUtils: L1TxUtils) {
|
|
960
1259
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
961
1260
|
to: this.address,
|
|
1261
|
+
abi: RollupAbi,
|
|
962
1262
|
data: encodeFunctionData({
|
|
963
1263
|
abi: RollupAbi,
|
|
964
1264
|
functionName: 'setupEpoch',
|
|
@@ -970,6 +1270,7 @@ export class RollupContract {
|
|
|
970
1270
|
vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
|
|
971
1271
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
972
1272
|
to: this.address,
|
|
1273
|
+
abi: RollupAbi,
|
|
973
1274
|
data: encodeFunctionData({
|
|
974
1275
|
abi: RollupAbi,
|
|
975
1276
|
functionName: 'vote',
|
|
@@ -997,7 +1298,7 @@ export class RollupContract {
|
|
|
997
1298
|
}
|
|
998
1299
|
|
|
999
1300
|
public listenToCheckpointInvalidated(
|
|
1000
|
-
callback: (args: { checkpointNumber: CheckpointNumber }) => unknown,
|
|
1301
|
+
callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
|
|
1001
1302
|
): WatchContractEventReturnType {
|
|
1002
1303
|
return this.rollup.watchEvent.CheckpointInvalidated(
|
|
1003
1304
|
{},
|
|
@@ -1006,7 +1307,7 @@ export class RollupContract {
|
|
|
1006
1307
|
for (const log of logs) {
|
|
1007
1308
|
const args = log.args;
|
|
1008
1309
|
if (args.checkpointNumber !== undefined) {
|
|
1009
|
-
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber) });
|
|
1310
|
+
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
|
|
1010
1311
|
}
|
|
1011
1312
|
}
|
|
1012
1313
|
},
|
|
@@ -1039,6 +1340,17 @@ export class RollupContract {
|
|
|
1039
1340
|
);
|
|
1040
1341
|
}
|
|
1041
1342
|
|
|
1343
|
+
/**
|
|
1344
|
+
* Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
|
|
1345
|
+
* The Rollup inherits from Ownable and emits this event in its constructor, so the event
|
|
1346
|
+
* is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
|
|
1347
|
+
* probe to detect RPC nodes that prune historical logs.
|
|
1348
|
+
*/
|
|
1349
|
+
async getOwnershipTransferredEventsAtDeploy() {
|
|
1350
|
+
const l1StartBlock = await this.getL1StartBlock();
|
|
1351
|
+
return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1042
1354
|
/** Fetches CheckpointProposed events within the given block range. */
|
|
1043
1355
|
async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
|
|
1044
1356
|
const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
|
|
@@ -1052,9 +1364,67 @@ export class RollupContract {
|
|
|
1052
1364
|
checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
|
|
1053
1365
|
archive: Fr.fromString(log.args.archive!),
|
|
1054
1366
|
versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
|
|
1055
|
-
attestationsHash:
|
|
1056
|
-
|
|
1367
|
+
attestationsHash: (() => {
|
|
1368
|
+
if (!log.args.attestationsHash) {
|
|
1369
|
+
throw new Error(
|
|
1370
|
+
`CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
return Buffer32.fromString(log.args.attestationsHash);
|
|
1374
|
+
})(),
|
|
1375
|
+
payloadDigest: (() => {
|
|
1376
|
+
if (!log.args.payloadDigest) {
|
|
1377
|
+
throw new Error(
|
|
1378
|
+
`CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
return Buffer32.fromString(log.args.payloadDigest);
|
|
1382
|
+
})(),
|
|
1057
1383
|
},
|
|
1058
1384
|
}));
|
|
1059
1385
|
}
|
|
1386
|
+
|
|
1387
|
+
/** Packs pending and proven checkpoint numbers into the chain tips storage format. */
|
|
1388
|
+
static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
|
|
1389
|
+
return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
/** Storage slot for the chain tips (offset 0 within the STF storage struct). */
|
|
1393
|
+
static get chainTipsStorageSlot(): bigint {
|
|
1394
|
+
return BigInt(RollupContract.stfStorageSlot);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* Computes the storage slot for a field within a tempCheckpointLog entry.
|
|
1399
|
+
* @param checkpointNumber - The checkpoint number
|
|
1400
|
+
* @param field - The field within the CompressedTempCheckpointLog struct
|
|
1401
|
+
*/
|
|
1402
|
+
async getTempCheckpointLogStorageSlot(
|
|
1403
|
+
checkpointNumber: CheckpointNumber,
|
|
1404
|
+
field: TempCheckpointLogField,
|
|
1405
|
+
): Promise<bigint> {
|
|
1406
|
+
const [epochDuration, proofSubmissionEpochs] = await Promise.all([
|
|
1407
|
+
this.getEpochDuration(),
|
|
1408
|
+
this.getProofSubmissionEpochs(),
|
|
1409
|
+
]);
|
|
1410
|
+
return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, { epochDuration, proofSubmissionEpochs });
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
private computeTempCheckpointLogStorageSlot(
|
|
1414
|
+
checkpointNumber: CheckpointNumber,
|
|
1415
|
+
field: TempCheckpointLogField,
|
|
1416
|
+
constants: { epochDuration: number; proofSubmissionEpochs: number },
|
|
1417
|
+
): bigint {
|
|
1418
|
+
const fieldOffset = BigInt(field);
|
|
1419
|
+
const { epochDuration, proofSubmissionEpochs } = constants;
|
|
1420
|
+
const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
|
|
1421
|
+
const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
|
|
1422
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
1423
|
+
const entryBase = BigInt(
|
|
1424
|
+
keccak256(
|
|
1425
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
|
|
1426
|
+
),
|
|
1427
|
+
);
|
|
1428
|
+
return entryBase + fieldOffset;
|
|
1429
|
+
}
|
|
1060
1430
|
}
|