From 462f441e4cfbd3ab420ff6d42eba33682b34e62b Mon Sep 17 00:00:00 2001 From: spypsy Date: Mon, 16 Dec 2024 13:17:59 +0000 Subject: [PATCH 1/7] chore: use L1 Tx Utils --- .../aztec.js/src/utils/portal_manager.ts | 95 ++++++++++++++----- .../cli/src/cmds/infrastructure/sequencers.ts | 75 +++++++++++---- yarn-project/ethereum/src/l1_tx_utils.ts | 2 +- 3 files changed, 129 insertions(+), 43 deletions(-) diff --git a/yarn-project/aztec.js/src/utils/portal_manager.ts b/yarn-project/aztec.js/src/utils/portal_manager.ts index 660a63687d2..f51690ad5c2 100644 --- a/yarn-project/aztec.js/src/utils/portal_manager.ts +++ b/yarn-project/aztec.js/src/utils/portal_manager.ts @@ -7,7 +7,7 @@ import { type SiblingPath, computeSecretHash, } from '@aztec/aztec.js'; -import { extractEvent } from '@aztec/ethereum'; +import { L1TxUtils, extractEvent } from '@aztec/ethereum'; import { sha256ToField } from '@aztec/foundation/crypto'; import { FeeJuicePortalAbi, OutboxAbi, TestERC20Abi, TokenPortalAbi } from '@aztec/l1-artifacts'; @@ -19,6 +19,7 @@ import { type HttpTransport, type PublicClient, type WalletClient, + encodeFunctionData, getContract, toFunctionSelector, } from 'viem'; @@ -59,6 +60,7 @@ export function generateClaimSecret(logger?: Logger): [Fr, Fr] { /** Helper for managing an ERC20 on L1. */ export class L1TokenManager { private contract: GetContractReturnType>; + private l1TxUtils: L1TxUtils; public constructor( /** Address of the ERC20 contract. */ @@ -72,6 +74,7 @@ export class L1TokenManager { abi: TestERC20Abi, client: this.walletClient, }); + this.l1TxUtils = new L1TxUtils(publicClient, walletClient, logger); } /** @@ -90,8 +93,13 @@ export class L1TokenManager { */ public async mint(amount: bigint, address: Hex, addressName?: string) { this.logger.info(`Minting ${amount} tokens for ${stringifyEthAddress(address, addressName)}`); - await this.publicClient.waitForTransactionReceipt({ - hash: await this.contract.write.mint([address, amount]), + await this.l1TxUtils.sendAndMonitorTransaction({ + to: this.contract.address, + data: encodeFunctionData({ + abi: this.contract.abi, + functionName: 'mint', + args: [address, amount], + }), }); } @@ -103,8 +111,13 @@ export class L1TokenManager { */ public async approve(amount: bigint, address: Hex, addressName = '') { this.logger.info(`Approving ${amount} tokens for ${stringifyEthAddress(address, addressName)}`); - await this.publicClient.waitForTransactionReceipt({ - hash: await this.contract.write.approve([address, amount]), + await this.l1TxUtils.sendAndMonitorTransaction({ + to: this.contract.address, + data: encodeFunctionData({ + abi: this.contract.abi, + functionName: 'approve', + args: [address, amount], + }), }); } } @@ -116,6 +129,7 @@ export class L1FeeJuicePortalManager { typeof FeeJuicePortalAbi, WalletClient >; + private readonly l1TxUtils: L1TxUtils; constructor( portalAddress: EthAddress, @@ -130,6 +144,7 @@ export class L1FeeJuicePortalManager { abi: FeeJuicePortalAbi, client: this.walletClient, }); + this.l1TxUtils = new L1TxUtils(publicClient, walletClient, logger); } /** Returns the associated token manager for the L1 ERC20. */ @@ -156,8 +171,13 @@ export class L1FeeJuicePortalManager { await this.contract.simulate.depositToAztecPublic(args); - const txReceipt = await this.publicClient.waitForTransactionReceipt({ - hash: await this.contract.write.depositToAztecPublic(args), + const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction({ + to: this.contract.address, + data: encodeFunctionData({ + abi: this.contract.abi, + functionName: 'depositToAztecPublic', + args, + }), }); const log = extractEvent( @@ -210,6 +230,7 @@ export class L1FeeJuicePortalManager { export class L1ToL2TokenPortalManager { protected readonly portal: GetContractReturnType>; protected readonly tokenManager: L1TokenManager; + protected readonly l1TxUtils: L1TxUtils; constructor( portalAddress: EthAddress, @@ -224,6 +245,7 @@ export class L1ToL2TokenPortalManager { abi: TokenPortalAbi, client: this.walletClient, }); + this.l1TxUtils = new L1TxUtils(publicClient, walletClient, logger); } /** Returns the token manager for the underlying L1 token. */ @@ -241,15 +263,22 @@ export class L1ToL2TokenPortalManager { const [claimSecret, claimSecretHash] = await this.bridgeSetup(amount, mint); this.logger.info('Sending L1 tokens to L2 to be claimed publicly'); - const { request } = await this.portal.simulate.depositToAztecPublic([ - to.toString(), - amount, - claimSecretHash.toString(), - ]); - - const txReceipt = await this.publicClient.waitForTransactionReceipt({ - hash: await this.walletClient.writeContract(request), - }); + const args = [to.toString(), amount, claimSecretHash.toString()] as const; + const { request } = await this.portal.simulate.depositToAztecPublic(args); + + const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction( + { + to: request.address!, + data: encodeFunctionData({ + abi: this.portal.abi, + functionName: 'depositToAztecPublic', + args, + }), + }, + { + fixedGas: request.gas, + }, + ); const log = extractEvent( txReceipt.logs, @@ -286,10 +315,13 @@ export class L1ToL2TokenPortalManager { const [claimSecret, claimSecretHash] = await this.bridgeSetup(amount, mint); this.logger.info('Sending L1 tokens to L2 to be claimed privately'); - const { request } = await this.portal.simulate.depositToAztecPrivate([amount, claimSecretHash.toString()]); - - const txReceipt = await this.publicClient.waitForTransactionReceipt({ - hash: await this.walletClient.writeContract(request), + const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction({ + to: this.portal.address, + data: encodeFunctionData({ + abi: this.portal.abi, + functionName: 'depositToAztecPrivate', + args: [amount, claimSecretHash.toString()], + }), }); const log = extractEvent( @@ -368,17 +400,30 @@ export class L1TokenPortalManager extends L1ToL2TokenPortalManager { throw new Error(`L1 to L2 message at block ${blockNumber} index ${messageIndex} has already been consumed`); } - // Call function on L1 contract to consume the message - const { request: withdrawRequest } = await this.portal.simulate.withdraw([ + const withdrawArgs = [ recipient.toString(), amount, false, BigInt(blockNumber), messageIndex, siblingPath.toBufferArray().map((buf: Buffer): Hex => `0x${buf.toString('hex')}`), - ]); - - await this.publicClient.waitForTransactionReceipt({ hash: await this.walletClient.writeContract(withdrawRequest) }); + ] as const; + // Call function on L1 contract to consume the message + const { request } = await this.portal.simulate.withdraw(withdrawArgs); + + await this.l1TxUtils.sendAndMonitorTransaction( + { + to: request.address!, + data: encodeFunctionData({ + abi: this.portal.abi, + functionName: 'withdraw', + args: withdrawArgs, + }), + }, + { + fixedGas: request.gas, + }, + ); const isConsumedAfter = await this.outbox.read.hasMessageBeenConsumedAtBlockAndIndex([blockNumber, messageIndex]); if (!isConsumedAfter) { diff --git a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts index a3e6c77d39d..d9e4f4e4e6a 100644 --- a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts +++ b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts @@ -1,9 +1,10 @@ import { createCompatibleClient } from '@aztec/aztec.js'; -import { MINIMUM_STAKE, createEthereumChain } from '@aztec/ethereum'; +import { L1TxUtils, MINIMUM_STAKE, createEthereumChain } from '@aztec/ethereum'; import { type LogFn, type Logger } from '@aztec/foundation/log'; import { RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; import { createPublicClient, createWalletClient, getContract, http } from 'viem'; +import { encodeFunctionData } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; export async function sequencers(opts: { @@ -48,6 +49,8 @@ export async function sequencers(opts: { const who = (maybeWho as `0x{string}`) ?? walletClient?.account.address.toString(); + const l1TxUtils = walletClient ? new L1TxUtils(publicClient, walletClient, debugLogger) : undefined; + if (command === 'list') { const sequencers = await rollup.read.getAttesters(); if (sequencers.length === 0) { @@ -59,8 +62,8 @@ export async function sequencers(opts: { } } } else if (command === 'add') { - if (!who || !writeableRollup || !walletClient) { - throw new Error(`Missing sequencer address`); + if (!who || !writeableRollup || !walletClient || !l1TxUtils) { + throw new Error(`Missing sequencer address or wallet configuration`); } log(`Adding ${who} as sequencer`); @@ -71,24 +74,62 @@ export async function sequencers(opts: { client: walletClient, }); - await Promise.all( - [ - await stakingAsset.write.mint([walletClient.account.address, MINIMUM_STAKE], {} as any), - await stakingAsset.write.approve([rollup.address, MINIMUM_STAKE], {} as any), - ].map(txHash => publicClient.waitForTransactionReceipt({ hash: txHash })), - ); + const mintRequest = { + to: stakingAsset.address, + data: encodeFunctionData({ + abi: stakingAsset.abi, + functionName: 'mint', + args: [walletClient.account.address, MINIMUM_STAKE], + }), + }; + const approveRequest = { + to: stakingAsset.address, + data: encodeFunctionData({ + abi: stakingAsset.abi, + functionName: 'approve', + args: [rollup.address, MINIMUM_STAKE], + }), + }; + + // Send transactions first + const [mintTxRes, approveTxRes] = await Promise.all([ + l1TxUtils.sendTransaction(mintRequest), + l1TxUtils.sendTransaction(approveRequest), + ]); + + // Monitor transactions in parallel + await Promise.all([ + l1TxUtils.monitorTransaction(mintRequest, mintTxRes.txHash, { gasLimit: mintTxRes.gasLimit }), + l1TxUtils.monitorTransaction(approveRequest, approveTxRes.txHash, { gasLimit: approveTxRes.gasLimit }), + ]); + + // Now send and monitor deposit transaction + const depositReceipt = await l1TxUtils.sendAndMonitorTransaction({ + to: writeableRollup.address, + data: encodeFunctionData({ + abi: writeableRollup.abi, + functionName: 'deposit', + args: [who, who, who, MINIMUM_STAKE], + }), + }); - const hash = await writeableRollup.write.deposit([who, who, who, MINIMUM_STAKE]); - await publicClient.waitForTransactionReceipt({ hash }); - log(`Added in tx ${hash}`); + log(`Added in tx ${depositReceipt.transactionHash}`); } else if (command === 'remove') { - if (!who || !writeableRollup) { - throw new Error(`Missing sequencer address`); + if (!who || !writeableRollup || !l1TxUtils) { + throw new Error(`Missing sequencer address or wallet configuration`); } log(`Removing ${who} as sequencer`); - const hash = await writeableRollup.write.initiateWithdraw([who, who]); - await publicClient.waitForTransactionReceipt({ hash }); - log(`Removed in tx ${hash}`); + + const receipt = await l1TxUtils.sendAndMonitorTransaction({ + to: writeableRollup.address, + data: encodeFunctionData({ + abi: writeableRollup.abi, + functionName: 'initiateWithdraw', + args: [who, who], + }), + }); + + log(`Removed in tx ${receipt.transactionHash}`); } else if (command === 'who-next') { const next = await rollup.read.getCurrentProposer(); log(`Sequencer expected to build is ${next}`); diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index b7eb9493a31..b6c3a54b352 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -206,7 +206,7 @@ export class L1TxUtils { public async monitorTransaction( request: L1TxRequest, initialTxHash: Hex, - params: { gasLimit: bigint }, + params?: { gasLimit: bigint }, _gasConfig?: Partial, _blobInputs?: L1BlobInputs, ): Promise { From 3621f0604fbda7ea82ac2476674c77cfbcf61e78 Mon Sep 17 00:00:00 2001 From: spypsy Date: Mon, 16 Dec 2024 17:38:49 +0000 Subject: [PATCH 2/7] undo change --- yarn-project/ethereum/src/l1_tx_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index b6c3a54b352..b7eb9493a31 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -206,7 +206,7 @@ export class L1TxUtils { public async monitorTransaction( request: L1TxRequest, initialTxHash: Hex, - params?: { gasLimit: bigint }, + params: { gasLimit: bigint }, _gasConfig?: Partial, _blobInputs?: L1BlobInputs, ): Promise { From 4925558d4a15ed2368b3fc145409b4ba06d18354 Mon Sep 17 00:00:00 2001 From: spypsy Date: Thu, 19 Dec 2024 09:27:42 +0000 Subject: [PATCH 3/7] PR fixes --- .../aztec.js/src/utils/portal_manager.ts | 49 +++++++------------ .../cli/src/cmds/infrastructure/sequencers.ts | 13 ++--- 2 files changed, 20 insertions(+), 42 deletions(-) diff --git a/yarn-project/aztec.js/src/utils/portal_manager.ts b/yarn-project/aztec.js/src/utils/portal_manager.ts index f51690ad5c2..ba6b2769b69 100644 --- a/yarn-project/aztec.js/src/utils/portal_manager.ts +++ b/yarn-project/aztec.js/src/utils/portal_manager.ts @@ -169,8 +169,6 @@ export class L1FeeJuicePortalManager { this.logger.info('Sending L1 Fee Juice to L2 to be claimed publicly'); const args = [to.toString(), amount, claimSecretHash.toString()] as const; - await this.contract.simulate.depositToAztecPublic(args); - const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction({ to: this.contract.address, data: encodeFunctionData({ @@ -264,21 +262,15 @@ export class L1ToL2TokenPortalManager { this.logger.info('Sending L1 tokens to L2 to be claimed publicly'); const args = [to.toString(), amount, claimSecretHash.toString()] as const; - const { request } = await this.portal.simulate.depositToAztecPublic(args); - - const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction( - { - to: request.address!, - data: encodeFunctionData({ - abi: this.portal.abi, - functionName: 'depositToAztecPublic', - args, - }), - }, - { - fixedGas: request.gas, - }, - ); + + const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction({ + to: to.toString()!, + data: encodeFunctionData({ + abi: this.portal.abi, + functionName: 'depositToAztecPublic', + args, + }), + }); const log = extractEvent( txReceipt.logs, @@ -409,21 +401,14 @@ export class L1TokenPortalManager extends L1ToL2TokenPortalManager { siblingPath.toBufferArray().map((buf: Buffer): Hex => `0x${buf.toString('hex')}`), ] as const; // Call function on L1 contract to consume the message - const { request } = await this.portal.simulate.withdraw(withdrawArgs); - - await this.l1TxUtils.sendAndMonitorTransaction( - { - to: request.address!, - data: encodeFunctionData({ - abi: this.portal.abi, - functionName: 'withdraw', - args: withdrawArgs, - }), - }, - { - fixedGas: request.gas, - }, - ); + await this.l1TxUtils.sendAndMonitorTransaction({ + to: this.portal.address, + data: encodeFunctionData({ + abi: this.portal.abi, + functionName: 'withdraw', + args: withdrawArgs, + }), + }); const isConsumedAfter = await this.outbox.read.hasMessageBeenConsumedAtBlockAndIndex([blockNumber, messageIndex]); if (!isConsumedAfter) { diff --git a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts index d9e4f4e4e6a..39070ec0c16 100644 --- a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts +++ b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts @@ -91,19 +91,12 @@ export async function sequencers(opts: { }), }; - // Send transactions first - const [mintTxRes, approveTxRes] = await Promise.all([ - l1TxUtils.sendTransaction(mintRequest), - l1TxUtils.sendTransaction(approveRequest), - ]); - - // Monitor transactions in parallel await Promise.all([ - l1TxUtils.monitorTransaction(mintRequest, mintTxRes.txHash, { gasLimit: mintTxRes.gasLimit }), - l1TxUtils.monitorTransaction(approveRequest, approveTxRes.txHash, { gasLimit: approveTxRes.gasLimit }), + l1TxUtils.sendAndMonitorTransaction(mintRequest), + l1TxUtils.sendAndMonitorTransaction(approveRequest), ]); - // Now send and monitor deposit transaction + // send and monitor deposit transaction const depositReceipt = await l1TxUtils.sendAndMonitorTransaction({ to: writeableRollup.address, data: encodeFunctionData({ From 46f27b0207652c107b2bdd5d0af679bb16519bf8 Mon Sep 17 00:00:00 2001 From: spypsy Date: Fri, 20 Dec 2024 13:29:03 +0000 Subject: [PATCH 4/7] fix tx recipient address --- yarn-project/aztec.js/src/utils/portal_manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/aztec.js/src/utils/portal_manager.ts b/yarn-project/aztec.js/src/utils/portal_manager.ts index ba6b2769b69..c2434ac61d3 100644 --- a/yarn-project/aztec.js/src/utils/portal_manager.ts +++ b/yarn-project/aztec.js/src/utils/portal_manager.ts @@ -264,7 +264,7 @@ export class L1ToL2TokenPortalManager { const args = [to.toString(), amount, claimSecretHash.toString()] as const; const txReceipt = await this.l1TxUtils.sendAndMonitorTransaction({ - to: to.toString()!, + to: this.portal.address, data: encodeFunctionData({ abi: this.portal.abi, functionName: 'depositToAztecPublic', From 790d6f29657683c0097de34321adb8bc33b3bebc Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 7 Jan 2025 14:13:21 +0000 Subject: [PATCH 5/7] remove old import --- yarn-project/cli/src/cmds/infrastructure/sequencers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts index 17129df23b6..82d347c00dc 100644 --- a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts +++ b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts @@ -1,5 +1,5 @@ import { createCompatibleClient } from '@aztec/aztec.js'; -import { L1TxUtils, MINIMUM_STAKE, createEthereumChain, getL1ContractsConfigEnvVars } from '@aztec/ethereum'; +import { L1TxUtils, createEthereumChain, getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { type LogFn, type Logger } from '@aztec/foundation/log'; import { RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; From b13e919ec41195a9264073b5b1a400b5e2512b1d Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 7 Jan 2025 14:13:42 +0000 Subject: [PATCH 6/7] single import --- yarn-project/cli/src/cmds/infrastructure/sequencers.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts index 82d347c00dc..665fa7fc9dd 100644 --- a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts +++ b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts @@ -3,8 +3,7 @@ import { L1TxUtils, createEthereumChain, getL1ContractsConfigEnvVars } from '@az import { type LogFn, type Logger } from '@aztec/foundation/log'; import { RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; -import { createPublicClient, createWalletClient, getContract, http } from 'viem'; -import { encodeFunctionData } from 'viem'; +import { createPublicClient, createWalletClient, encodeFunctionData, getContract, http } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; export async function sequencers(opts: { From 7821149c5445aadf0ed1b2a462dca9dbbce2a912 Mon Sep 17 00:00:00 2001 From: spypsy Date: Wed, 8 Jan 2025 16:59:58 +0000 Subject: [PATCH 7/7] fix outdated value --- yarn-project/cli/src/cmds/infrastructure/sequencers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts index 665fa7fc9dd..74432781dab 100644 --- a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts +++ b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts @@ -102,7 +102,7 @@ export async function sequencers(opts: { data: encodeFunctionData({ abi: writeableRollup.abi, functionName: 'deposit', - args: [who, who, who, MINIMUM_STAKE], + args: [who, who, who, config.minimumStake], }), });