@aztec/ethereum 0.0.1-commit.c2595eba → 0.0.1-commit.c2eed6949
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 +10 -2
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +13 -7
- package/dest/config.d.ts +3 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -3
- package/dest/contracts/empire_base.d.ts +3 -1
- package/dest/contracts/empire_base.d.ts.map +1 -1
- package/dest/contracts/empire_slashing_proposer.d.ts +3 -1
- package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
- package/dest/contracts/empire_slashing_proposer.js +22 -15
- package/dest/contracts/fee_asset_handler.d.ts +1 -1
- package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
- package/dest/contracts/fee_asset_handler.js +2 -0
- package/dest/contracts/fee_asset_price_oracle.d.ts +101 -0
- package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
- package/dest/contracts/fee_asset_price_oracle.js +651 -0
- package/dest/contracts/governance.d.ts +1 -1
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +5 -3
- package/dest/contracts/governance_proposer.d.ts +3 -1
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +13 -1
- package/dest/contracts/index.d.ts +2 -1
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +1 -0
- package/dest/contracts/multicall.d.ts +51 -2
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +87 -1
- 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 +22 -8
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +68 -12
- package/dest/contracts/tally_slashing_proposer.d.ts +1 -1
- package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
- package/dest/contracts/tally_slashing_proposer.js +8 -1
- package/dest/deploy_aztec_l1_contracts.d.ts +2 -3
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +37 -22
- package/dest/deploy_l1_contract.js +3 -3
- package/dest/generated/l1-contracts-defaults.d.ts +1 -1
- package/dest/generated/l1-contracts-defaults.js +1 -1
- package/dest/l1_artifacts.d.ts +1051 -42
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_reader.d.ts +3 -1
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +6 -1
- package/dest/l1_tx_utils/config.d.ts +7 -1
- package/dest/l1_tx_utils/config.d.ts.map +1 -1
- package/dest/l1_tx_utils/config.js +14 -1
- package/dest/l1_tx_utils/constants.d.ts +1 -1
- package/dest/l1_tx_utils/constants.js +2 -2
- package/dest/l1_tx_utils/factory.d.ts +18 -10
- package/dest/l1_tx_utils/factory.d.ts.map +1 -1
- package/dest/l1_tx_utils/factory.js +17 -7
- package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +1 -1
- package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
- package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
- package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
- package/dest/l1_tx_utils/index-blobs.js +2 -2
- package/dest/l1_tx_utils/index.d.ts +2 -1
- package/dest/l1_tx_utils/index.d.ts.map +1 -1
- package/dest/l1_tx_utils/index.js +1 -0
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +18 -7
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +58 -42
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +11 -7
- package/dest/l1_tx_utils/tx_delayer.d.ts +56 -0
- package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
- package/dest/{test → l1_tx_utils}/tx_delayer.js +62 -34
- package/dest/publisher_manager.d.ts +21 -7
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +81 -7
- package/dest/test/chain_monitor.d.ts +22 -3
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +33 -2
- package/dest/test/eth_cheat_codes.d.ts +6 -4
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +6 -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/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 +2 -1
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +62 -12
- package/package.json +5 -7
- package/src/client.ts +10 -2
- package/src/config.ts +15 -2
- package/src/contracts/empire_base.ts +2 -0
- package/src/contracts/empire_slashing_proposer.ts +22 -27
- package/src/contracts/fee_asset_handler.ts +2 -0
- package/src/contracts/fee_asset_price_oracle.ts +280 -0
- package/src/contracts/governance.ts +5 -3
- package/src/contracts/governance_proposer.ts +10 -1
- package/src/contracts/index.ts +1 -0
- package/src/contracts/multicall.ts +70 -3
- package/src/contracts/registry.ts +31 -1
- package/src/contracts/rollup.ts +79 -16
- package/src/contracts/tally_slashing_proposer.ts +5 -1
- package/src/deploy_aztec_l1_contracts.ts +60 -29
- package/src/deploy_l1_contract.ts +3 -3
- package/src/generated/l1-contracts-defaults.ts +1 -1
- package/src/l1_reader.ts +13 -1
- 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 +60 -32
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +11 -7
- package/src/{test → l1_tx_utils}/tx_delayer.ts +78 -50
- package/src/publisher_manager.ts +105 -10
- package/src/test/chain_monitor.ts +60 -3
- package/src/test/eth_cheat_codes.ts +6 -4
- package/src/test/index.ts +0 -2
- package/src/test/start_anvil.ts +177 -29
- package/src/test/upgrade_utils.ts +2 -2
- package/src/utils.ts +70 -14
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
- package/dest/test/delayed_tx_utils.d.ts +0 -13
- package/dest/test/delayed_tx_utils.d.ts.map +0 -1
- package/dest/test/delayed_tx_utils.js +0 -28
- package/dest/test/tx_delayer.d.ts +0 -36
- package/dest/test/tx_delayer.d.ts.map +0 -1
- package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
- package/src/test/delayed_tx_utils.ts +0 -52
package/src/contracts/rollup.ts
CHANGED
|
@@ -134,6 +134,14 @@ export type L1FeeData = {
|
|
|
134
134
|
blobFee: bigint;
|
|
135
135
|
};
|
|
136
136
|
|
|
137
|
+
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
138
|
+
export type ManaMinFeeComponents = {
|
|
139
|
+
sequencerCost: bigint;
|
|
140
|
+
proverCost: bigint;
|
|
141
|
+
congestionCost: bigint;
|
|
142
|
+
congestionMultiplier: bigint;
|
|
143
|
+
};
|
|
144
|
+
|
|
137
145
|
/**
|
|
138
146
|
* Reward configuration for the rollup
|
|
139
147
|
*/
|
|
@@ -193,10 +201,10 @@ export type CheckpointProposedArgs = {
|
|
|
193
201
|
checkpointNumber: CheckpointNumber;
|
|
194
202
|
archive: Fr;
|
|
195
203
|
versionedBlobHashes: Buffer[];
|
|
196
|
-
/** Hash of attestations
|
|
197
|
-
attestationsHash
|
|
198
|
-
/** Digest of the payload
|
|
199
|
-
payloadDigest
|
|
204
|
+
/** Hash of attestations emitted in the CheckpointProposed event. */
|
|
205
|
+
attestationsHash: Buffer32;
|
|
206
|
+
/** Digest of the payload emitted in the CheckpointProposed event. */
|
|
207
|
+
payloadDigest: Buffer32;
|
|
200
208
|
};
|
|
201
209
|
|
|
202
210
|
/** Log type for CheckpointProposed events. */
|
|
@@ -379,6 +387,20 @@ export class RollupContract {
|
|
|
379
387
|
return Fr.fromString(await this.rollup.read.archiveAt([0n]));
|
|
380
388
|
}
|
|
381
389
|
|
|
390
|
+
@memoize
|
|
391
|
+
async getVkTreeRoot(): Promise<Fr> {
|
|
392
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
|
|
393
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
394
|
+
return Fr.fromString(value ?? '0x0');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
@memoize
|
|
398
|
+
async getProtocolContractsHash(): Promise<Fr> {
|
|
399
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
|
|
400
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
401
|
+
return Fr.fromString(value ?? '0x0');
|
|
402
|
+
}
|
|
403
|
+
|
|
382
404
|
/**
|
|
383
405
|
* Returns rollup constants used for epoch queries.
|
|
384
406
|
* Return type is `L1RollupConstants` which is defined in stdlib,
|
|
@@ -391,13 +413,25 @@ export class RollupContract {
|
|
|
391
413
|
slotDuration: number;
|
|
392
414
|
epochDuration: number;
|
|
393
415
|
proofSubmissionEpochs: number;
|
|
416
|
+
targetCommitteeSize: number;
|
|
417
|
+
rollupManaLimit: number;
|
|
394
418
|
}> {
|
|
395
|
-
const [
|
|
419
|
+
const [
|
|
420
|
+
l1StartBlock,
|
|
421
|
+
l1GenesisTime,
|
|
422
|
+
slotDuration,
|
|
423
|
+
epochDuration,
|
|
424
|
+
proofSubmissionEpochs,
|
|
425
|
+
targetCommitteeSize,
|
|
426
|
+
rollupManaLimit,
|
|
427
|
+
] = await Promise.all([
|
|
396
428
|
this.getL1StartBlock(),
|
|
397
429
|
this.getL1GenesisTime(),
|
|
398
430
|
this.getSlotDuration(),
|
|
399
431
|
this.getEpochDuration(),
|
|
400
432
|
this.getProofSubmissionEpochs(),
|
|
433
|
+
this.getTargetCommitteeSize(),
|
|
434
|
+
this.getManaLimit(),
|
|
401
435
|
]);
|
|
402
436
|
return {
|
|
403
437
|
l1StartBlock,
|
|
@@ -405,6 +439,8 @@ export class RollupContract {
|
|
|
405
439
|
slotDuration,
|
|
406
440
|
epochDuration: Number(epochDuration),
|
|
407
441
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
442
|
+
targetCommitteeSize,
|
|
443
|
+
rollupManaLimit: Number(rollupManaLimit),
|
|
408
444
|
};
|
|
409
445
|
}
|
|
410
446
|
|
|
@@ -499,8 +535,9 @@ export class RollupContract {
|
|
|
499
535
|
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber());
|
|
500
536
|
}
|
|
501
537
|
|
|
502
|
-
async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
503
|
-
|
|
538
|
+
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
539
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
540
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
504
541
|
}
|
|
505
542
|
|
|
506
543
|
async getSlotNumber(): Promise<SlotNumber> {
|
|
@@ -741,14 +778,13 @@ export class RollupContract {
|
|
|
741
778
|
* timestamp of the next L1 block
|
|
742
779
|
* @throws otherwise
|
|
743
780
|
*/
|
|
744
|
-
public async
|
|
781
|
+
public async canProposeAt(
|
|
745
782
|
archive: Buffer,
|
|
746
783
|
account: `0x${string}` | Account,
|
|
747
|
-
|
|
784
|
+
timestamp: bigint,
|
|
748
785
|
opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
749
786
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
750
|
-
const
|
|
751
|
-
const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
|
|
787
|
+
const timeOfNextL1Slot = timestamp;
|
|
752
788
|
const who = typeof account === 'string' ? account : account.address;
|
|
753
789
|
|
|
754
790
|
try {
|
|
@@ -805,6 +841,7 @@ export class RollupContract {
|
|
|
805
841
|
): L1TxRequest {
|
|
806
842
|
return {
|
|
807
843
|
to: this.address,
|
|
844
|
+
abi: RollupAbi,
|
|
808
845
|
data: encodeFunctionData({
|
|
809
846
|
abi: RollupAbi,
|
|
810
847
|
functionName: 'invalidateBadAttestation',
|
|
@@ -826,6 +863,7 @@ export class RollupContract {
|
|
|
826
863
|
): L1TxRequest {
|
|
827
864
|
return {
|
|
828
865
|
to: this.address,
|
|
866
|
+
abi: RollupAbi,
|
|
829
867
|
data: encodeFunctionData({
|
|
830
868
|
abi: RollupAbi,
|
|
831
869
|
functionName: 'invalidateInsufficientAttestations',
|
|
@@ -846,6 +884,16 @@ export class RollupContract {
|
|
|
846
884
|
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
|
|
847
885
|
}
|
|
848
886
|
|
|
887
|
+
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
888
|
+
const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
|
|
889
|
+
return {
|
|
890
|
+
sequencerCost: result.sequencerCost,
|
|
891
|
+
proverCost: result.proverCost,
|
|
892
|
+
congestionCost: result.congestionCost,
|
|
893
|
+
congestionMultiplier: result.congestionMultiplier,
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
|
|
849
897
|
async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
|
|
850
898
|
return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([timestamp]));
|
|
851
899
|
}
|
|
@@ -889,11 +937,10 @@ export class RollupContract {
|
|
|
889
937
|
return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
|
|
890
938
|
}
|
|
891
939
|
|
|
892
|
-
async getAttesters(): Promise<EthAddress[]> {
|
|
940
|
+
async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
|
|
893
941
|
const attesterSize = await this.getActiveAttesterCount();
|
|
894
942
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
895
|
-
const ts = (await this.client.getBlock()).timestamp;
|
|
896
|
-
|
|
943
|
+
const ts = timestamp ?? (await this.client.getBlock()).timestamp;
|
|
897
944
|
const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
|
|
898
945
|
const chunks = chunk(indices, 1000);
|
|
899
946
|
|
|
@@ -959,6 +1006,7 @@ export class RollupContract {
|
|
|
959
1006
|
setupEpoch(l1TxUtils: L1TxUtils) {
|
|
960
1007
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
961
1008
|
to: this.address,
|
|
1009
|
+
abi: RollupAbi,
|
|
962
1010
|
data: encodeFunctionData({
|
|
963
1011
|
abi: RollupAbi,
|
|
964
1012
|
functionName: 'setupEpoch',
|
|
@@ -970,6 +1018,7 @@ export class RollupContract {
|
|
|
970
1018
|
vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
|
|
971
1019
|
return l1TxUtils.sendAndMonitorTransaction({
|
|
972
1020
|
to: this.address,
|
|
1021
|
+
abi: RollupAbi,
|
|
973
1022
|
data: encodeFunctionData({
|
|
974
1023
|
abi: RollupAbi,
|
|
975
1024
|
functionName: 'vote',
|
|
@@ -1052,8 +1101,22 @@ export class RollupContract {
|
|
|
1052
1101
|
checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
|
|
1053
1102
|
archive: Fr.fromString(log.args.archive!),
|
|
1054
1103
|
versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
|
|
1055
|
-
attestationsHash:
|
|
1056
|
-
|
|
1104
|
+
attestationsHash: (() => {
|
|
1105
|
+
if (!log.args.attestationsHash) {
|
|
1106
|
+
throw new Error(
|
|
1107
|
+
`CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
return Buffer32.fromString(log.args.attestationsHash);
|
|
1111
|
+
})(),
|
|
1112
|
+
payloadDigest: (() => {
|
|
1113
|
+
if (!log.args.payloadDigest) {
|
|
1114
|
+
throw new Error(
|
|
1115
|
+
`CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
return Buffer32.fromString(log.args.payloadDigest);
|
|
1119
|
+
})(),
|
|
1057
1120
|
},
|
|
1058
1121
|
}));
|
|
1059
1122
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { L1TxRequest } from '@aztec/ethereum/l1-tx-utils';
|
|
2
2
|
import type { ViemClient } from '@aztec/ethereum/types';
|
|
3
|
-
import { tryExtractEvent } from '@aztec/ethereum/utils';
|
|
3
|
+
import { mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
4
4
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
5
5
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
6
6
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
7
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
8
8
|
import { hexToBuffer } from '@aztec/foundation/string';
|
|
9
|
+
import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
|
|
9
10
|
import { TallySlashingProposerAbi } from '@aztec/l1-artifacts/TallySlashingProposerAbi';
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -160,6 +161,7 @@ export class TallySlashingProposerContract {
|
|
|
160
161
|
|
|
161
162
|
return {
|
|
162
163
|
to: this.contract.address,
|
|
164
|
+
abi: TallySlashingProposerAbi,
|
|
163
165
|
data: encodeFunctionData({
|
|
164
166
|
abi: TallySlashingProposerAbi,
|
|
165
167
|
functionName: 'vote',
|
|
@@ -207,6 +209,7 @@ export class TallySlashingProposerContract {
|
|
|
207
209
|
public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
|
|
208
210
|
return {
|
|
209
211
|
to: this.contract.address,
|
|
212
|
+
abi: TallySlashingProposerAbi,
|
|
210
213
|
data: encodeFunctionData({
|
|
211
214
|
abi: TallySlashingProposerAbi,
|
|
212
215
|
functionName: 'vote',
|
|
@@ -224,6 +227,7 @@ export class TallySlashingProposerContract {
|
|
|
224
227
|
public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
|
|
225
228
|
return {
|
|
226
229
|
to: this.contract.address,
|
|
230
|
+
abi: mergeAbis([TallySlashingProposerAbi, SlasherAbi]),
|
|
227
231
|
data: encodeFunctionData({
|
|
228
232
|
abi: TallySlashingProposerAbi,
|
|
229
233
|
functionName: 'executeRound',
|
|
@@ -15,7 +15,7 @@ import { tmpdir } from 'os';
|
|
|
15
15
|
import { dirname, join, resolve } from 'path';
|
|
16
16
|
import readline from 'readline';
|
|
17
17
|
import type { Hex } from 'viem';
|
|
18
|
-
import {
|
|
18
|
+
import { mainnet, sepolia } from 'viem/chains';
|
|
19
19
|
|
|
20
20
|
import { createEthereumChain, isAnvilTestChain } from './chain.js';
|
|
21
21
|
import { createExtendedL1Client } from './client.js';
|
|
@@ -23,7 +23,6 @@ import type { L1ContractsConfig } from './config.js';
|
|
|
23
23
|
import { deployMulticall3 } from './contracts/multicall.js';
|
|
24
24
|
import { RollupContract } from './contracts/rollup.js';
|
|
25
25
|
import type { L1ContractAddresses } from './l1_contract_addresses.js';
|
|
26
|
-
import type { L1TxUtilsConfig } from './l1_tx_utils/config.js';
|
|
27
26
|
import type { ExtendedViemWalletClient } from './types.js';
|
|
28
27
|
|
|
29
28
|
const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
|
|
@@ -31,9 +30,9 @@ const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
|
|
|
31
30
|
const JSON_DEPLOY_RESULT_PREFIX = 'JSON DEPLOY RESULT:';
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
|
-
* Runs a process
|
|
35
|
-
*
|
|
36
|
-
*
|
|
33
|
+
* Runs a process and parses JSON deploy results from stdout.
|
|
34
|
+
* Lines starting with JSON_DEPLOY_RESULT_PREFIX are parsed and returned.
|
|
35
|
+
* All other stdout goes to logger.info, stderr goes to logger.warn.
|
|
37
36
|
*/
|
|
38
37
|
function runProcess<T>(
|
|
39
38
|
command: string,
|
|
@@ -49,26 +48,41 @@ function runProcess<T>(
|
|
|
49
48
|
});
|
|
50
49
|
|
|
51
50
|
let result: T | undefined;
|
|
51
|
+
let parseError: Error | undefined;
|
|
52
|
+
let settled = false;
|
|
52
53
|
|
|
53
54
|
readline.createInterface({ input: proc.stdout }).on('line', line => {
|
|
54
55
|
const trimmedLine = line.trim();
|
|
55
56
|
if (trimmedLine.startsWith(JSON_DEPLOY_RESULT_PREFIX)) {
|
|
56
57
|
const jsonStr = trimmedLine.slice(JSON_DEPLOY_RESULT_PREFIX.length).trim();
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
try {
|
|
59
|
+
result = JSON.parse(jsonStr);
|
|
60
|
+
} catch {
|
|
61
|
+
parseError = new Error(`Failed to parse deploy result JSON: ${jsonStr.slice(0, 200)}`);
|
|
62
|
+
}
|
|
59
63
|
} else {
|
|
60
64
|
logger.info(line);
|
|
61
65
|
}
|
|
62
66
|
});
|
|
63
|
-
readline.createInterface({ input: proc.stderr }).on('line', logger.
|
|
67
|
+
readline.createInterface({ input: proc.stderr }).on('line', logger.warn.bind(logger));
|
|
64
68
|
|
|
65
69
|
proc.on('error', error => {
|
|
70
|
+
if (settled) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
settled = true;
|
|
66
74
|
reject(new Error(`Failed to spawn ${command}: ${error.message}`));
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
proc.on('close', code => {
|
|
78
|
+
if (settled) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
settled = true;
|
|
70
82
|
if (code !== 0) {
|
|
71
|
-
reject(new Error(`${command} exited with code ${code}
|
|
83
|
+
reject(new Error(`${command} exited with code ${code}`));
|
|
84
|
+
} else if (parseError) {
|
|
85
|
+
reject(parseError);
|
|
72
86
|
} else {
|
|
73
87
|
resolve(result);
|
|
74
88
|
}
|
|
@@ -168,6 +182,9 @@ export function prepareL1ContractsForDeployment(): string {
|
|
|
168
182
|
const foundryTomlPath = join(basePath, 'foundry.toml');
|
|
169
183
|
let foundryToml = readFileSync(foundryTomlPath, 'utf-8');
|
|
170
184
|
const solcPathMatch = foundryToml.match(/solc\s*=\s*"\.\/solc-([^"]+)"/);
|
|
185
|
+
// Did we find a hardcoded solc path that we need to make absolute?
|
|
186
|
+
// This code path happens in CI currently as we bundle solc there to avoid race conditions when
|
|
187
|
+
// downloading solc.
|
|
171
188
|
if (solcPathMatch) {
|
|
172
189
|
const solcVersion = solcPathMatch[1];
|
|
173
190
|
const absoluteSolcPath = join(basePath, `solc-${solcVersion}`);
|
|
@@ -318,11 +335,8 @@ export async function deployAztecL1Contracts(
|
|
|
318
335
|
);
|
|
319
336
|
}
|
|
320
337
|
|
|
321
|
-
|
|
322
|
-
const MAGIC_ANVIL_BATCH_SIZE = 8;
|
|
323
|
-
// Anvil seems to stall with unbounded batch size. Otherwise no max batch size is desirable.
|
|
338
|
+
const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
|
|
324
339
|
const forgeArgs = [
|
|
325
|
-
'script',
|
|
326
340
|
FORGE_SCRIPT,
|
|
327
341
|
'--sig',
|
|
328
342
|
'run()',
|
|
@@ -330,8 +344,6 @@ export async function deployAztecL1Contracts(
|
|
|
330
344
|
privateKey,
|
|
331
345
|
'--rpc-url',
|
|
332
346
|
rpcUrl,
|
|
333
|
-
'--broadcast',
|
|
334
|
-
...(chainId === foundry.id ? ['--batch-size', MAGIC_ANVIL_BATCH_SIZE.toString()] : []),
|
|
335
347
|
...(shouldVerify ? ['--verify'] : []),
|
|
336
348
|
];
|
|
337
349
|
const forgeEnv = {
|
|
@@ -340,7 +352,12 @@ export async function deployAztecL1Contracts(
|
|
|
340
352
|
FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
|
|
341
353
|
...getDeployAztecL1ContractsEnvVars(args),
|
|
342
354
|
};
|
|
343
|
-
const result = await runProcess<ForgeL1ContractsDeployResult>(
|
|
355
|
+
const result = await runProcess<ForgeL1ContractsDeployResult>(
|
|
356
|
+
process.execPath,
|
|
357
|
+
[scriptPath, ...forgeArgs],
|
|
358
|
+
forgeEnv,
|
|
359
|
+
l1ContractsPath,
|
|
360
|
+
);
|
|
344
361
|
if (!result) {
|
|
345
362
|
throw new Error('Forge script did not output deployment result');
|
|
346
363
|
}
|
|
@@ -473,7 +490,25 @@ export type VerificationRecord = {
|
|
|
473
490
|
libraries: VerificationLibraryEntry[];
|
|
474
491
|
};
|
|
475
492
|
|
|
476
|
-
export interface DeployAztecL1ContractsArgs
|
|
493
|
+
export interface DeployAztecL1ContractsArgs
|
|
494
|
+
extends Omit<
|
|
495
|
+
L1ContractsConfig,
|
|
496
|
+
| 'gasLimitBufferPercentage'
|
|
497
|
+
| 'maxGwei'
|
|
498
|
+
| 'maxBlobGwei'
|
|
499
|
+
| 'priorityFeeBumpPercentage'
|
|
500
|
+
| 'priorityFeeRetryBumpPercentage'
|
|
501
|
+
| 'minimumPriorityFeePerGas'
|
|
502
|
+
| 'maxSpeedUpAttempts'
|
|
503
|
+
| 'checkIntervalMs'
|
|
504
|
+
| 'stallTimeMs'
|
|
505
|
+
| 'txTimeoutMs'
|
|
506
|
+
| 'cancelTxOnTimeout'
|
|
507
|
+
| 'txCancellationFinalTimeoutMs'
|
|
508
|
+
| 'txUnseenConsideredDroppedMs'
|
|
509
|
+
| 'enableDelayer'
|
|
510
|
+
| 'txDelayerMaxInclusionTimeIntoSlot'
|
|
511
|
+
> {
|
|
477
512
|
/** The vk tree root. */
|
|
478
513
|
vkTreeRoot: Fr;
|
|
479
514
|
/** The hash of the protocol contracts. */
|
|
@@ -583,17 +618,8 @@ export const deployRollupForUpgrade = async (
|
|
|
583
618
|
const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
|
|
584
619
|
await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
585
620
|
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
FORGE_SCRIPT,
|
|
589
|
-
'--sig',
|
|
590
|
-
'run()',
|
|
591
|
-
'--private-key',
|
|
592
|
-
privateKey,
|
|
593
|
-
'--rpc-url',
|
|
594
|
-
rpcUrl,
|
|
595
|
-
'--broadcast',
|
|
596
|
-
];
|
|
621
|
+
const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
|
|
622
|
+
const forgeArgs = [FORGE_SCRIPT, '--sig', 'run()', '--private-key', privateKey, '--rpc-url', rpcUrl];
|
|
597
623
|
const forgeEnv = {
|
|
598
624
|
FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
|
|
599
625
|
// Env vars required by l1-contracts/script/deploy/RollupConfiguration.sol.
|
|
@@ -602,7 +628,12 @@ export const deployRollupForUpgrade = async (
|
|
|
602
628
|
...getDeployRollupForUpgradeEnvVars(args),
|
|
603
629
|
};
|
|
604
630
|
|
|
605
|
-
const result = await runProcess<ForgeRollupUpgradeResult>(
|
|
631
|
+
const result = await runProcess<ForgeRollupUpgradeResult>(
|
|
632
|
+
process.execPath,
|
|
633
|
+
[scriptPath, ...forgeArgs],
|
|
634
|
+
forgeEnv,
|
|
635
|
+
l1ContractsPath,
|
|
636
|
+
);
|
|
606
637
|
if (!result) {
|
|
607
638
|
throw new Error('Forge script did not output deployment result');
|
|
608
639
|
}
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from './deploy_aztec_l1_contracts.js';
|
|
25
25
|
import { RegisterNewRollupVersionPayloadArtifact } from './l1_artifacts.js';
|
|
26
26
|
import { type L1TxUtilsConfig, getL1TxUtilsConfigEnvVars } from './l1_tx_utils/config.js';
|
|
27
|
-
import {
|
|
27
|
+
import { createL1TxUtils } from './l1_tx_utils/factory.js';
|
|
28
28
|
import type { L1TxUtils } from './l1_tx_utils/l1_tx_utils.js';
|
|
29
29
|
import type { GasPrice, L1TxConfig, L1TxRequest } from './l1_tx_utils/types.js';
|
|
30
30
|
import type { ExtendedViemWalletClient } from './types.js';
|
|
@@ -46,7 +46,7 @@ export class L1Deployer {
|
|
|
46
46
|
private createVerificationJson: boolean = false,
|
|
47
47
|
) {
|
|
48
48
|
this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), { size: 32 }) : undefined;
|
|
49
|
-
this.l1TxUtils =
|
|
49
|
+
this.l1TxUtils = createL1TxUtils(
|
|
50
50
|
this.client,
|
|
51
51
|
{ logger: this.logger, dateProvider },
|
|
52
52
|
{ ...this.txUtilsConfig, debugMaxGasLimit: acceleratedTestDeployments },
|
|
@@ -179,7 +179,7 @@ export async function deployL1Contract(
|
|
|
179
179
|
|
|
180
180
|
if (!l1TxUtils) {
|
|
181
181
|
const config = getL1TxUtilsConfigEnvVars();
|
|
182
|
-
l1TxUtils =
|
|
182
|
+
l1TxUtils = createL1TxUtils(
|
|
183
183
|
extendedClient,
|
|
184
184
|
{ logger },
|
|
185
185
|
{ ...config, debugMaxGasLimit: acceleratedTestDeployments },
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
/** Default L1 contracts configuration values from network-defaults.yml */
|
|
5
5
|
export const l1ContractsDefaultEnv = {
|
|
6
6
|
ETHEREUM_SLOT_DURATION: 12,
|
|
7
|
-
AZTEC_SLOT_DURATION:
|
|
7
|
+
AZTEC_SLOT_DURATION: 72,
|
|
8
8
|
AZTEC_EPOCH_DURATION: 32,
|
|
9
9
|
AZTEC_TARGET_COMMITTEE_SIZE: 48,
|
|
10
10
|
AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: 2,
|
package/src/l1_reader.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ConfigMappingsType,
|
|
3
|
+
getConfigFromMappings,
|
|
4
|
+
numberConfigHelper,
|
|
5
|
+
optionalNumberConfigHelper,
|
|
6
|
+
} from '@aztec/foundation/config';
|
|
2
7
|
|
|
3
8
|
import { type L1ContractAddresses, l1ContractAddressesMapping } from './l1_contract_addresses.js';
|
|
4
9
|
|
|
@@ -14,6 +19,8 @@ export interface L1ReaderConfig {
|
|
|
14
19
|
l1Contracts: L1ContractAddresses;
|
|
15
20
|
/** The polling interval viem uses in ms */
|
|
16
21
|
viemPollingIntervalMS: number;
|
|
22
|
+
/** Timeout for HTTP requests to the L1 RPC node in ms. */
|
|
23
|
+
l1HttpTimeoutMS?: number;
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
export const l1ReaderConfigMappings: ConfigMappingsType<L1ReaderConfig> = {
|
|
@@ -43,6 +50,11 @@ export const l1ReaderConfigMappings: ConfigMappingsType<L1ReaderConfig> = {
|
|
|
43
50
|
description: 'The polling interval viem uses in ms',
|
|
44
51
|
...numberConfigHelper(1_000),
|
|
45
52
|
},
|
|
53
|
+
l1HttpTimeoutMS: {
|
|
54
|
+
env: 'ETHEREUM_HTTP_TIMEOUT_MS',
|
|
55
|
+
description: 'Timeout for HTTP requests to the L1 RPC node in ms.',
|
|
56
|
+
...optionalNumberConfigHelper(),
|
|
57
|
+
},
|
|
46
58
|
};
|
|
47
59
|
|
|
48
60
|
export function getL1ReaderConfigFromEnv(): L1ReaderConfig {
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
getConfigFromMappings,
|
|
6
6
|
getDefaultConfig,
|
|
7
7
|
numberConfigHelper,
|
|
8
|
+
optionalNumberConfigHelper,
|
|
8
9
|
} from '@aztec/foundation/config';
|
|
9
10
|
|
|
10
11
|
export interface L1TxUtilsConfig {
|
|
@@ -60,6 +61,12 @@ export interface L1TxUtilsConfig {
|
|
|
60
61
|
* How long a tx nonce can be unseen in the mempool before considering it dropped
|
|
61
62
|
*/
|
|
62
63
|
txUnseenConsideredDroppedMs?: number;
|
|
64
|
+
/** Enable tx delayer. When true, wraps the viem client to intercept and delay txs. Test-only. */
|
|
65
|
+
enableDelayer?: boolean;
|
|
66
|
+
/** Max seconds into an L1 slot for tx inclusion. Txs sent later are deferred to next slot. Only used when enableDelayer is true. */
|
|
67
|
+
txDelayerMaxInclusionTimeIntoSlot?: number;
|
|
68
|
+
/** How many seconds an L1 slot lasts. */
|
|
69
|
+
ethereumSlotDuration?: number;
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
|
|
@@ -142,6 +149,19 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
|
|
|
142
149
|
env: 'L1_TX_MONITOR_TX_UNSEEN_CONSIDERED_DROPPED_MS',
|
|
143
150
|
...numberConfigHelper(6 * 12 * 1000), // 6 L1 blocks
|
|
144
151
|
},
|
|
152
|
+
enableDelayer: {
|
|
153
|
+
description: 'Enable tx delayer for testing.',
|
|
154
|
+
...booleanConfigHelper(false),
|
|
155
|
+
},
|
|
156
|
+
txDelayerMaxInclusionTimeIntoSlot: {
|
|
157
|
+
description: 'Max seconds into L1 slot for tx inclusion when delayer is enabled.',
|
|
158
|
+
...optionalNumberConfigHelper(),
|
|
159
|
+
},
|
|
160
|
+
ethereumSlotDuration: {
|
|
161
|
+
env: 'ETHEREUM_SLOT_DURATION',
|
|
162
|
+
description: 'How many seconds an L1 slot lasts.',
|
|
163
|
+
...numberConfigHelper(12),
|
|
164
|
+
},
|
|
145
165
|
};
|
|
146
166
|
|
|
147
167
|
// We abuse the fact that all mappings above have a non null default value and force-type this to Required
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// 1_000_000_000_000_000_000 Wei = 1 ETH
|
|
4
4
|
export const WEI_CONST = 1_000_000_000n;
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
export const
|
|
6
|
+
// EIP-7825: protocol-level cap on tx gas limit (2^24). Clients reject above this.
|
|
7
|
+
export const MAX_L1_TX_LIMIT = 16_777_216n;
|
|
8
8
|
|
|
9
9
|
// setting a minimum bump percentage to 10% due to geth's implementation
|
|
10
10
|
// https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/legacypool/list.go#L298
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
+
import type { BlobKzgInstance } from '@aztec/blob-lib/types';
|
|
1
2
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
3
|
import type { Logger } from '@aztec/foundation/log';
|
|
3
4
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
4
5
|
|
|
5
|
-
import type { TransactionSerializable } from 'viem';
|
|
6
|
-
|
|
7
6
|
import type { EthSigner } from '../eth-signer/eth-signer.js';
|
|
8
7
|
import type { ExtendedViemWalletClient, ViemClient } from '../types.js';
|
|
9
8
|
import type { L1TxUtilsConfig } from './config.js';
|
|
10
9
|
import type { IL1TxMetrics, IL1TxStore } from './interfaces.js';
|
|
11
10
|
import { L1TxUtils } from './l1_tx_utils.js';
|
|
12
11
|
import { createViemSigner } from './signer.js';
|
|
12
|
+
import { Delayer } from './tx_delayer.js';
|
|
13
13
|
import type { SigningCallback } from './types.js';
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
/** Source of signing capability: either a wallet client or a separate client + signer. */
|
|
16
|
+
export type L1SignerSource = ExtendedViemWalletClient | { client: ViemClient; signer: EthSigner };
|
|
17
|
+
|
|
18
|
+
export function resolveSignerSource(source: L1SignerSource): {
|
|
19
|
+
client: ViemClient;
|
|
20
|
+
address: EthAddress;
|
|
21
|
+
signingCallback: SigningCallback;
|
|
22
|
+
} {
|
|
23
|
+
if ('account' in source && source.account) {
|
|
24
|
+
return {
|
|
25
|
+
client: source as ExtendedViemWalletClient,
|
|
26
|
+
address: EthAddress.fromString((source as ExtendedViemWalletClient).account.address),
|
|
27
|
+
signingCallback: createViemSigner(source as ExtendedViemWalletClient),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const { client, signer } = source as { client: ViemClient; signer: EthSigner };
|
|
31
|
+
return {
|
|
26
32
|
client,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
deps?.dateProvider,
|
|
31
|
-
config,
|
|
32
|
-
config?.debugMaxGasLimit ?? false,
|
|
33
|
-
deps?.store,
|
|
34
|
-
deps?.metrics,
|
|
35
|
-
);
|
|
33
|
+
address: signer.address,
|
|
34
|
+
signingCallback: async (tx, _addr) => (await signer.signTransaction(tx)).toViemTransactionSignature(),
|
|
35
|
+
};
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function
|
|
39
|
-
|
|
40
|
-
signer: EthSigner,
|
|
38
|
+
export function createL1TxUtils(
|
|
39
|
+
source: L1SignerSource,
|
|
41
40
|
deps?: {
|
|
42
41
|
logger?: Logger;
|
|
43
42
|
dateProvider?: DateProvider;
|
|
44
43
|
store?: IL1TxStore;
|
|
45
44
|
metrics?: IL1TxMetrics;
|
|
45
|
+
kzg?: BlobKzgInstance;
|
|
46
|
+
delayer?: Delayer;
|
|
46
47
|
},
|
|
47
48
|
config?: Partial<L1TxUtilsConfig> & { debugMaxGasLimit?: boolean },
|
|
48
49
|
): L1TxUtils {
|
|
49
|
-
const
|
|
50
|
-
return (await signer.signTransaction(transaction)).toViemTransactionSignature();
|
|
51
|
-
};
|
|
52
|
-
|
|
50
|
+
const { client, address, signingCallback } = resolveSignerSource(source);
|
|
53
51
|
return new L1TxUtils(
|
|
54
52
|
client,
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
address,
|
|
54
|
+
signingCallback,
|
|
57
55
|
deps?.logger,
|
|
58
56
|
deps?.dateProvider,
|
|
59
57
|
config,
|
|
60
58
|
config?.debugMaxGasLimit ?? false,
|
|
61
59
|
deps?.store,
|
|
62
60
|
deps?.metrics,
|
|
61
|
+
deps?.kzg,
|
|
62
|
+
deps?.delayer,
|
|
63
63
|
);
|
|
64
64
|
}
|
|
@@ -134,7 +134,7 @@ export const P75AllTxsPriorityFeeStrategy: PriorityFeeStrategy = {
|
|
|
134
134
|
// Sanity check: cap competitive fee at 100x network estimate to avoid using unrealistic fees
|
|
135
135
|
const maxReasonableFee = networkEstimate * 100n;
|
|
136
136
|
if (competitiveFee > maxReasonableFee && networkEstimate > 0n) {
|
|
137
|
-
logger?.
|
|
137
|
+
logger?.debug('Competitive fee exceeds sanity cap, using capped value', {
|
|
138
138
|
competitiveFee: formatGwei(competitiveFee),
|
|
139
139
|
networkEstimate: formatGwei(networkEstimate),
|
|
140
140
|
cappedTo: formatGwei(maxReasonableFee),
|
|
@@ -207,7 +207,7 @@ export const P75BlobTxsOnlyPriorityFeeStrategy: PriorityFeeStrategy = {
|
|
|
207
207
|
|
|
208
208
|
// Debug: Log suspicious fees from history
|
|
209
209
|
if (medianHistoricalFee > 100n * WEI_CONST) {
|
|
210
|
-
logger?.
|
|
210
|
+
logger?.debug('Suspicious high fee in history', {
|
|
211
211
|
historicalMedian: formatGwei(medianHistoricalFee),
|
|
212
212
|
allP75Fees: percentile75Fees.map(f => formatGwei(f)),
|
|
213
213
|
});
|