Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: use L1 Tx Utils #10759

Merged
merged 14 commits into from
Jan 10, 2025
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
Loading