@aztec/ethereum 0.70.0 → 0.72.1
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/contracts/rollup.d.ts +3 -3
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +1 -1
- package/dest/deploy_l1_contracts.d.ts +19756 -68
- package/dest/deploy_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_l1_contracts.js +36 -61
- package/dest/eth_cheat_codes.d.ts +1 -1
- package/dest/eth_cheat_codes.d.ts.map +1 -1
- package/dest/eth_cheat_codes.js +1 -1
- package/dest/l1_tx_utils.d.ts +9 -3
- package/dest/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils.js +80 -21
- package/dest/utils.d.ts +11 -1
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +58 -4
- package/package.json +4 -4
- package/src/contracts/rollup.ts +1 -1
- package/src/deploy_l1_contracts.ts +60 -139
- package/src/eth_cheat_codes.ts +1 -1
- package/src/l1_tx_utils.ts +91 -22
- package/src/utils.ts +65 -5
|
@@ -109,121 +109,67 @@ export interface ContractArtifacts {
|
|
|
109
109
|
libraries?: Libraries;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
* All L1 Contract Artifacts for deployment
|
|
114
|
-
*/
|
|
115
|
-
export interface L1ContractArtifactsForDeployment {
|
|
116
|
-
/**
|
|
117
|
-
* Inbox contract artifacts
|
|
118
|
-
*/
|
|
119
|
-
inbox: ContractArtifacts;
|
|
120
|
-
/**
|
|
121
|
-
* Outbox contract artifacts
|
|
122
|
-
*/
|
|
123
|
-
outbox: ContractArtifacts;
|
|
124
|
-
/**
|
|
125
|
-
* Registry contract artifacts
|
|
126
|
-
*/
|
|
127
|
-
registry: ContractArtifacts;
|
|
128
|
-
/**
|
|
129
|
-
* Rollup contract artifacts
|
|
130
|
-
*/
|
|
131
|
-
rollup: ContractArtifacts;
|
|
132
|
-
/**
|
|
133
|
-
* The token to stake.
|
|
134
|
-
*/
|
|
135
|
-
stakingAsset: ContractArtifacts;
|
|
136
|
-
/**
|
|
137
|
-
* The token to pay for gas. This will be bridged to L2 via the feeJuicePortal below
|
|
138
|
-
*/
|
|
139
|
-
feeAsset: ContractArtifacts;
|
|
140
|
-
/**
|
|
141
|
-
* Fee juice portal contract artifacts. Optional for now as gas is not strictly enforced
|
|
142
|
-
*/
|
|
143
|
-
feeJuicePortal: ContractArtifacts;
|
|
144
|
-
/**
|
|
145
|
-
* CoinIssuer contract artifacts.
|
|
146
|
-
*/
|
|
147
|
-
coinIssuer: ContractArtifacts;
|
|
148
|
-
/**
|
|
149
|
-
* RewardDistributor contract artifacts.
|
|
150
|
-
*/
|
|
151
|
-
rewardDistributor: ContractArtifacts;
|
|
152
|
-
/**
|
|
153
|
-
* GovernanceProposer contract artifacts.
|
|
154
|
-
*/
|
|
155
|
-
governanceProposer: ContractArtifacts;
|
|
156
|
-
/**
|
|
157
|
-
* Governance contract artifacts.
|
|
158
|
-
*/
|
|
159
|
-
governance: ContractArtifacts;
|
|
160
|
-
/**
|
|
161
|
-
* SlashFactory contract artifacts.
|
|
162
|
-
*/
|
|
163
|
-
slashFactory: ContractArtifacts;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export const l1Artifacts: L1ContractArtifactsForDeployment = {
|
|
112
|
+
export const l1Artifacts = {
|
|
167
113
|
registry: {
|
|
168
114
|
contractAbi: RegistryAbi,
|
|
169
|
-
contractBytecode: RegistryBytecode,
|
|
115
|
+
contractBytecode: RegistryBytecode as Hex,
|
|
170
116
|
},
|
|
171
117
|
inbox: {
|
|
172
118
|
contractAbi: InboxAbi,
|
|
173
|
-
contractBytecode: InboxBytecode,
|
|
119
|
+
contractBytecode: InboxBytecode as Hex,
|
|
174
120
|
},
|
|
175
121
|
outbox: {
|
|
176
122
|
contractAbi: OutboxAbi,
|
|
177
|
-
contractBytecode: OutboxBytecode,
|
|
123
|
+
contractBytecode: OutboxBytecode as Hex,
|
|
178
124
|
},
|
|
179
125
|
rollup: {
|
|
180
126
|
contractAbi: RollupAbi,
|
|
181
|
-
contractBytecode: RollupBytecode,
|
|
127
|
+
contractBytecode: RollupBytecode as Hex,
|
|
182
128
|
libraries: {
|
|
183
129
|
linkReferences: RollupLinkReferences,
|
|
184
130
|
libraryCode: {
|
|
185
131
|
LeonidasLib: {
|
|
186
132
|
contractAbi: LeonidasLibAbi,
|
|
187
|
-
contractBytecode: LeonidasLibBytecode,
|
|
133
|
+
contractBytecode: LeonidasLibBytecode as Hex,
|
|
188
134
|
},
|
|
189
135
|
ExtRollupLib: {
|
|
190
136
|
contractAbi: ExtRollupLibAbi,
|
|
191
|
-
contractBytecode: ExtRollupLibBytecode,
|
|
137
|
+
contractBytecode: ExtRollupLibBytecode as Hex,
|
|
192
138
|
},
|
|
193
139
|
},
|
|
194
140
|
},
|
|
195
141
|
},
|
|
196
142
|
stakingAsset: {
|
|
197
143
|
contractAbi: TestERC20Abi,
|
|
198
|
-
contractBytecode: TestERC20Bytecode,
|
|
144
|
+
contractBytecode: TestERC20Bytecode as Hex,
|
|
199
145
|
},
|
|
200
146
|
feeAsset: {
|
|
201
147
|
contractAbi: TestERC20Abi,
|
|
202
|
-
contractBytecode: TestERC20Bytecode,
|
|
148
|
+
contractBytecode: TestERC20Bytecode as Hex,
|
|
203
149
|
},
|
|
204
150
|
feeJuicePortal: {
|
|
205
151
|
contractAbi: FeeJuicePortalAbi,
|
|
206
|
-
contractBytecode: FeeJuicePortalBytecode,
|
|
152
|
+
contractBytecode: FeeJuicePortalBytecode as Hex,
|
|
207
153
|
},
|
|
208
154
|
rewardDistributor: {
|
|
209
155
|
contractAbi: RewardDistributorAbi,
|
|
210
|
-
contractBytecode: RewardDistributorBytecode,
|
|
156
|
+
contractBytecode: RewardDistributorBytecode as Hex,
|
|
211
157
|
},
|
|
212
158
|
coinIssuer: {
|
|
213
159
|
contractAbi: CoinIssuerAbi,
|
|
214
|
-
contractBytecode: CoinIssuerBytecode,
|
|
160
|
+
contractBytecode: CoinIssuerBytecode as Hex,
|
|
215
161
|
},
|
|
216
162
|
governanceProposer: {
|
|
217
163
|
contractAbi: GovernanceProposerAbi,
|
|
218
|
-
contractBytecode: GovernanceProposerBytecode,
|
|
164
|
+
contractBytecode: GovernanceProposerBytecode as Hex,
|
|
219
165
|
},
|
|
220
166
|
governance: {
|
|
221
167
|
contractAbi: GovernanceAbi,
|
|
222
|
-
contractBytecode: GovernanceBytecode,
|
|
168
|
+
contractBytecode: GovernanceBytecode as Hex,
|
|
223
169
|
},
|
|
224
170
|
slashFactory: {
|
|
225
171
|
contractAbi: SlashFactoryAbi,
|
|
226
|
-
contractBytecode: SlashFactoryBytecode,
|
|
172
|
+
contractBytecode: SlashFactoryBytecode as Hex,
|
|
227
173
|
},
|
|
228
174
|
};
|
|
229
175
|
|
|
@@ -400,6 +346,8 @@ export const deployL1Contracts = async (
|
|
|
400
346
|
account.address.toString(),
|
|
401
347
|
rollupConfigArgs,
|
|
402
348
|
];
|
|
349
|
+
await deployer.waitForDeployments();
|
|
350
|
+
|
|
403
351
|
const rollupAddress = await deployer.deploy(l1Artifacts.rollup, rollupArgs);
|
|
404
352
|
logger.verbose(`Deployed Rollup at ${rollupAddress}`, rollupConfigArgs);
|
|
405
353
|
|
|
@@ -443,32 +391,49 @@ export const deployL1Contracts = async (
|
|
|
443
391
|
}
|
|
444
392
|
|
|
445
393
|
if (args.initialValidators && args.initialValidators.length > 0) {
|
|
446
|
-
//
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
[
|
|
450
|
-
await stakingAsset.write.mint([walletClient.account.address, stakeNeeded], {} as any),
|
|
451
|
-
await stakingAsset.write.approve([rollupAddress.toString(), stakeNeeded], {} as any),
|
|
452
|
-
].map(txHash => publicClient.waitForTransactionReceipt({ hash: txHash })),
|
|
394
|
+
// Check if some of the initial validators are already registered, so we support idempotent deployments
|
|
395
|
+
const validatorsInfo = await Promise.all(
|
|
396
|
+
args.initialValidators.map(async address => ({ address, ...(await rollup.read.getInfo([address.toString()])) })),
|
|
453
397
|
);
|
|
398
|
+
const existingValidators = validatorsInfo.filter(v => v.status !== 0);
|
|
399
|
+
if (existingValidators.length > 0) {
|
|
400
|
+
logger.warn(
|
|
401
|
+
`Validators ${existingValidators.map(v => v.address).join(', ')} already exist. Skipping from initialization.`,
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const newValidatorsAddresses = validatorsInfo.filter(v => v.status === 0).map(v => v.address.toString());
|
|
454
406
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
407
|
+
if (newValidatorsAddresses.length > 0) {
|
|
408
|
+
// Mint tokens, approve them, use cheat code to initialise validator set without setting up the epoch.
|
|
409
|
+
const stakeNeeded = args.minimumStake * BigInt(newValidatorsAddresses.length);
|
|
410
|
+
await Promise.all(
|
|
411
|
+
[
|
|
412
|
+
await stakingAsset.write.mint([walletClient.account.address, stakeNeeded], {} as any),
|
|
413
|
+
await stakingAsset.write.approve([rollupAddress.toString(), stakeNeeded], {} as any),
|
|
414
|
+
].map(txHash => publicClient.waitForTransactionReceipt({ hash: txHash })),
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
const initiateValidatorSetTxHash = await rollup.write.cheat__InitialiseValidatorSet([
|
|
418
|
+
newValidatorsAddresses.map(v => ({
|
|
419
|
+
attester: v,
|
|
420
|
+
proposer: v,
|
|
421
|
+
withdrawer: v,
|
|
422
|
+
amount: args.minimumStake,
|
|
423
|
+
})),
|
|
424
|
+
]);
|
|
425
|
+
txHashes.push(initiateValidatorSetTxHash);
|
|
426
|
+
logger.info(
|
|
427
|
+
`Initialized validator set (${newValidatorsAddresses.join(', ')}) in tx ${initiateValidatorSetTxHash}`,
|
|
428
|
+
);
|
|
429
|
+
}
|
|
465
430
|
}
|
|
466
431
|
|
|
467
432
|
// @note This value MUST match what is in `constants.nr`. It is currently specified here instead of just importing
|
|
468
433
|
// because there is circular dependency hell. This is a temporary solution. #3342
|
|
469
434
|
// @todo #8084
|
|
470
435
|
// fund the portal contract with Fee Juice
|
|
471
|
-
const FEE_JUICE_INITIAL_MINT =
|
|
436
|
+
const FEE_JUICE_INITIAL_MINT = 200000000000000000000000n;
|
|
472
437
|
const mintTxHash = await feeAsset.write.mint([feeJuicePortalAddress.toString(), FEE_JUICE_INITIAL_MINT], {} as any);
|
|
473
438
|
|
|
474
439
|
// @note This is used to ensure we fully wait for the transaction when running against a real chain
|
|
@@ -476,8 +441,8 @@ export const deployL1Contracts = async (
|
|
|
476
441
|
await publicClient.waitForTransactionReceipt({ hash: mintTxHash });
|
|
477
442
|
logger.verbose(`Funding fee juice portal contract with fee juice in ${mintTxHash}`);
|
|
478
443
|
|
|
479
|
-
if (!(await feeJuicePortal.read.initialized(
|
|
480
|
-
const initPortalTxHash = await feeJuicePortal.write.initialize(
|
|
444
|
+
if (!(await feeJuicePortal.read.initialized())) {
|
|
445
|
+
const initPortalTxHash = await feeJuicePortal.write.initialize();
|
|
481
446
|
txHashes.push(initPortalTxHash);
|
|
482
447
|
logger.verbose(`Fee juice portal initializing in tx ${initPortalTxHash}`);
|
|
483
448
|
} else {
|
|
@@ -493,13 +458,13 @@ export const deployL1Contracts = async (
|
|
|
493
458
|
// The edge case being that the genesis block is already occupying slot 0, so we cannot have another block.
|
|
494
459
|
try {
|
|
495
460
|
// Need to get the time
|
|
496
|
-
const currentSlot = (await rollup.read.getCurrentSlot(
|
|
461
|
+
const currentSlot = (await rollup.read.getCurrentSlot()) as bigint;
|
|
497
462
|
|
|
498
463
|
if (BigInt(currentSlot) === 0n) {
|
|
499
|
-
const ts = Number(await rollup.read.getTimestampForSlot([
|
|
464
|
+
const ts = Number(await rollup.read.getTimestampForSlot([1n]));
|
|
500
465
|
await rpcCall('evm_setNextBlockTimestamp', [ts]);
|
|
501
466
|
await rpcCall('hardhat_mine', [1]);
|
|
502
|
-
const currentSlot = (await rollup.read.getCurrentSlot(
|
|
467
|
+
const currentSlot = (await rollup.read.getCurrentSlot()) as bigint;
|
|
503
468
|
|
|
504
469
|
if (BigInt(currentSlot) !== 1n) {
|
|
505
470
|
throw new Error(`Error jumping time: current slot is ${currentSlot}`);
|
|
@@ -518,10 +483,10 @@ export const deployL1Contracts = async (
|
|
|
518
483
|
}
|
|
519
484
|
|
|
520
485
|
// Inbox and Outbox are immutable and are deployed from Rollup's constructor so we just fetch them from the contract.
|
|
521
|
-
const inboxAddress = EthAddress.fromString((await rollup.read.INBOX(
|
|
486
|
+
const inboxAddress = EthAddress.fromString((await rollup.read.INBOX()) as any);
|
|
522
487
|
logger.verbose(`Inbox available at ${inboxAddress}`);
|
|
523
488
|
|
|
524
|
-
const outboxAddress = EthAddress.fromString((await rollup.read.OUTBOX(
|
|
489
|
+
const outboxAddress = EthAddress.fromString((await rollup.read.OUTBOX()) as any);
|
|
525
490
|
logger.verbose(`Outbox available at ${outboxAddress}`);
|
|
526
491
|
|
|
527
492
|
// We need to call a function on the registry to set the various contract addresses.
|
|
@@ -541,7 +506,7 @@ export const deployL1Contracts = async (
|
|
|
541
506
|
}
|
|
542
507
|
|
|
543
508
|
// If the owner is not the Governance contract, transfer ownership to the Governance contract
|
|
544
|
-
if ((await registryContract.read.owner(
|
|
509
|
+
if ((await registryContract.read.owner()) !== getAddress(governanceAddress.toString())) {
|
|
545
510
|
const transferOwnershipTxHash = await registryContract.write.transferOwnership(
|
|
546
511
|
[getAddress(governanceAddress.toString())],
|
|
547
512
|
{
|
|
@@ -616,50 +581,6 @@ class L1Deployer {
|
|
|
616
581
|
}
|
|
617
582
|
}
|
|
618
583
|
|
|
619
|
-
/**
|
|
620
|
-
* Compiles a contract source code using the provided solc compiler.
|
|
621
|
-
* @param fileName - Contract file name (eg UltraHonkVerifier.sol)
|
|
622
|
-
* @param contractName - Contract name within the file (eg HonkVerifier)
|
|
623
|
-
* @param source - Source code to compile
|
|
624
|
-
* @param solc - Solc instance
|
|
625
|
-
* @returns ABI and bytecode of the compiled contract
|
|
626
|
-
*/
|
|
627
|
-
export function compileContract(
|
|
628
|
-
fileName: string,
|
|
629
|
-
contractName: string,
|
|
630
|
-
source: string,
|
|
631
|
-
solc: { compile: (source: string) => string },
|
|
632
|
-
): { abi: Narrow<Abi | readonly unknown[]>; bytecode: Hex } {
|
|
633
|
-
const input = {
|
|
634
|
-
language: 'Solidity',
|
|
635
|
-
sources: {
|
|
636
|
-
[fileName]: {
|
|
637
|
-
content: source,
|
|
638
|
-
},
|
|
639
|
-
},
|
|
640
|
-
settings: {
|
|
641
|
-
// we require the optimizer
|
|
642
|
-
optimizer: {
|
|
643
|
-
enabled: true,
|
|
644
|
-
runs: 200,
|
|
645
|
-
},
|
|
646
|
-
evmVersion: 'cancun',
|
|
647
|
-
outputSelection: {
|
|
648
|
-
'*': {
|
|
649
|
-
'*': ['evm.bytecode.object', 'abi'],
|
|
650
|
-
},
|
|
651
|
-
},
|
|
652
|
-
},
|
|
653
|
-
};
|
|
654
|
-
|
|
655
|
-
const output = JSON.parse(solc.compile(JSON.stringify(input)));
|
|
656
|
-
|
|
657
|
-
const abi = output.contracts[fileName][contractName].abi;
|
|
658
|
-
const bytecode: `0x${string}` = `0x${output.contracts[fileName][contractName].evm.bytecode.object}`;
|
|
659
|
-
|
|
660
|
-
return { abi, bytecode };
|
|
661
|
-
}
|
|
662
|
-
|
|
663
584
|
// docs:start:deployL1Contract
|
|
664
585
|
/**
|
|
665
586
|
* Helper function to deploy ETH contracts.
|
package/src/eth_cheat_codes.ts
CHANGED
|
@@ -126,7 +126,7 @@ export class EthCheatCodes {
|
|
|
126
126
|
* Set the next block base fee per gas
|
|
127
127
|
* @param baseFee - The base fee to set
|
|
128
128
|
*/
|
|
129
|
-
public async setNextBlockBaseFeePerGas(baseFee: bigint): Promise<void> {
|
|
129
|
+
public async setNextBlockBaseFeePerGas(baseFee: bigint | number): Promise<void> {
|
|
130
130
|
const res = await this.rpcCall('anvil_setNextBlockBaseFeePerGas', [baseFee.toString()]);
|
|
131
131
|
if (res.error) {
|
|
132
132
|
throw new Error(`Error setting next block base fee per gas: ${res.error.message}`);
|
package/src/l1_tx_utils.ts
CHANGED
|
@@ -12,11 +12,15 @@ import { sleep } from '@aztec/foundation/sleep';
|
|
|
12
12
|
import {
|
|
13
13
|
type Account,
|
|
14
14
|
type Address,
|
|
15
|
+
type BlockOverrides,
|
|
15
16
|
type Chain,
|
|
16
17
|
type GetTransactionReturnType,
|
|
17
18
|
type Hex,
|
|
18
19
|
type HttpTransport,
|
|
20
|
+
MethodNotFoundRpcError,
|
|
21
|
+
MethodNotSupportedRpcError,
|
|
19
22
|
type PublicClient,
|
|
23
|
+
type StateOverride,
|
|
20
24
|
type TransactionReceipt,
|
|
21
25
|
type WalletClient,
|
|
22
26
|
formatGwei,
|
|
@@ -95,9 +99,9 @@ export interface L1TxUtilsConfig {
|
|
|
95
99
|
|
|
96
100
|
export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
|
|
97
101
|
gasLimitBufferPercentage: {
|
|
98
|
-
description: 'How much to increase gas
|
|
102
|
+
description: 'How much to increase calculated gas limit by (percentage)',
|
|
99
103
|
env: 'L1_GAS_LIMIT_BUFFER_PERCENTAGE',
|
|
100
|
-
...numberConfigHelper(
|
|
104
|
+
...numberConfigHelper(20),
|
|
101
105
|
},
|
|
102
106
|
minGwei: {
|
|
103
107
|
description: 'Minimum gas price in gwei',
|
|
@@ -199,7 +203,7 @@ export class L1TxUtils {
|
|
|
199
203
|
*/
|
|
200
204
|
public async sendTransaction(
|
|
201
205
|
request: L1TxRequest,
|
|
202
|
-
_gasConfig?: Partial<L1TxUtilsConfig> & {
|
|
206
|
+
_gasConfig?: Partial<L1TxUtilsConfig> & { gasLimit?: bigint; txTimeoutAt?: Date },
|
|
203
207
|
blobInputs?: L1BlobInputs,
|
|
204
208
|
): Promise<{ txHash: Hex; gasLimit: bigint; gasPrice: GasPrice }> {
|
|
205
209
|
try {
|
|
@@ -207,8 +211,8 @@ export class L1TxUtils {
|
|
|
207
211
|
const account = this.walletClient.account;
|
|
208
212
|
let gasLimit: bigint;
|
|
209
213
|
|
|
210
|
-
if (gasConfig.
|
|
211
|
-
gasLimit = gasConfig.
|
|
214
|
+
if (gasConfig.gasLimit) {
|
|
215
|
+
gasLimit = gasConfig.gasLimit;
|
|
212
216
|
} else {
|
|
213
217
|
gasLimit = await this.estimateGas(account, request);
|
|
214
218
|
}
|
|
@@ -246,9 +250,9 @@ export class L1TxUtils {
|
|
|
246
250
|
|
|
247
251
|
return { txHash, gasLimit, gasPrice };
|
|
248
252
|
} catch (err: any) {
|
|
249
|
-
const
|
|
250
|
-
this.logger?.error(`Failed to send transaction`,
|
|
251
|
-
throw
|
|
253
|
+
const viemError = formatViemError(err);
|
|
254
|
+
this.logger?.error(`Failed to send L1 transaction`, viemError.message, { metaMessages: viemError.metaMessages });
|
|
255
|
+
throw viemError;
|
|
252
256
|
}
|
|
253
257
|
}
|
|
254
258
|
|
|
@@ -306,15 +310,16 @@ export class L1TxUtils {
|
|
|
306
310
|
try {
|
|
307
311
|
const receipt = await this.publicClient.getTransactionReceipt({ hash });
|
|
308
312
|
if (receipt) {
|
|
309
|
-
this.logger?.debug(`L1 transaction ${hash} mined`);
|
|
310
313
|
if (receipt.status === 'reverted') {
|
|
311
|
-
this.logger?.error(`L1 transaction ${hash} reverted
|
|
314
|
+
this.logger?.error(`L1 transaction ${hash} reverted`, receipt);
|
|
315
|
+
} else {
|
|
316
|
+
this.logger?.debug(`L1 transaction ${hash} mined`);
|
|
312
317
|
}
|
|
313
318
|
return receipt;
|
|
314
319
|
}
|
|
315
320
|
} catch (err) {
|
|
316
321
|
if (err instanceof Error && err.message.includes('reverted')) {
|
|
317
|
-
throw err;
|
|
322
|
+
throw formatViemError(err);
|
|
318
323
|
}
|
|
319
324
|
}
|
|
320
325
|
}
|
|
@@ -382,16 +387,20 @@ export class L1TxUtils {
|
|
|
382
387
|
}
|
|
383
388
|
await sleep(gasConfig.checkIntervalMs!);
|
|
384
389
|
} catch (err: any) {
|
|
385
|
-
const
|
|
386
|
-
this.logger?.warn(`Error monitoring
|
|
387
|
-
if (
|
|
388
|
-
throw
|
|
390
|
+
const viemError = formatViemError(err);
|
|
391
|
+
this.logger?.warn(`Error monitoring L1 transaction ${currentTxHash}:`, viemError.message);
|
|
392
|
+
if (viemError.message?.includes('reverted')) {
|
|
393
|
+
throw viemError;
|
|
389
394
|
}
|
|
390
395
|
await sleep(gasConfig.checkIntervalMs!);
|
|
391
396
|
}
|
|
392
397
|
// Check if tx has timed out.
|
|
393
398
|
txTimedOut = isTimedOut();
|
|
394
399
|
}
|
|
400
|
+
this.logger?.error(`L1 transaction ${currentTxHash} timed out`, {
|
|
401
|
+
txHash: currentTxHash,
|
|
402
|
+
...tx,
|
|
403
|
+
});
|
|
395
404
|
throw new Error(`L1 transaction ${currentTxHash} timed out`);
|
|
396
405
|
}
|
|
397
406
|
|
|
@@ -403,7 +412,7 @@ export class L1TxUtils {
|
|
|
403
412
|
*/
|
|
404
413
|
public async sendAndMonitorTransaction(
|
|
405
414
|
request: L1TxRequest,
|
|
406
|
-
gasConfig?: Partial<L1TxUtilsConfig> & {
|
|
415
|
+
gasConfig?: Partial<L1TxUtilsConfig> & { gasLimit?: bigint; txTimeoutAt?: Date },
|
|
407
416
|
blobInputs?: L1BlobInputs,
|
|
408
417
|
): Promise<{ receipt: TransactionReceipt; gasPrice: GasPrice }> {
|
|
409
418
|
const { txHash, gasLimit, gasPrice } = await this.sendTransaction(request, gasConfig, blobInputs);
|
|
@@ -429,14 +438,14 @@ export class L1TxUtils {
|
|
|
429
438
|
try {
|
|
430
439
|
const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' });
|
|
431
440
|
blobBaseFee = BigInt(blobBaseFeeHex);
|
|
432
|
-
this.logger?.debug('Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) });
|
|
441
|
+
this.logger?.debug('L1 Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) });
|
|
433
442
|
} catch {
|
|
434
|
-
this.logger?.warn('Failed to get blob base fee', attempt);
|
|
443
|
+
this.logger?.warn('Failed to get L1 blob base fee', attempt);
|
|
435
444
|
}
|
|
436
445
|
|
|
437
446
|
let priorityFee: bigint;
|
|
438
447
|
if (gasConfig.fixedPriorityFeePerGas) {
|
|
439
|
-
this.logger?.debug('Using fixed priority fee per gas', {
|
|
448
|
+
this.logger?.debug('Using fixed priority fee per L1 gas', {
|
|
440
449
|
fixedPriorityFeePerGas: gasConfig.fixedPriorityFeePerGas,
|
|
441
450
|
});
|
|
442
451
|
// try to maintain precision up to 1000000 wei
|
|
@@ -514,7 +523,7 @@ export class L1TxUtils {
|
|
|
514
523
|
maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee;
|
|
515
524
|
}
|
|
516
525
|
|
|
517
|
-
this.logger?.debug(`Computed gas price`, {
|
|
526
|
+
this.logger?.debug(`Computed L1 gas price`, {
|
|
518
527
|
attempt,
|
|
519
528
|
baseFee: formatGwei(baseFee),
|
|
520
529
|
maxFeePerGas: formatGwei(maxFeePerGas),
|
|
@@ -553,14 +562,74 @@ export class L1TxUtils {
|
|
|
553
562
|
maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
|
|
554
563
|
})
|
|
555
564
|
)?.gas;
|
|
565
|
+
this.logger?.debug('L1 gas used in estimateGas by blob tx', { gas: initialEstimate });
|
|
556
566
|
} else {
|
|
557
567
|
initialEstimate = await this.publicClient.estimateGas({ account, ...request });
|
|
568
|
+
this.logger?.debug('L1 gas used in estimateGas by non-blob tx', { gas: initialEstimate });
|
|
558
569
|
}
|
|
559
570
|
|
|
560
571
|
// Add buffer based on either fixed amount or percentage
|
|
561
|
-
const withBuffer =
|
|
562
|
-
initialEstimate + (initialEstimate * BigInt((gasConfig.gasLimitBufferPercentage || 0) * 1_00)) / 100_00n;
|
|
572
|
+
const withBuffer = this.bumpGasLimit(initialEstimate, gasConfig);
|
|
563
573
|
|
|
564
574
|
return withBuffer;
|
|
565
575
|
}
|
|
576
|
+
|
|
577
|
+
public async simulateGasUsed(
|
|
578
|
+
request: L1TxRequest & { gas?: bigint },
|
|
579
|
+
blockOverrides: BlockOverrides<bigint, number> = {},
|
|
580
|
+
stateOverrides: StateOverride = [],
|
|
581
|
+
_gasConfig?: L1TxUtilsConfig & { fallbackGasEstimate?: bigint },
|
|
582
|
+
): Promise<bigint> {
|
|
583
|
+
const gasConfig = { ...this.config, ..._gasConfig };
|
|
584
|
+
const gasPrice = await this.getGasPrice(gasConfig, false);
|
|
585
|
+
|
|
586
|
+
const nonce = await this.publicClient.getTransactionCount({ address: this.walletClient.account.address });
|
|
587
|
+
|
|
588
|
+
try {
|
|
589
|
+
const result = await this.publicClient.simulate({
|
|
590
|
+
validation: true,
|
|
591
|
+
blocks: [
|
|
592
|
+
{
|
|
593
|
+
blockOverrides,
|
|
594
|
+
stateOverrides,
|
|
595
|
+
calls: [
|
|
596
|
+
{
|
|
597
|
+
from: this.walletClient.account.address,
|
|
598
|
+
to: request.to!,
|
|
599
|
+
data: request.data,
|
|
600
|
+
maxFeePerGas: gasPrice.maxFeePerGas,
|
|
601
|
+
maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
|
|
602
|
+
gas: request.gas ?? 10_000_000n,
|
|
603
|
+
nonce,
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
});
|
|
609
|
+
this.logger?.debug(`L1 gas used in simulation: ${result[0].calls[0].gasUsed}`, {
|
|
610
|
+
result,
|
|
611
|
+
});
|
|
612
|
+
if (result[0].calls[0].status === 'failure') {
|
|
613
|
+
this.logger?.error('L1 transaction Simulation failed', {
|
|
614
|
+
error: result[0].calls[0].error,
|
|
615
|
+
});
|
|
616
|
+
throw new Error(`L1 transaction simulation failed with error: ${result[0].calls[0].error.message}`);
|
|
617
|
+
}
|
|
618
|
+
return result[0].gasUsed;
|
|
619
|
+
} catch (err) {
|
|
620
|
+
if (err instanceof MethodNotFoundRpcError || err instanceof MethodNotSupportedRpcError) {
|
|
621
|
+
this.logger?.error('Node does not support eth_simulateV1 API');
|
|
622
|
+
if (gasConfig.fallbackGasEstimate) {
|
|
623
|
+
this.logger?.debug(`Using fallback gas estimate: ${gasConfig.fallbackGasEstimate}`);
|
|
624
|
+
return gasConfig.fallbackGasEstimate;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
throw err;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
public bumpGasLimit(gasLimit: bigint, _gasConfig?: L1TxUtilsConfig): bigint {
|
|
632
|
+
const gasConfig = { ...this.config, ..._gasConfig };
|
|
633
|
+
return gasLimit + (gasLimit * BigInt((gasConfig?.gasLimitBufferPercentage || 0) * 1_00)) / 100_00n;
|
|
634
|
+
}
|
|
566
635
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Fr } from '@aztec/foundation/fields';
|
|
2
2
|
import { type Logger } from '@aztec/foundation/log';
|
|
3
|
+
import { ErrorsAbi } from '@aztec/l1-artifacts';
|
|
3
4
|
|
|
4
5
|
import {
|
|
5
6
|
type Abi,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
type DecodeEventLogReturnType,
|
|
10
11
|
type Hex,
|
|
11
12
|
type Log,
|
|
13
|
+
decodeErrorResult,
|
|
12
14
|
decodeEventLog,
|
|
13
15
|
} from 'viem';
|
|
14
16
|
|
|
@@ -19,6 +21,16 @@ export interface L2Claim {
|
|
|
19
21
|
messageLeafIndex: bigint;
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
export class FormattedViemError extends Error {
|
|
25
|
+
metaMessages?: any[];
|
|
26
|
+
|
|
27
|
+
constructor(message: string, metaMessages?: any[]) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'FormattedViemError';
|
|
30
|
+
this.metaMessages = metaMessages;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
export function extractEvent<
|
|
23
35
|
const TAbi extends Abi | readonly unknown[],
|
|
24
36
|
TEventName extends ContractEventName<TAbi>,
|
|
@@ -80,7 +92,56 @@ export function prettyLogViemErrorMsg(err: any) {
|
|
|
80
92
|
return err?.message ?? err;
|
|
81
93
|
}
|
|
82
94
|
|
|
83
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Formats a Viem error into a FormattedViemError instance.
|
|
97
|
+
* @param error - The error to format.
|
|
98
|
+
* @param abi - The ABI to use for decoding.
|
|
99
|
+
* @returns A FormattedViemError instance.
|
|
100
|
+
*/
|
|
101
|
+
export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViemError {
|
|
102
|
+
// If error is already a FormattedViemError, return it as is
|
|
103
|
+
if (error instanceof FormattedViemError) {
|
|
104
|
+
return error;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// First try to decode as a custom error using the ABI
|
|
108
|
+
try {
|
|
109
|
+
if (error?.data) {
|
|
110
|
+
// Try to decode the error data using the ABI
|
|
111
|
+
const decoded = decodeErrorResult({
|
|
112
|
+
abi,
|
|
113
|
+
data: error.data as Hex,
|
|
114
|
+
});
|
|
115
|
+
if (decoded) {
|
|
116
|
+
return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// If it's a BaseError, try to get the custom error through ContractFunctionRevertedError
|
|
121
|
+
if (error instanceof BaseError) {
|
|
122
|
+
const revertError = error.walk(err => err instanceof ContractFunctionRevertedError);
|
|
123
|
+
if (revertError instanceof ContractFunctionRevertedError) {
|
|
124
|
+
let errorName = revertError.data?.errorName;
|
|
125
|
+
if (!errorName) {
|
|
126
|
+
errorName = revertError.signature ?? '';
|
|
127
|
+
}
|
|
128
|
+
const args =
|
|
129
|
+
revertError.metaMessages && revertError.metaMessages?.length > 1
|
|
130
|
+
? revertError.metaMessages[1].trimStart()
|
|
131
|
+
: '';
|
|
132
|
+
return new FormattedViemError(`${errorName}${args}`, error?.metaMessages);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} catch (decodeErr) {
|
|
136
|
+
// If decoding fails, we fall back to the original formatting
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// If it's a regular Error instance, return it with its message
|
|
140
|
+
if (error instanceof Error) {
|
|
141
|
+
return new FormattedViemError(error.message);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Original formatting logic for non-custom errors
|
|
84
145
|
const truncateHex = (hex: string, length = 100) => {
|
|
85
146
|
if (!hex || typeof hex !== 'string') {
|
|
86
147
|
return hex;
|
|
@@ -168,8 +229,7 @@ export function formatViemError(error: any): string {
|
|
|
168
229
|
return result;
|
|
169
230
|
};
|
|
170
231
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
);
|
|
232
|
+
const formattedRes = extractAndFormatRequestBody(error?.message || String(error));
|
|
233
|
+
|
|
234
|
+
return new FormattedViemError(formattedRes.replace(/\\n/g, '\n'), error?.metaMessages);
|
|
175
235
|
}
|