@aztec/ethereum 0.0.1-commit.c31f2472 → 0.0.1-commit.c52d6e7
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 +29 -8
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +69 -14
- 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 +135 -35
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +231 -18
- package/dest/contracts/governance_proposer.d.ts +33 -2
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +58 -7
- package/dest/contracts/gse.d.ts +8 -2
- package/dest/contracts/gse.d.ts.map +1 -1
- package/dest/contracts/gse.js +11 -2
- 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 +130 -13
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +203 -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 +5428 -31
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +414 -75
- package/dest/contracts/slashing_proposer.d.ts +170 -0
- package/dest/contracts/slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/slashing_proposer.js +748 -0
- package/dest/deploy_aztec_l1_contracts.d.ts +19 -12
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +65 -39
- package/dest/deploy_l1_contract.js +3 -3
- package/dest/foundry_binary.d.ts +13 -0
- package/dest/foundry_binary.d.ts.map +1 -0
- package/dest/foundry_binary.js +52 -0
- package/dest/l1_artifacts.d.ts +15526 -15844
- 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 +17 -4
- 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_fee_analyzer.d.ts +23 -4
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_fee_analyzer.js +65 -3
- 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 +185 -88
- 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/l1_tx_utils/types.d.ts +27 -1
- package/dest/l1_tx_utils/types.d.ts.map +1 -1
- package/dest/l1_tx_utils/types.js +10 -0
- package/dest/publisher_manager.d.ts +34 -8
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +119 -8
- package/dest/queries.d.ts +13 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +69 -4
- package/dest/test/blob_kzg_warmup.d.ts +19 -0
- package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
- package/dest/test/blob_kzg_warmup.js +64 -0
- package/dest/test/chain_monitor.d.ts +55 -4
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +104 -3
- package/dest/test/eth_cheat_codes.d.ts +37 -6
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +89 -41
- package/dest/test/index.d.ts +2 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -2
- package/dest/test/rollup_cheat_codes.d.ts +60 -3
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +86 -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 +182 -32
- 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 -9
- package/src/client.ts +66 -2
- package/src/config.ts +97 -18
- 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 +315 -13
- package/src/contracts/governance_proposer.ts +62 -10
- package/src/contracts/gse.ts +19 -2
- package/src/contracts/inbox.ts +13 -5
- package/src/contracts/index.ts +3 -2
- package/src/contracts/multicall.ts +247 -103
- package/src/contracts/outbox.ts +42 -8
- package/src/contracts/registry.ts +31 -1
- package/src/contracts/rollup.ts +467 -78
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +93 -28
- package/src/deploy_aztec_l1_contracts.ts +92 -47
- package/src/deploy_l1_contract.ts +3 -3
- package/src/foundry_binary.ts +57 -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 +23 -3
- 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_fee_analyzer.ts +75 -3
- package/src/l1_tx_utils/l1_tx_utils.ts +170 -68
- 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/l1_tx_utils/types.ts +32 -0
- package/src/publisher_manager.ts +143 -12
- package/src/queries.ts +76 -4
- package/src/test/blob_kzg_warmup.ts +65 -0
- package/src/test/chain_monitor.ts +148 -3
- package/src/test/eth_cheat_codes.ts +82 -45
- package/src/test/index.ts +1 -2
- package/src/test/rollup_cheat_codes.ts +115 -6
- package/src/test/start_anvil.ts +212 -32
- 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 +0 -140
- package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/tally_slashing_proposer.js +0 -313
- package/dest/generated/l1-contracts-defaults.d.ts +0 -30
- package/dest/generated/l1-contracts-defaults.d.ts.map +0 -1
- package/dest/generated/l1-contracts-defaults.js +0 -30
- 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/generated/l1-contracts-defaults.ts +0 -32
- 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
|
|
|
@@ -78,6 +83,7 @@ export type ViemHeader = {
|
|
|
78
83
|
feeRecipient: `0x${string}`;
|
|
79
84
|
gasFees: ViemGasFees;
|
|
80
85
|
totalManaUsed: bigint;
|
|
86
|
+
accumulatedFees: bigint;
|
|
81
87
|
};
|
|
82
88
|
|
|
83
89
|
export type ViemGasFees = {
|
|
@@ -85,12 +91,6 @@ export type ViemGasFees = {
|
|
|
85
91
|
feePerL2Gas: bigint;
|
|
86
92
|
};
|
|
87
93
|
|
|
88
|
-
export enum SlashingProposerType {
|
|
89
|
-
None = 0,
|
|
90
|
-
Tally = 1,
|
|
91
|
-
Empire = 2,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
94
|
/**
|
|
95
95
|
* Status of a validator/attester in the staking system.
|
|
96
96
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -134,6 +134,39 @@ export type L1FeeData = {
|
|
|
134
134
|
blobFee: bigint;
|
|
135
135
|
};
|
|
136
136
|
|
|
137
|
+
/** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */
|
|
138
|
+
export enum TempCheckpointLogField {
|
|
139
|
+
HeaderHash = 0,
|
|
140
|
+
BlobCommitmentsHash = 1,
|
|
141
|
+
OutHash = 2,
|
|
142
|
+
AttestationsHash = 3,
|
|
143
|
+
PayloadDigest = 4,
|
|
144
|
+
SlotNumber = 5,
|
|
145
|
+
FeeHeader = 6,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Field-level override input for `tempCheckpointLogs[checkpointNumber]`. Covers the fields the
|
|
150
|
+
* `propose()` path actually reads back. `payloadDigest` is `Buffer32` because it carries an
|
|
151
|
+
* arbitrary `bytes32` value rather than a BN254 scalar. `slotNumber` carries the uint32 portion
|
|
152
|
+
* of the on-chain `CompressedSlot`.
|
|
153
|
+
*/
|
|
154
|
+
export type TempCheckpointLogOverrideFields = {
|
|
155
|
+
headerHash?: Fr;
|
|
156
|
+
outHash?: Fr;
|
|
157
|
+
payloadDigest?: Buffer32;
|
|
158
|
+
slotNumber?: SlotNumber;
|
|
159
|
+
feeHeader?: FeeHeader;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
163
|
+
export type ManaMinFeeComponents = {
|
|
164
|
+
sequencerCost: bigint;
|
|
165
|
+
proverCost: bigint;
|
|
166
|
+
congestionCost: bigint;
|
|
167
|
+
congestionMultiplier: bigint;
|
|
168
|
+
};
|
|
169
|
+
|
|
137
170
|
/**
|
|
138
171
|
* Reward configuration for the rollup
|
|
139
172
|
*/
|
|
@@ -193,17 +226,44 @@ export type CheckpointProposedArgs = {
|
|
|
193
226
|
checkpointNumber: CheckpointNumber;
|
|
194
227
|
archive: Fr;
|
|
195
228
|
versionedBlobHashes: Buffer[];
|
|
196
|
-
/** Hash of attestations
|
|
197
|
-
attestationsHash
|
|
198
|
-
/** Digest of the payload
|
|
199
|
-
payloadDigest
|
|
229
|
+
/** Hash of attestations emitted in the CheckpointProposed event. */
|
|
230
|
+
attestationsHash: Buffer32;
|
|
231
|
+
/** Digest of the payload emitted in the CheckpointProposed event. */
|
|
232
|
+
payloadDigest: Buffer32;
|
|
200
233
|
};
|
|
201
234
|
|
|
202
235
|
/** Log type for CheckpointProposed events. */
|
|
203
236
|
export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
|
|
204
237
|
|
|
238
|
+
const INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR = 'ValidatorSelection__InsufficientValidatorSetSize';
|
|
239
|
+
|
|
240
|
+
function isValidatorSelectionError(err: unknown, errorName: string): boolean {
|
|
241
|
+
return (
|
|
242
|
+
getErrorCause(err, ContractFunctionRevertedError)?.data?.errorName === errorName ||
|
|
243
|
+
decodeRpcRequestErrorName(err) === errorName
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function decodeRpcRequestErrorName(err: unknown): string | undefined {
|
|
248
|
+
const data = getErrorCause(err, RpcRequestError)?.data;
|
|
249
|
+
if (!isHexString(data)) {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
return decodeErrorResult({ abi: RollupAbi, data }).errorName;
|
|
255
|
+
} catch {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function isHexString(value: unknown): value is Hex {
|
|
261
|
+
return typeof value === 'string' && value.startsWith('0x');
|
|
262
|
+
}
|
|
263
|
+
|
|
205
264
|
export class RollupContract {
|
|
206
265
|
private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
|
|
266
|
+
private readonly logger = createLogger('ethereum:rollup');
|
|
207
267
|
|
|
208
268
|
private static cachedStfStorageSlot: Hex | undefined;
|
|
209
269
|
private cachedEscapeHatch?: {
|
|
@@ -233,7 +293,7 @@ export class RollupContract {
|
|
|
233
293
|
|
|
234
294
|
static getFromConfig(config: L1ReaderConfig) {
|
|
235
295
|
const client = getPublicClient(config);
|
|
236
|
-
const address = config.
|
|
296
|
+
const address = config.rollupAddress.toString();
|
|
237
297
|
return new RollupContract(client, address);
|
|
238
298
|
}
|
|
239
299
|
|
|
@@ -259,34 +319,18 @@ export class RollupContract {
|
|
|
259
319
|
return this.rollup;
|
|
260
320
|
}
|
|
261
321
|
|
|
262
|
-
public async getSlashingProposer(): Promise<
|
|
263
|
-
EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
|
|
264
|
-
> {
|
|
322
|
+
public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
|
|
265
323
|
const slasher = await this.getSlasherContract();
|
|
266
324
|
if (!slasher) {
|
|
267
325
|
return undefined;
|
|
268
326
|
}
|
|
269
327
|
|
|
270
328
|
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}`);
|
|
329
|
+
if (proposerAddress.isZero()) {
|
|
330
|
+
return undefined;
|
|
289
331
|
}
|
|
332
|
+
|
|
333
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
290
334
|
}
|
|
291
335
|
|
|
292
336
|
@memoize
|
|
@@ -379,6 +423,20 @@ export class RollupContract {
|
|
|
379
423
|
return Fr.fromString(await this.rollup.read.archiveAt([0n]));
|
|
380
424
|
}
|
|
381
425
|
|
|
426
|
+
@memoize
|
|
427
|
+
async getVkTreeRoot(): Promise<Fr> {
|
|
428
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
|
|
429
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
430
|
+
return Fr.fromString(value ?? '0x0');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
@memoize
|
|
434
|
+
async getProtocolContractsHash(): Promise<Fr> {
|
|
435
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
|
|
436
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
437
|
+
return Fr.fromString(value ?? '0x0');
|
|
438
|
+
}
|
|
439
|
+
|
|
382
440
|
/**
|
|
383
441
|
* Returns rollup constants used for epoch queries.
|
|
384
442
|
* Return type is `L1RollupConstants` which is defined in stdlib,
|
|
@@ -391,13 +449,25 @@ export class RollupContract {
|
|
|
391
449
|
slotDuration: number;
|
|
392
450
|
epochDuration: number;
|
|
393
451
|
proofSubmissionEpochs: number;
|
|
452
|
+
targetCommitteeSize: number;
|
|
453
|
+
rollupManaLimit: number;
|
|
394
454
|
}> {
|
|
395
|
-
const [
|
|
455
|
+
const [
|
|
456
|
+
l1StartBlock,
|
|
457
|
+
l1GenesisTime,
|
|
458
|
+
slotDuration,
|
|
459
|
+
epochDuration,
|
|
460
|
+
proofSubmissionEpochs,
|
|
461
|
+
targetCommitteeSize,
|
|
462
|
+
rollupManaLimit,
|
|
463
|
+
] = await Promise.all([
|
|
396
464
|
this.getL1StartBlock(),
|
|
397
465
|
this.getL1GenesisTime(),
|
|
398
466
|
this.getSlotDuration(),
|
|
399
467
|
this.getEpochDuration(),
|
|
400
468
|
this.getProofSubmissionEpochs(),
|
|
469
|
+
this.getTargetCommitteeSize(),
|
|
470
|
+
this.getManaLimit(),
|
|
401
471
|
]);
|
|
402
472
|
return {
|
|
403
473
|
l1StartBlock,
|
|
@@ -405,6 +475,8 @@ export class RollupContract {
|
|
|
405
475
|
slotDuration,
|
|
406
476
|
epochDuration: Number(epochDuration),
|
|
407
477
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
478
|
+
targetCommitteeSize,
|
|
479
|
+
rollupManaLimit: Number(rollupManaLimit),
|
|
408
480
|
};
|
|
409
481
|
}
|
|
410
482
|
|
|
@@ -459,7 +531,11 @@ export class RollupContract {
|
|
|
459
531
|
|
|
460
532
|
const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
|
|
461
533
|
return isOpen;
|
|
462
|
-
} catch {
|
|
534
|
+
} catch (err) {
|
|
535
|
+
this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
|
|
536
|
+
epoch: Number(epoch),
|
|
537
|
+
error: err,
|
|
538
|
+
});
|
|
463
539
|
return false;
|
|
464
540
|
}
|
|
465
541
|
}
|
|
@@ -479,8 +555,19 @@ export class RollupContract {
|
|
|
479
555
|
return EthAddress.fromString(await this.rollup.read.owner());
|
|
480
556
|
}
|
|
481
557
|
|
|
482
|
-
async getActiveAttesterCount(): Promise<number> {
|
|
483
|
-
|
|
558
|
+
async getActiveAttesterCount(options?: { blockNumber?: bigint }): Promise<number> {
|
|
559
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
560
|
+
return Number(await this.rollup.read.getActiveAttesterCount(options));
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Number of attesters that were staked at the given (historical) L1 timestamp. This is the count that
|
|
565
|
+
* validator-set sampling uses to decide whether an epoch gets a committee, and the historical counterpart
|
|
566
|
+
* of {@link getActiveAttesterCount} (which reads at the latest L1 block).
|
|
567
|
+
*/
|
|
568
|
+
async getAttesterCountAtTime(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<number> {
|
|
569
|
+
const gse = new GSEContract(this.client, await this.getGSE());
|
|
570
|
+
return gse.getAttesterCountAtTime(this.address, timestamp, options);
|
|
484
571
|
}
|
|
485
572
|
|
|
486
573
|
public async getSlashingProposerAddress() {
|
|
@@ -495,26 +582,41 @@ export class RollupContract {
|
|
|
495
582
|
return this.rollup.read.getCheckpointReward();
|
|
496
583
|
}
|
|
497
584
|
|
|
498
|
-
async getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
499
|
-
|
|
585
|
+
async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
586
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
587
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
|
|
500
588
|
}
|
|
501
589
|
|
|
502
|
-
async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
503
|
-
|
|
590
|
+
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
591
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
592
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
504
593
|
}
|
|
505
594
|
|
|
506
|
-
async getSlotNumber(): Promise<SlotNumber> {
|
|
507
|
-
|
|
595
|
+
async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
|
|
596
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
597
|
+
return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
|
|
508
598
|
}
|
|
509
599
|
|
|
510
|
-
async getL1FeesAt(timestamp: bigint): Promise<L1FeeData> {
|
|
511
|
-
|
|
600
|
+
async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
|
|
601
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
602
|
+
const result = await this.rollup.read.getL1FeesAt([timestamp], options);
|
|
512
603
|
return {
|
|
513
604
|
baseFee: result.baseFee,
|
|
514
605
|
blobFee: result.blobFee,
|
|
515
606
|
};
|
|
516
607
|
}
|
|
517
608
|
|
|
609
|
+
async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
|
|
610
|
+
const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
|
|
611
|
+
return {
|
|
612
|
+
excessMana: result.excessMana,
|
|
613
|
+
manaUsed: result.manaUsed,
|
|
614
|
+
ethPerFeeAsset: result.ethPerFeeAsset,
|
|
615
|
+
congestionCost: result.congestionCost,
|
|
616
|
+
proverCost: result.proverCost,
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
518
620
|
getEthPerFeeAsset(): Promise<bigint> {
|
|
519
621
|
return this.rollup.read.getEthPerFeeAsset();
|
|
520
622
|
}
|
|
@@ -528,7 +630,7 @@ export class RollupContract {
|
|
|
528
630
|
args: [timestamp],
|
|
529
631
|
})
|
|
530
632
|
.catch(e => {
|
|
531
|
-
if (e
|
|
633
|
+
if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
|
|
532
634
|
return { result: undefined };
|
|
533
635
|
}
|
|
534
636
|
throw e;
|
|
@@ -558,7 +660,7 @@ export class RollupContract {
|
|
|
558
660
|
args: [],
|
|
559
661
|
})
|
|
560
662
|
.catch(e => {
|
|
561
|
-
if (e
|
|
663
|
+
if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
|
|
562
664
|
return { result: undefined };
|
|
563
665
|
}
|
|
564
666
|
throw e;
|
|
@@ -589,8 +691,9 @@ export class RollupContract {
|
|
|
589
691
|
return EthAddress.fromString(result);
|
|
590
692
|
}
|
|
591
693
|
|
|
592
|
-
async getCheckpoint(checkpointNumber: CheckpointNumber): Promise<CheckpointLog> {
|
|
593
|
-
|
|
694
|
+
async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
|
|
695
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
696
|
+
const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
|
|
594
697
|
return {
|
|
595
698
|
archive: Fr.fromString(result.archive),
|
|
596
699
|
headerHash: Buffer32.fromString(result.headerHash),
|
|
@@ -623,6 +726,30 @@ export class RollupContract {
|
|
|
623
726
|
);
|
|
624
727
|
}
|
|
625
728
|
|
|
729
|
+
/**
|
|
730
|
+
* Returns the effective pending checkpoint, accounting for potential prunes.
|
|
731
|
+
* When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
|
|
732
|
+
* This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
|
|
733
|
+
* @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
|
|
734
|
+
* @param options - Optional L1 block number to pin the queries to.
|
|
735
|
+
*/
|
|
736
|
+
getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
|
|
737
|
+
return retry(
|
|
738
|
+
async () => {
|
|
739
|
+
const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
|
|
740
|
+
const canPrune = await this.canPruneAtTime(timestamp, options);
|
|
741
|
+
if (canPrune) {
|
|
742
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
|
|
743
|
+
return await this.getCheckpoint(provenCheckpointNumber, options);
|
|
744
|
+
}
|
|
745
|
+
const pendingCheckpointNumber = await this.getCheckpointNumber(options);
|
|
746
|
+
return await this.getCheckpoint(pendingCheckpointNumber, options);
|
|
747
|
+
},
|
|
748
|
+
'getting effective pending checkpoint',
|
|
749
|
+
makeBackoff([0.5, 0.5, 0.5]),
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
|
|
626
753
|
async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
|
|
627
754
|
const { pending, proven } = await this.rollup.read.getTips();
|
|
628
755
|
return {
|
|
@@ -697,7 +824,7 @@ export class RollupContract {
|
|
|
697
824
|
}
|
|
698
825
|
|
|
699
826
|
async getEpochProofPublicInputs(
|
|
700
|
-
args: readonly [bigint, bigint, EpochProofPublicInputArgs, readonly
|
|
827
|
+
args: readonly [bigint, bigint, EpochProofPublicInputArgs, readonly ViemHeader[], `0x${string}`],
|
|
701
828
|
): Promise<Fr[]> {
|
|
702
829
|
const result = await this.rollup.read.getEpochProofPublicInputs(args);
|
|
703
830
|
return result.map(Fr.fromString);
|
|
@@ -741,14 +868,13 @@ export class RollupContract {
|
|
|
741
868
|
* timestamp of the next L1 block
|
|
742
869
|
* @throws otherwise
|
|
743
870
|
*/
|
|
744
|
-
public async
|
|
871
|
+
public async canProposeAt(
|
|
745
872
|
archive: Buffer,
|
|
746
873
|
account: `0x${string}` | Account,
|
|
747
|
-
|
|
748
|
-
|
|
874
|
+
timestamp: bigint,
|
|
875
|
+
stateOverride: StateOverride = [],
|
|
749
876
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
750
|
-
const
|
|
751
|
-
const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
|
|
877
|
+
const timeOfNextL1Slot = timestamp;
|
|
752
878
|
const who = typeof account === 'string' ? account : account.address;
|
|
753
879
|
|
|
754
880
|
try {
|
|
@@ -760,7 +886,7 @@ export class RollupContract {
|
|
|
760
886
|
functionName: 'canProposeAtTime',
|
|
761
887
|
args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
|
|
762
888
|
account,
|
|
763
|
-
stateOverride
|
|
889
|
+
stateOverride,
|
|
764
890
|
});
|
|
765
891
|
|
|
766
892
|
return {
|
|
@@ -774,20 +900,32 @@ export class RollupContract {
|
|
|
774
900
|
}
|
|
775
901
|
|
|
776
902
|
/**
|
|
777
|
-
* Returns a state override that sets the pending
|
|
778
|
-
*
|
|
779
|
-
*
|
|
903
|
+
* Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
|
|
904
|
+
* Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
|
|
905
|
+
* a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
|
|
906
|
+
* value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
|
|
907
|
+
*
|
|
908
|
+
* Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
|
|
909
|
+
* `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
|
|
780
910
|
*/
|
|
781
|
-
public async
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
911
|
+
public async makeChainTipsOverride(override: {
|
|
912
|
+
pending?: CheckpointNumber;
|
|
913
|
+
proven?: CheckpointNumber;
|
|
914
|
+
}): Promise<StateOverride> {
|
|
915
|
+
if (override.pending === undefined && override.proven === undefined) {
|
|
785
916
|
return [];
|
|
786
917
|
}
|
|
787
918
|
const slot = RollupContract.stfStorageSlot;
|
|
788
919
|
const currentValue = await this.client.getStorageAt({ address: this.address, slot });
|
|
789
|
-
const
|
|
790
|
-
const
|
|
920
|
+
const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
|
|
921
|
+
const currentPending = currentRaw >> 128n;
|
|
922
|
+
const currentProven = currentRaw & ((1n << 128n) - 1n);
|
|
923
|
+
const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
|
|
924
|
+
const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
|
|
925
|
+
if (newProven > newPending) {
|
|
926
|
+
throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
|
|
927
|
+
}
|
|
928
|
+
const newValue = (newPending << 128n) | newProven;
|
|
791
929
|
return [
|
|
792
930
|
{
|
|
793
931
|
address: this.address,
|
|
@@ -796,6 +934,168 @@ export class RollupContract {
|
|
|
796
934
|
];
|
|
797
935
|
}
|
|
798
936
|
|
|
937
|
+
/**
|
|
938
|
+
* Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
|
|
939
|
+
* the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
|
|
940
|
+
* pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
|
|
941
|
+
*
|
|
942
|
+
* `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
|
|
943
|
+
* never asserts against them, so leaving them at storage zero is harmless.
|
|
944
|
+
*/
|
|
945
|
+
public async makeTempCheckpointLogOverride(
|
|
946
|
+
checkpointNumber: CheckpointNumber,
|
|
947
|
+
fields: TempCheckpointLogOverrideFields,
|
|
948
|
+
): Promise<StateOverride> {
|
|
949
|
+
const constants = await this.getRollupConstants();
|
|
950
|
+
const slotAt = (field: TempCheckpointLogField) =>
|
|
951
|
+
`0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}` as const;
|
|
952
|
+
const word = (v: bigint) => `0x${v.toString(16).padStart(64, '0')}` as const;
|
|
953
|
+
|
|
954
|
+
const stateDiff: { slot: `0x${string}`; value: `0x${string}` }[] = [];
|
|
955
|
+
|
|
956
|
+
if (fields.headerHash) {
|
|
957
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.HeaderHash), value: fields.headerHash.toString() });
|
|
958
|
+
}
|
|
959
|
+
if (fields.outHash) {
|
|
960
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.OutHash), value: fields.outHash.toString() });
|
|
961
|
+
}
|
|
962
|
+
if (fields.payloadDigest) {
|
|
963
|
+
stateDiff.push({
|
|
964
|
+
slot: slotAt(TempCheckpointLogField.PayloadDigest),
|
|
965
|
+
value: fields.payloadDigest.toString() as `0x${string}`,
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
if (fields.slotNumber !== undefined) {
|
|
969
|
+
// CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here
|
|
970
|
+
// so a malformed override surfaces immediately rather than silently truncating into a wrong slot.
|
|
971
|
+
const slotNumber = BigInt(fields.slotNumber);
|
|
972
|
+
if (slotNumber < 0n || slotNumber > 0xffffffffn) {
|
|
973
|
+
throw new Error(`slotNumber ${slotNumber} does not fit in uint32`);
|
|
974
|
+
}
|
|
975
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.SlotNumber), value: word(slotNumber) });
|
|
976
|
+
}
|
|
977
|
+
if (fields.feeHeader) {
|
|
978
|
+
stateDiff.push({
|
|
979
|
+
slot: slotAt(TempCheckpointLogField.FeeHeader),
|
|
980
|
+
value: word(RollupContract.compressFeeHeader(fields.feeHeader)),
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if (stateDiff.length === 0) {
|
|
985
|
+
return [];
|
|
986
|
+
}
|
|
987
|
+
return [{ address: this.address, stateDiff }];
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Returns a state override that sets archives[checkpointNumber] to the given archive value.
|
|
992
|
+
* Used when simulating a canProposeAtTime call where the local archive differs from L1
|
|
993
|
+
* (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
|
|
994
|
+
*/
|
|
995
|
+
public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
|
|
996
|
+
const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
|
|
997
|
+
const archiveSlot = hexToBigInt(
|
|
998
|
+
keccak256(
|
|
999
|
+
encodeAbiParameters(
|
|
1000
|
+
[{ type: 'uint256' }, { type: 'uint256' }],
|
|
1001
|
+
[BigInt(checkpointNumber), archivesMappingBase],
|
|
1002
|
+
),
|
|
1003
|
+
),
|
|
1004
|
+
);
|
|
1005
|
+
return [
|
|
1006
|
+
{
|
|
1007
|
+
address: this.address,
|
|
1008
|
+
stateDiff: [
|
|
1009
|
+
{
|
|
1010
|
+
slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
|
|
1011
|
+
value: archive.toString(),
|
|
1012
|
+
},
|
|
1013
|
+
],
|
|
1014
|
+
},
|
|
1015
|
+
];
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
|
|
1019
|
+
public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
|
|
1020
|
+
type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
|
|
1021
|
+
const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
|
|
1022
|
+
for (const override of overrides) {
|
|
1023
|
+
for (const entry of override) {
|
|
1024
|
+
const key = entry.address.toLowerCase();
|
|
1025
|
+
const existing = byAddress.get(key);
|
|
1026
|
+
if (existing) {
|
|
1027
|
+
existing.stateDiff.push(...(entry.stateDiff ?? []));
|
|
1028
|
+
if (entry.balance !== undefined) {
|
|
1029
|
+
existing.balance = entry.balance;
|
|
1030
|
+
}
|
|
1031
|
+
} else {
|
|
1032
|
+
byAddress.set(key, {
|
|
1033
|
+
address: entry.address,
|
|
1034
|
+
balance: entry.balance,
|
|
1035
|
+
stateDiff: [...(entry.stateDiff ?? [])],
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
return [...byAddress.values()];
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
|
|
1044
|
+
public static compressFeeHeader(feeHeader: FeeHeader): bigint {
|
|
1045
|
+
const MASK_48_BITS = (1n << 48n) - 1n;
|
|
1046
|
+
const MASK_64_BITS = (1n << 64n) - 1n;
|
|
1047
|
+
const MASK_63_BITS = (1n << 63n) - 1n;
|
|
1048
|
+
|
|
1049
|
+
let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
|
|
1050
|
+
value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
|
|
1051
|
+
value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
|
|
1052
|
+
value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
|
|
1053
|
+
value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
|
|
1054
|
+
value |= 1n << 255n; // preheat flag
|
|
1055
|
+
return value;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/** Computes the fee header for a child checkpoint given parent fee header and child data.
|
|
1059
|
+
* Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
|
|
1060
|
+
public static computeChildFeeHeader(
|
|
1061
|
+
parentFeeHeader: FeeHeader,
|
|
1062
|
+
childManaUsed: bigint,
|
|
1063
|
+
feeAssetPriceModifier: bigint,
|
|
1064
|
+
manaTarget: bigint,
|
|
1065
|
+
): FeeHeader {
|
|
1066
|
+
const MIN_ETH_PER_FEE_ASSET = 100n;
|
|
1067
|
+
const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
|
|
1068
|
+
|
|
1069
|
+
// excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
|
|
1070
|
+
const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
|
|
1071
|
+
const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
|
|
1072
|
+
|
|
1073
|
+
// ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
|
|
1074
|
+
const parentPrice =
|
|
1075
|
+
parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
|
|
1076
|
+
let newPrice: bigint;
|
|
1077
|
+
if (feeAssetPriceModifier >= 0n) {
|
|
1078
|
+
newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
|
|
1079
|
+
} else {
|
|
1080
|
+
const absMod = -feeAssetPriceModifier;
|
|
1081
|
+
newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
|
|
1082
|
+
}
|
|
1083
|
+
if (newPrice < MIN_ETH_PER_FEE_ASSET) {
|
|
1084
|
+
newPrice = MIN_ETH_PER_FEE_ASSET;
|
|
1085
|
+
}
|
|
1086
|
+
if (newPrice > MAX_ETH_PER_FEE_ASSET) {
|
|
1087
|
+
newPrice = MAX_ETH_PER_FEE_ASSET;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
return {
|
|
1091
|
+
excessMana,
|
|
1092
|
+
manaUsed: childManaUsed,
|
|
1093
|
+
ethPerFeeAsset: newPrice,
|
|
1094
|
+
congestionCost: 0n,
|
|
1095
|
+
proverCost: 0n,
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
|
|
799
1099
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
|
|
800
1100
|
public buildInvalidateBadAttestationRequest(
|
|
801
1101
|
checkpointNumber: CheckpointNumber,
|
|
@@ -805,6 +1105,7 @@ export class RollupContract {
|
|
|
805
1105
|
): L1TxRequest {
|
|
806
1106
|
return {
|
|
807
1107
|
to: this.address,
|
|
1108
|
+
abi: RollupAbi,
|
|
808
1109
|
data: encodeFunctionData({
|
|
809
1110
|
abi: RollupAbi,
|
|
810
1111
|
functionName: 'invalidateBadAttestation',
|
|
@@ -826,6 +1127,7 @@ export class RollupContract {
|
|
|
826
1127
|
): L1TxRequest {
|
|
827
1128
|
return {
|
|
828
1129
|
to: this.address,
|
|
1130
|
+
abi: RollupAbi,
|
|
829
1131
|
data: encodeFunctionData({
|
|
830
1132
|
abi: RollupAbi,
|
|
831
1133
|
functionName: 'invalidateInsufficientAttestations',
|
|
@@ -842,8 +1144,18 @@ export class RollupContract {
|
|
|
842
1144
|
return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
|
|
843
1145
|
}
|
|
844
1146
|
|
|
845
|
-
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
|
|
846
|
-
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
|
|
1147
|
+
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
|
|
1148
|
+
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
1152
|
+
const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
|
|
1153
|
+
return {
|
|
1154
|
+
sequencerCost: result.sequencerCost,
|
|
1155
|
+
proverCost: result.proverCost,
|
|
1156
|
+
congestionCost: result.congestionCost,
|
|
1157
|
+
congestionMultiplier: result.congestionMultiplier,
|
|
1158
|
+
};
|
|
847
1159
|
}
|
|
848
1160
|
|
|
849
1161
|
async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
|
|
@@ -889,15 +1201,21 @@ export class RollupContract {
|
|
|
889
1201
|
return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
|
|
890
1202
|
}
|
|
891
1203
|
|
|
892
|
-
async getAttesters(): Promise<EthAddress[]> {
|
|
893
|
-
|
|
1204
|
+
async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
|
|
1205
|
+
// Pin every read to a single L1 block so the attester count and the chunked index reads
|
|
1206
|
+
// observe a consistent set. Without this, the count and each chunk default to `latest` and
|
|
1207
|
+
// can straddle a block boundary (or reorg), yielding an inconsistent or truncated set.
|
|
1208
|
+
const block = await this.client.getBlock();
|
|
1209
|
+
const blockNumber = block.number ?? undefined;
|
|
1210
|
+
const ts = timestamp ?? block.timestamp;
|
|
1211
|
+
const attesterSize = await this.getActiveAttesterCount({ blockNumber });
|
|
894
1212
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
895
|
-
const ts = (await this.client.getBlock()).timestamp;
|
|
896
|
-
|
|
897
1213
|
const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
|
|
898
1214
|
const chunks = chunk(indices, 1000);
|
|
899
1215
|
|
|
900
|
-
const results = await Promise.all(
|
|
1216
|
+
const results = await Promise.all(
|
|
1217
|
+
chunks.map(chunk => gse.getAttestersFromIndicesAtTime(this.address, ts, chunk, { blockNumber })),
|
|
1218
|
+
);
|
|
901
1219
|
return results.flat().map(addr => EthAddress.fromString(addr));
|
|
902
1220
|
}
|
|
903
1221
|
|
|
@@ -959,6 +1277,7 @@ export class RollupContract {
|
|
|
959
1277
|
setupEpoch(l1TxUtils: L1TxUtils) {
|
|
960
1278
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
961
1279
|
to: this.address,
|
|
1280
|
+
abi: RollupAbi,
|
|
962
1281
|
data: encodeFunctionData({
|
|
963
1282
|
abi: RollupAbi,
|
|
964
1283
|
functionName: 'setupEpoch',
|
|
@@ -970,6 +1289,7 @@ export class RollupContract {
|
|
|
970
1289
|
vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
|
|
971
1290
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
972
1291
|
to: this.address,
|
|
1292
|
+
abi: RollupAbi,
|
|
973
1293
|
data: encodeFunctionData({
|
|
974
1294
|
abi: RollupAbi,
|
|
975
1295
|
functionName: 'vote',
|
|
@@ -997,7 +1317,7 @@ export class RollupContract {
|
|
|
997
1317
|
}
|
|
998
1318
|
|
|
999
1319
|
public listenToCheckpointInvalidated(
|
|
1000
|
-
callback: (args: { checkpointNumber: CheckpointNumber }) => unknown,
|
|
1320
|
+
callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
|
|
1001
1321
|
): WatchContractEventReturnType {
|
|
1002
1322
|
return this.rollup.watchEvent.CheckpointInvalidated(
|
|
1003
1323
|
{},
|
|
@@ -1006,7 +1326,7 @@ export class RollupContract {
|
|
|
1006
1326
|
for (const log of logs) {
|
|
1007
1327
|
const args = log.args;
|
|
1008
1328
|
if (args.checkpointNumber !== undefined) {
|
|
1009
|
-
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber) });
|
|
1329
|
+
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
|
|
1010
1330
|
}
|
|
1011
1331
|
}
|
|
1012
1332
|
},
|
|
@@ -1039,6 +1359,17 @@ export class RollupContract {
|
|
|
1039
1359
|
);
|
|
1040
1360
|
}
|
|
1041
1361
|
|
|
1362
|
+
/**
|
|
1363
|
+
* Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
|
|
1364
|
+
* The Rollup inherits from Ownable and emits this event in its constructor, so the event
|
|
1365
|
+
* is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
|
|
1366
|
+
* probe to detect RPC nodes that prune historical logs.
|
|
1367
|
+
*/
|
|
1368
|
+
async getOwnershipTransferredEventsAtDeploy() {
|
|
1369
|
+
const l1StartBlock = await this.getL1StartBlock();
|
|
1370
|
+
return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1042
1373
|
/** Fetches CheckpointProposed events within the given block range. */
|
|
1043
1374
|
async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
|
|
1044
1375
|
const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
|
|
@@ -1052,9 +1383,67 @@ export class RollupContract {
|
|
|
1052
1383
|
checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
|
|
1053
1384
|
archive: Fr.fromString(log.args.archive!),
|
|
1054
1385
|
versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
|
|
1055
|
-
attestationsHash:
|
|
1056
|
-
|
|
1386
|
+
attestationsHash: (() => {
|
|
1387
|
+
if (!log.args.attestationsHash) {
|
|
1388
|
+
throw new Error(
|
|
1389
|
+
`CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
return Buffer32.fromString(log.args.attestationsHash);
|
|
1393
|
+
})(),
|
|
1394
|
+
payloadDigest: (() => {
|
|
1395
|
+
if (!log.args.payloadDigest) {
|
|
1396
|
+
throw new Error(
|
|
1397
|
+
`CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
return Buffer32.fromString(log.args.payloadDigest);
|
|
1401
|
+
})(),
|
|
1057
1402
|
},
|
|
1058
1403
|
}));
|
|
1059
1404
|
}
|
|
1405
|
+
|
|
1406
|
+
/** Packs pending and proven checkpoint numbers into the chain tips storage format. */
|
|
1407
|
+
static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
|
|
1408
|
+
return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/** Storage slot for the chain tips (offset 0 within the STF storage struct). */
|
|
1412
|
+
static get chainTipsStorageSlot(): bigint {
|
|
1413
|
+
return BigInt(RollupContract.stfStorageSlot);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Computes the storage slot for a field within a tempCheckpointLog entry.
|
|
1418
|
+
* @param checkpointNumber - The checkpoint number
|
|
1419
|
+
* @param field - The field within the CompressedTempCheckpointLog struct
|
|
1420
|
+
*/
|
|
1421
|
+
async getTempCheckpointLogStorageSlot(
|
|
1422
|
+
checkpointNumber: CheckpointNumber,
|
|
1423
|
+
field: TempCheckpointLogField,
|
|
1424
|
+
): Promise<bigint> {
|
|
1425
|
+
const [epochDuration, proofSubmissionEpochs] = await Promise.all([
|
|
1426
|
+
this.getEpochDuration(),
|
|
1427
|
+
this.getProofSubmissionEpochs(),
|
|
1428
|
+
]);
|
|
1429
|
+
return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, { epochDuration, proofSubmissionEpochs });
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
private computeTempCheckpointLogStorageSlot(
|
|
1433
|
+
checkpointNumber: CheckpointNumber,
|
|
1434
|
+
field: TempCheckpointLogField,
|
|
1435
|
+
constants: { epochDuration: number; proofSubmissionEpochs: number },
|
|
1436
|
+
): bigint {
|
|
1437
|
+
const fieldOffset = BigInt(field);
|
|
1438
|
+
const { epochDuration, proofSubmissionEpochs } = constants;
|
|
1439
|
+
const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
|
|
1440
|
+
const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
|
|
1441
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
1442
|
+
const entryBase = BigInt(
|
|
1443
|
+
keccak256(
|
|
1444
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
|
|
1445
|
+
),
|
|
1446
|
+
);
|
|
1447
|
+
return entryBase + fieldOffset;
|
|
1448
|
+
}
|
|
1060
1449
|
}
|