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

feat(aztec-js): Return contract instance when awaiting deploy tx #1360

Merged
merged 3 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion circuits/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ See [CODING_STANDARD.md](./CODING_STANDARD.md)\*\* before contributing!

## Repository Overview

The [`aztec3-circpuits`](https://github.com/AztecProtocol/aztec3-packages) circuits folder contains circuits and related C++ code (`cpp/`) for Aztec3 along with Typescript wrappers (`ts/`).
The [`aztec3-circuits`](https://github.com/AztecProtocol/aztec3-packages) circuits folder contains circuits and related C++ code (`cpp/`) for Aztec3 along with Typescript wrappers (`ts/`).

### Dependencies

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ async function deployAllContracts(owner: AztecAddress) {

// deploy l2 uniswap contract and attach to portal
const tx = UniswapContract.deploy(aztecRpcClient).send({ portalContract: uniswapPortalAddress });
await tx.isMined(0, 0.5);
await tx.isMined({ interval: 0.5 });
const receipt = await tx.getReceipt();
const uniswapL2Contract = await UniswapContract.create(receipt.contractAddress!, wallet);
await uniswapL2Contract.attach(uniswapPortalAddress);
Expand Down Expand Up @@ -158,7 +158,7 @@ const transferWethOnL2 = async (
const transferTx = wethL2Contract.methods
.transfer(transferAmount, ownerAddress, receiver)
.send({ origin: ownerAddress });
await transferTx.isMined(0, 0.5);
await transferTx.isMined({ interval: 0.5 });
const transferReceipt = await transferTx.getReceipt();
// expect(transferReceipt.status).toBe(TxStatus.MINED);
logger(`WETH to L2 Transfer Receipt status: ${transferReceipt.status} should be ${TxStatus.MINED}`);
Expand Down Expand Up @@ -226,7 +226,7 @@ async function main() {
const consumptionTx = wethL2Contract.methods
.mint(wethAmountToBridge, owner, messageKey, secret, ethAccount.toField())
.send({ origin: owner });
await consumptionTx.isMined(0, 0.5);
await consumptionTx.isMined({ interval: 0.5 });
const consumptionReceipt = await consumptionTx.getReceipt();
// expect(consumptionReceipt.status).toBe(TxStatus.MINED);
logger(`Consumption Receipt status: ${consumptionReceipt.status} should be ${TxStatus.MINED}`);
Expand Down Expand Up @@ -261,7 +261,7 @@ async function main() {
ethAccount.toField(),
)
.send({ origin: owner });
await withdrawTx.isMined(0, 0.5);
await withdrawTx.isMined({ interval: 0.5 });
const withdrawReceipt = await withdrawTx.getReceipt();
// expect(withdrawReceipt.status).toBe(TxStatus.MINED);
logger(`Withdraw receipt status: ${withdrawReceipt.status} should be ${TxStatus.MINED}`);
Expand Down Expand Up @@ -312,7 +312,7 @@ async function main() {
const daiMintTx = daiL2Contract.methods
.mint(daiAmountToBridge, owner, depositDaiMessageKey, secret, ethAccount.toField())
.send({ origin: owner });
await daiMintTx.isMined(0, 0.5);
await daiMintTx.isMined({ interval: 0.5 });
const daiMintTxReceipt = await daiMintTx.getReceipt();
// expect(daiMintTxReceipt.status).toBe(TxStatus.MINED);
logger(`DAI mint TX status: ${daiMintTxReceipt.status} should be ${TxStatus.MINED}`);
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec-sandbox/src/examples/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export async function deployAndInitializeNonNativeL2TokenContracts(
const tx = NonNativeTokenContract.deploy(wallet, initialBalance, owner).send({
portalContract: tokenPortalAddress,
});
await tx.isMined(0, 0.1);
await tx.isMined({ interval: 0.1 });
const receipt = await tx.getReceipt();
const l2Contract = await NonNativeTokenContract.create(receipt.contractAddress!, wallet);
await l2Contract.attach(tokenPortalAddress);
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/aztec-sandbox/src/examples/zk_token_contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async function main() {
// Mint more tokens
logger(`Minting ${SECONDARY_AMOUNT} more coins`);
const mintTx = zkContract.methods.mint(SECONDARY_AMOUNT, ownerAddress).send({ origin: ownerAddress });
await mintTx.isMined(0, 0.5);
await mintTx.isMined({ interval: 0.5 });
const balanceAfterMint = await getBalance(zkContract, ownerAddress);
logger(`Owner's balance is now: ${balanceAfterMint}`);

Expand All @@ -70,7 +70,7 @@ async function main() {
const transferTx = zkContract.methods
.transfer(SECONDARY_AMOUNT, ownerAddress, address2)
.send({ origin: ownerAddress });
await transferTx.isMined(0, 0.5);
await transferTx.isMined({ interval: 0.5 });
const balanceAfterTransfer = await getBalance(zkContract, ownerAddress);
const receiverBalance = await getBalance(zkContract, address2);
logger(`Owner's balance is now ${balanceAfterTransfer}`);
Expand Down
34 changes: 23 additions & 11 deletions yarn-project/aztec.js/src/contract/sent_tx.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import { FieldsOf } from '@aztec/circuits.js';
import { retryUntil } from '@aztec/foundation/retry';
import { AztecRPC, TxHash, TxReceipt, TxStatus } from '@aztec/types';

/** Options related to waiting for a tx. */
export type WaitOpts = {
/** The maximum time (in seconds) to wait for the transaction to be mined. */
timeout?: number;
/** The time interval (in seconds) between retries to fetch the transaction receipt. */
interval?: number;
};

const DefaultWaitOpts: WaitOpts = {
timeout: 0,
interval: 1,
};

/**
* The SentTx class represents a sent transaction through the AztecRPCClient, providing methods to fetch
* its hash, receipt, and mining status.
Expand Down Expand Up @@ -32,12 +46,11 @@ export class SentTx {

/**
* Awaits for a tx to be mined and returns the receipt. Throws if tx is not mined.
* @param timeout - The maximum time (in seconds) to wait for the transaction to be mined. A value of 0 means no timeout.
* @param interval - The time interval (in seconds) between retries to fetch the transaction receipt.
* @param opts - Options for configuring the waiting for the tx to be mined.
* @returns The transaction receipt.
*/
public async wait(timeout = 0, interval = 1): Promise<TxReceipt> {
const receipt = await this.waitForReceipt(timeout, interval);
public async wait(opts?: WaitOpts): Promise<FieldsOf<TxReceipt>> {
const receipt = await this.waitForReceipt(opts);
if (receipt.status !== TxStatus.MINED)
throw new Error(`Transaction ${await this.getTxHash()} was ${receipt.status}`);
return receipt;
Expand All @@ -48,25 +61,24 @@ export class SentTx {
* Resolves to true if the transaction status is 'MINED', false otherwise.
* Throws an error if the transaction receipt cannot be fetched after the given timeout.
*
* @param timeout - The maximum time (in seconds) to wait for the transaction to be mined. A value of 0 means no timeout.
* @param interval - The time interval (in seconds) between retries to fetch the transaction receipt.
* @param opts - Options for configuring the waiting for the tx to be mined.
* @returns A Promise that resolves to a boolean indicating if the transaction is mined or not.
*/
public async isMined(timeout = 0, interval = 1): Promise<boolean> {
const receipt = await this.waitForReceipt(timeout, interval);
public async isMined(opts?: WaitOpts): Promise<boolean> {
const receipt = await this.waitForReceipt(opts);
return receipt.status === TxStatus.MINED;
}

protected async waitForReceipt(timeout = 0, interval = 1): Promise<TxReceipt> {
protected async waitForReceipt(opts?: WaitOpts): Promise<TxReceipt> {
const txHash = await this.getTxHash();
return await retryUntil(
async () => {
const txReceipt = await this.arc.getTxReceipt(txHash);
return txReceipt.status != TxStatus.PENDING ? txReceipt : undefined;
},
'isMined',
timeout,
interval,
opts?.timeout ?? DefaultWaitOpts.timeout,
opts?.interval ?? DefaultWaitOpts.interval,
);
}
}
14 changes: 8 additions & 6 deletions yarn-project/aztec.js/src/contract_deployer/deploy_method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { Fr } from '@aztec/foundation/fields';
import { AztecRPC, ExecutionRequest, PackedArguments, PublicKey, Tx, TxExecutionRequest } from '@aztec/types';

import { BaseWallet, Wallet } from '../aztec_rpc_client/wallet.js';
import { Contract, ContractFunctionInteraction, SendMethodOptions } from '../contract/index.js';
import { Contract, ContractBase, ContractFunctionInteraction, SendMethodOptions } from '../contract/index.js';
import { DeploySentTx } from './deploy_sent_tx.js';

/**
* Options for deploying a contract on the Aztec network.
Expand Down Expand Up @@ -56,7 +57,7 @@ class DeployerWallet extends BaseWallet {
* Creates a TxRequest from a contract ABI, for contract deployment.
* Extends the ContractFunctionInteraction class.
*/
export class DeployMethod extends ContractFunctionInteraction {
export class DeployMethod<TContract extends ContractBase = Contract> extends ContractFunctionInteraction {
/**
* The partially computed contract address. Known after creation of the deployment transaction.
*/
Expand All @@ -67,7 +68,7 @@ export class DeployMethod extends ContractFunctionInteraction {
*/
public completeContractAddress?: AztecAddress = undefined;

constructor(private publicKey: PublicKey, arc: AztecRPC, private abi: ContractAbi, args: any[] = []) {
constructor(private publicKey: PublicKey, private arc: AztecRPC, private abi: ContractAbi, args: any[] = []) {
const constructorAbi = abi.functions.find(f => f.name === 'constructor');
if (!constructorAbi) {
throw new Error('Cannot find constructor in the ABI.');
Expand Down Expand Up @@ -126,10 +127,11 @@ export class DeployMethod extends ContractFunctionInteraction {
* allowing us to send a transaction specifically for contract deployment.
*
* @param options - An object containing various deployment options such as portalContract, contractAddressSalt, and from.
* @returns A Promise that resolves to the transaction receipt upon successful deployment.
* @returns A SentTx object that returns the receipt and the deployed contract instance.
*/
public send(options: DeployOptions = {}) {
return super.send(options);
public send(options: DeployOptions = {}): DeploySentTx<TContract> {
const txHashPromise = super.send(options).getTxHash();
return new DeploySentTx(this.abi, this.arc, txHashPromise);
}

/**
Expand Down
55 changes: 55 additions & 0 deletions yarn-project/aztec.js/src/contract_deployer/deploy_sent_tx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { FieldsOf } from '@aztec/circuits.js';
import { ContractAbi } from '@aztec/foundation/abi';
import { TxHash, TxReceipt } from '@aztec/types';

import { AztecAddress, AztecRPC, Contract, ContractBase, SentTx, WaitOpts, Wallet } from '../index.js';

/** Options related to waiting for a deployment tx. */
export type DeployedWaitOpts = WaitOpts & {
/** Wallet to use for creating a contract instance. Uses the one set in the deployer constructor if not set. */
wallet?: Wallet;
};

/** Extends a transaction receipt with a contract instance that represents the newly deployed contract. */
export type DeployTxReceipt<TContract extends ContractBase = Contract> = FieldsOf<TxReceipt> & {
/** Instance of the newly deployed contract. */
contract: TContract;
};

/**
* A contract deployment transaction sent to the network, extending SentTx with methods to create a contract instance.
*/
export class DeploySentTx<TContract extends ContractBase = Contract> extends SentTx {
constructor(private abi: ContractAbi, wallet: AztecRPC | Wallet, txHashPromise: Promise<TxHash>) {
super(wallet, txHashPromise);
}

/**
* Awaits for the tx to be mined and returns the contract instance. Throws if tx is not mined.
* @param opts - Options for configuring the waiting for the tx to be mined.
* @returns The deployed contract instance.
*/
public async deployed(opts?: DeployedWaitOpts): Promise<TContract> {
const receipt = await this.wait(opts);
return receipt.contract;
}

/**
* Awaits for the tx to be mined and returns the receipt along with a contract instance. Throws if tx is not mined.
* @param opts - Options for configuring the waiting for the tx to be mined.
* @returns The transaction receipt with the deployed contract instance.
*/
public async wait(opts?: DeployedWaitOpts): Promise<DeployTxReceipt<TContract>> {
const receipt = await super.wait(opts);
const contract = await this.getContractInstance(opts?.wallet, receipt.contractAddress);
return { ...receipt, contract };
}

protected getContractInstance(wallet?: Wallet, address?: AztecAddress): Promise<TContract> {
const isWallet = (rpc: AztecRPC | Wallet): rpc is Wallet => !!(rpc as Wallet).createAuthenticatedTxRequest;
const contractWallet = wallet ?? (isWallet(this.arc) && this.arc);
if (!contractWallet) throw new Error(`A wallet is required for creating a contract instance`);
if (!address) throw new Error(`Contract address is missing from transaction receipt`);
return Contract.create(address, this.abi, contractWallet) as Promise<TContract>;
}
}
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/utils/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function createAccounts(
await aztecRpcClient.addAccount(privKey, deploymentInfo.address, deploymentInfo.partialAddress);
const contractDeployer = new ContractDeployer(accountContractAbi, aztecRpcClient, publicKey);
const tx = contractDeployer.deploy().send({ contractAddressSalt: salt });
await tx.isMined(0, 0.5);
await tx.isMined({ interval: 0.5 });
const receipt = await tx.getReceipt();
if (receipt.status !== TxStatus.MINED) {
throw new Error(`Deployment tx not mined (status is ${receipt.status})`);
Expand Down
8 changes: 4 additions & 4 deletions yarn-project/end-to-end/src/cross_chain/test_harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class CrossChainTestHarness {
.transfer(transferAmount, this.ownerAddress, this.receiver)
.send({ origin: this.accounts[0] });

await transferTx.isMined(0, 0.1);
await transferTx.isMined({ interval: 0.1 });
const transferReceipt = await transferTx.getReceipt();

expect(transferReceipt.status).toBe(TxStatus.MINED);
Expand All @@ -170,7 +170,7 @@ export class CrossChainTestHarness {
.mint(bridgeAmount, this.ownerAddress, messageKey, secret, this.ethAccount.toField())
.send({ origin: this.ownerAddress });

await consumptionTx.isMined(0, 0.1);
await consumptionTx.isMined({ interval: 0.1 });
const consumptionReceipt = await consumptionTx.getReceipt();
expect(consumptionReceipt.status).toBe(TxStatus.MINED);
}
Expand All @@ -182,7 +182,7 @@ export class CrossChainTestHarness {
.mintPublic(bridgeAmount, this.ownerAddress, messageKey, secret, this.ethAccount.toField())
.send({ origin: this.ownerAddress });

await consumptionTx.isMined(0, 0.1);
await consumptionTx.isMined({ interval: 0.1 });
const consumptionReceipt = await consumptionTx.getReceipt();
expect(consumptionReceipt.status).toBe(TxStatus.MINED);
}
Expand Down Expand Up @@ -254,7 +254,7 @@ export class CrossChainTestHarness {
async shieldFundsOnL2(shieldAmount: bigint, secretHash: Fr) {
this.logger('Shielding funds on L2');
const shieldTx = this.l2Contract.methods.shield(shieldAmount, secretHash).send({ origin: this.ownerAddress });
await shieldTx.isMined(0, 0.1);
await shieldTx.isMined({ interval: 0.1 });
const shieldReceipt = await shieldTx.getReceipt();
expect(shieldReceipt.status).toBe(TxStatus.MINED);
}
Expand Down
10 changes: 5 additions & 5 deletions yarn-project/end-to-end/src/e2e_2_rpc_servers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe('e2e_2_rpc_servers', () => {
logger(`Deploying ZkToken contract...`);
const tx = ZkTokenContract.deploy(aztecRpcServerA, initialBalance, owner).send();
const receipt = await tx.getReceipt();
await tx.isMined(0, 0.1);
await tx.isMined({ interval: 0.1 });
const minedReceipt = await tx.getReceipt();
expect(minedReceipt.status).toEqual(TxStatus.MINED);
logger('L2 contract deployed');
Expand Down Expand Up @@ -121,7 +121,7 @@ describe('e2e_2_rpc_servers', () => {
const contractWithWalletA = await ZkTokenContract.create(tokenAddress, walletA);
const txAToB = contractWithWalletA.methods.transfer(transferAmount1, userA, userB).send({ origin: userA });

await txAToB.isMined(0, 0.1);
await txAToB.isMined({ interval: 0.1 });
const receiptAToB = await txAToB.getReceipt();

expect(receiptAToB.status).toBe(TxStatus.MINED);
Expand All @@ -136,7 +136,7 @@ describe('e2e_2_rpc_servers', () => {
const contractWithWalletB = await ZkTokenContract.create(tokenAddress, walletB);
const txBToA = contractWithWalletB.methods.transfer(transferAmount2, userB, userA).send({ origin: userB });

await txBToA.isMined(0, 0.1);
await txBToA.isMined({ interval: 0.1 });
const receiptBToA = await txBToA.getReceipt();

expect(receiptBToA.status).toBe(TxStatus.MINED);
Expand All @@ -152,7 +152,7 @@ describe('e2e_2_rpc_servers', () => {
logger(`Deploying Child contract...`);
const tx = ChildContract.deploy(aztecRpcServerA).send();
const receipt = await tx.getReceipt();
await tx.isMined(0, 0.1);
await tx.isMined({ interval: 0.1 });
const minedReceipt = await tx.getReceipt();
expect(minedReceipt.status).toEqual(TxStatus.MINED);
logger('Child contract deployed');
Expand Down Expand Up @@ -188,7 +188,7 @@ describe('e2e_2_rpc_servers', () => {

const childContractWithWalletB = await ChildContract.create(childAddress, walletB);
const tx = childContractWithWalletB.methods.pubStoreValue(newValueToSet).send({ origin: userB });
await tx.isMined(0, 0.1);
await tx.isMined({ interval: 0.1 });

const receipt = await tx.getReceipt();
expect(receipt.status).toBe(TxStatus.MINED);
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_account_contracts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ function itShouldBehaveLikeAnAccountContract(
const { logger } = context;
logger('Calling private function...');
const tx = child.methods.value(42).send();
expect(await tx.isMined(0, 0.1)).toBeTruthy();
expect(await tx.isMined({ interval: 0.1 })).toBeTruthy();
}, 60_000);

it('calls a public function', async () => {
const { logger, aztecRpcServer } = context;
logger('Calling public function...');
const tx = child.methods.pubStoreValue(42).send();
expect(await tx.isMined(0, 0.1)).toBeTruthy();
expect(await tx.isMined({ interval: 0.1 })).toBeTruthy();
expect(toBigInt((await aztecRpcServer.getPublicStorageAt(child.address, new Fr(1)))!)).toEqual(42n);
}, 60_000);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('e2e_cross_chain_messaging', () => {
.withdraw(withdrawAmount, ownerAddress, ethAccount, EthAddress.ZERO.toField())
.send({ origin: ownerAddress });

await withdrawTx.isMined(0, 0.1);
await withdrawTx.isMined({ interval: 0.1 });
const withdrawReceipt = await withdrawTx.getReceipt();

expect(withdrawReceipt.status).toBe(TxStatus.MINED);
Expand Down
Loading