Skip to content

Commit

Permalink
chore: use L1 Tx Utils (#10759)
Browse files Browse the repository at this point in the history
Fixes #10464
  • Loading branch information
spypsy authored Jan 10, 2025
1 parent f034e2a commit ccf28f5
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 43 deletions.
80 changes: 55 additions & 25 deletions yarn-project/aztec.js/src/utils/portal_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -19,6 +19,7 @@ import {
type HttpTransport,
type PublicClient,
type WalletClient,
encodeFunctionData,
getContract,
toFunctionSelector,
} from 'viem';
Expand Down Expand Up @@ -59,6 +60,7 @@ export function generateClaimSecret(logger?: Logger): [Fr, Fr] {
/** Helper for managing an ERC20 on L1. */
export class L1TokenManager {
private contract: GetContractReturnType<typeof TestERC20Abi, WalletClient<HttpTransport, Chain, Account>>;
private l1TxUtils: L1TxUtils;

public constructor(
/** Address of the ERC20 contract. */
Expand All @@ -72,6 +74,7 @@ export class L1TokenManager {
abi: TestERC20Abi,
client: this.walletClient,
});
this.l1TxUtils = new L1TxUtils(publicClient, walletClient, logger);
}

/**
Expand All @@ -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],
}),
});
}

Expand All @@ -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],
}),
});
}
}
Expand All @@ -116,6 +129,7 @@ export class L1FeeJuicePortalManager {
typeof FeeJuicePortalAbi,
WalletClient<HttpTransport, Chain, Account>
>;
private readonly l1TxUtils: L1TxUtils;

constructor(
portalAddress: EthAddress,
Expand All @@ -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. */
Expand All @@ -154,10 +169,13 @@ 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.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(
Expand Down Expand Up @@ -210,6 +228,7 @@ export class L1FeeJuicePortalManager {
export class L1ToL2TokenPortalManager {
protected readonly portal: GetContractReturnType<typeof TokenPortalAbi, WalletClient<HttpTransport, Chain, Account>>;
protected readonly tokenManager: L1TokenManager;
protected readonly l1TxUtils: L1TxUtils;

constructor(
portalAddress: EthAddress,
Expand All @@ -224,6 +243,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. */
Expand All @@ -241,14 +261,15 @@ 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 args = [to.toString(), amount, claimSecretHash.toString()] as const;

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: 'depositToAztecPublic',
args,
}),
});

const log = extractEvent(
Expand Down Expand Up @@ -286,10 +307,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(
Expand Down Expand Up @@ -368,17 +392,23 @@ 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
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) {
Expand Down
68 changes: 50 additions & 18 deletions yarn-project/cli/src/cmds/infrastructure/sequencers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createCompatibleClient } from '@aztec/aztec.js';
import { 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';

import { createPublicClient, createWalletClient, getContract, http } from 'viem';
import { createPublicClient, createWalletClient, encodeFunctionData, getContract, http } from 'viem';
import { mnemonicToAccount } from 'viem/accounts';

export async function sequencers(opts: {
Expand Down Expand Up @@ -48,6 +48,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) {
Expand All @@ -59,8 +61,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`);
Expand All @@ -72,25 +74,55 @@ export async function sequencers(opts: {
});

const config = getL1ContractsConfigEnvVars();
const mintRequest = {
to: stakingAsset.address,
data: encodeFunctionData({
abi: stakingAsset.abi,
functionName: 'mint',
args: [walletClient.account.address, config.minimumStake],
}),
};
const approveRequest = {
to: stakingAsset.address,
data: encodeFunctionData({
abi: stakingAsset.abi,
functionName: 'approve',
args: [rollup.address, config.minimumStake],
}),
};

await Promise.all([
l1TxUtils.sendAndMonitorTransaction(mintRequest),
l1TxUtils.sendAndMonitorTransaction(approveRequest),
]);

await Promise.all(
[
await stakingAsset.write.mint([walletClient.account.address, config.minimumStake], {} as any),
await stakingAsset.write.approve([rollup.address, config.minimumStake], {} as any),
].map(txHash => publicClient.waitForTransactionReceipt({ hash: txHash })),
);
// send and monitor deposit transaction
const depositReceipt = await l1TxUtils.sendAndMonitorTransaction({
to: writeableRollup.address,
data: encodeFunctionData({
abi: writeableRollup.abi,
functionName: 'deposit',
args: [who, who, who, config.minimumStake],
}),
});

const hash = await writeableRollup.write.deposit([who, who, who, config.minimumStake]);
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}`);
Expand Down

0 comments on commit ccf28f5

Please sign in to comment.