diff --git a/yarn-project/aztec.js/src/account/account.ts b/yarn-project/aztec.js/src/account/account.ts index 823ea801dea..60b1174bf41 100644 --- a/yarn-project/aztec.js/src/account/account.ts +++ b/yarn-project/aztec.js/src/account/account.ts @@ -1,7 +1,7 @@ import { Fr, PublicKey, getContractDeploymentInfo } from '@aztec/circuits.js'; import { AztecRPC, PrivateKey } from '@aztec/types'; -import { AccountWallet, ContractDeployer, DeployMethod, WaitOpts, Wallet, generatePublicKey } from '../index.js'; +import { AccountWallet, ContractDeployer, DeployMethod, WaitOpts, generatePublicKey } from '../index.js'; import { CompleteAddress, isCompleteAddress } from './complete_address.js'; import { DeployAccountSentTx } from './deploy_account_sent_tx.js'; import { AccountContract, Entrypoint, Salt } from './index.js'; @@ -71,9 +71,9 @@ export class Account { * instances to be interacted with from this account. * @returns A Wallet instance. */ - public async getWallet(): Promise { + public async getWallet(): Promise { const entrypoint = await this.getEntrypoint(); - return new AccountWallet(this.rpc, entrypoint); + return new AccountWallet(this.rpc, entrypoint, await this.getCompleteAddress()); } /** @@ -82,7 +82,7 @@ export class Account { * Use the returned wallet to create Contract instances to be interacted with from this account. * @returns A Wallet instance. */ - public async register(): Promise { + public async register(): Promise { const { address, partialAddress } = await this.getCompleteAddress(); await this.rpc.addAccount(this.encryptionPrivateKey, address, partialAddress); return this.getWallet(); @@ -130,7 +130,8 @@ export class Account { * @param opts - Options to wait for the tx to be mined. * @returns A Wallet instance. */ - public async waitDeploy(opts: WaitOpts = {}): Promise { - return (await this.deploy()).getWallet(opts); + public async waitDeploy(opts: WaitOpts = {}): Promise { + await this.deploy().then(tx => tx.wait(opts)); + return this.getWallet(); } } diff --git a/yarn-project/aztec.js/src/account/index.ts b/yarn-project/aztec.js/src/account/index.ts index 0d81f73c575..666dd7249ac 100644 --- a/yarn-project/aztec.js/src/account/index.ts +++ b/yarn-project/aztec.js/src/account/index.ts @@ -1,6 +1,6 @@ import { AztecRPC, PrivateKey } from '@aztec/types'; -import { Fr } from '../index.js'; +import { AccountContract, AccountWallet, AztecAddress, Fr } from '../index.js'; import { Account } from './account.js'; import { CompleteAddress } from './complete_address.js'; import { EcdsaAccountContract } from './contract/ecdsa_account_contract.js'; @@ -65,3 +65,37 @@ export function getUnsafeSchnorrAccount( saltOrAddress, ); } + +/** + * Gets a wallet for an already registered account using Schnorr signatures with a single key for encryption and authentication. + * @param rpc - An AztecRPC server instance. + * @param address - Address for the account. + * @param signingPrivateKey - Grumpkin key used for note encryption and signing transactions. + * @returns A wallet for this account that can be used to interact with a contract instance. + */ +export function getUnsafeSchnorrWallet( + rpc: AztecRPC, + address: AztecAddress, + signingKey: PrivateKey, +): Promise { + return getWallet(rpc, address, new SingleKeyAccountContract(signingKey)); +} + +/** + * Gets a wallet for an already registered account. + * @param rpc - An AztecRPC server instance. + * @param address - Address for the account. + * @param accountContract - Account contract implementation. + * * @returns A wallet for this account that can be used to interact with a contract instance. + */ +export async function getWallet( + rpc: AztecRPC, + address: AztecAddress, + accountContract: AccountContract, +): Promise { + const [publicKey, partialAddress] = await rpc.getPublicKeyAndPartialAddress(address); + const nodeInfo = await rpc.getNodeInfo(); + const completeAddress: CompleteAddress = { publicKey, partialAddress, address }; + const entrypoint = await accountContract.getEntrypoint(completeAddress, nodeInfo); + return new AccountWallet(rpc, entrypoint, completeAddress); +} diff --git a/yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts b/yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts index a892613850d..b3f32b7a78b 100644 --- a/yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts +++ b/yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts @@ -15,6 +15,7 @@ import { } from '@aztec/types'; import { CreateTxRequestOpts, Entrypoint } from '../account/entrypoint/index.js'; +import { CompleteAddress } from '../index.js'; /** * The wallet interface. @@ -99,9 +100,9 @@ export abstract class BaseWallet implements Wallet { } /** - * A simple wallet implementation that forwards authentication requests to a provided account implementation. + * A simple wallet implementation that forwards authentication requests to a provided entrypoint implementation. */ -export class AccountWallet extends BaseWallet { +export class EntrypointWallet extends BaseWallet { constructor(rpc: AztecRPC, protected accountImpl: Entrypoint) { super(rpc); } @@ -109,3 +110,17 @@ export class AccountWallet extends BaseWallet { return this.accountImpl.createTxExecutionRequest(executions, opts); } } + +/** + * A wallet implementation that forwards authentication requests to a provided account. + */ +export class AccountWallet extends EntrypointWallet { + constructor(rpc: AztecRPC, protected accountImpl: Entrypoint, protected address: CompleteAddress) { + super(rpc, accountImpl); + } + + /** Returns the complete address of the account that implements this wallet. */ + public getCompleteAddress() { + return this.address; + } +} diff --git a/yarn-project/aztec.js/src/utils/account.ts b/yarn-project/aztec.js/src/utils/account.ts index 0acd2103271..0253d95ca62 100644 --- a/yarn-project/aztec.js/src/utils/account.ts +++ b/yarn-project/aztec.js/src/utils/account.ts @@ -5,7 +5,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { AztecRPC, TxStatus } from '@aztec/types'; import { SingleKeyAccountEntrypoint } from '../account/entrypoint/single_key_account_entrypoint.js'; -import { AccountWallet, Wallet } from '../aztec_rpc_client/wallet.js'; +import { EntrypointWallet, Wallet } from '../aztec_rpc_client/wallet.js'; import { ContractDeployer, EntrypointCollection, generatePublicKey } from '../index.js'; /** @@ -47,7 +47,7 @@ export async function createAccounts( new SingleKeyAccountEntrypoint(address, deploymentInfo.partialAddress, privKey, await Schnorr.new()), ); } - return new AccountWallet(aztecRpcClient, accountImpls); + return new EntrypointWallet(aztecRpcClient, accountImpls); } /** @@ -71,5 +71,5 @@ export async function getAccountWallet( address, new SingleKeyAccountEntrypoint(address, deploymentInfo.partialAddress, privateKey, await Schnorr.new()), ); - return new AccountWallet(aztecRpcClient, accountCollection); + return new EntrypointWallet(aztecRpcClient, accountCollection); } diff --git a/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts b/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts index bbd44ab2621..f8d5e409e1c 100644 --- a/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts +++ b/yarn-project/end-to-end/src/e2e_aztec_js_browser.test.ts @@ -1,7 +1,8 @@ +/* eslint-disable no-console */ import * as AztecJs from '@aztec/aztec.js'; import { AztecAddress, PrivateKey } from '@aztec/circuits.js'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import { PrivateTokenContractAbi, SchnorrSingleKeyAccountContractAbi } from '@aztec/noir-contracts/artifacts'; +import { PrivateTokenContractAbi } from '@aztec/noir-contracts/artifacts'; import { Server } from 'http'; import Koa from 'koa'; @@ -33,6 +34,7 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { let contractAddress: AztecAddress; let logger: DebugLogger; + let pageLogger: DebugLogger; let app: Koa; let testClient: AztecJs.AztecRPC; let server: Server; @@ -50,6 +52,7 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { }); logger = createDebugLogger('aztec:aztec.js:web'); + pageLogger = createDebugLogger('aztec:aztec.js:web:page'); browser = await launch({ executablePath: process.env.CHROME_BIN, @@ -68,10 +71,10 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { }); page = await browser.newPage(); page.on('console', msg => { - logger('PAGE MSG:', msg.text()); + pageLogger(msg.text()); }); page.on('pageerror', err => { - logger('PAGE ERROR:', err.toString()); + pageLogger.error(err.toString()); }); await page.goto(`http://localhost:${PORT}/index.html`); }); @@ -88,21 +91,20 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { }); expect(createAccountsExists).toBe(true); }); + it('Creates an account', async () => { const result = await page.evaluate( - async (rpcUrl, privateKey, schnorrAbi) => { - const { Fr, PrivateKey, createAztecRpcClient, createAccounts, mustSucceedFetch } = window.AztecJs; + async (rpcUrl, privateKeyString) => { + const { PrivateKey, createAztecRpcClient, mustSucceedFetch, getUnsafeSchnorrAccount } = window.AztecJs; const client = createAztecRpcClient(rpcUrl!, mustSucceedFetch); - - await createAccounts(client, schnorrAbi, PrivateKey.fromString(privateKey)!, Fr.ZERO); + const privateKey = PrivateKey.fromString(privateKeyString); + await getUnsafeSchnorrAccount(client, privateKey).waitDeploy(); const accounts = await client.getAccounts(); - // eslint-disable-next-line no-console console.log(`Created Account: ${accounts[0].toString()}`); return accounts[0].toString(); }, SANDBOX_URL, privKey.toString(), - SchnorrSingleKeyAccountContractAbi, ); const account = (await testClient.getAccounts())[0]; expect(result).toEqual(account.toString()); @@ -115,11 +117,10 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { const client = createAztecRpcClient(rpcUrl!, mustSucceedFetch); const owner = (await client.getAccounts())[0]; const publicKey = await client.getPublicKey(owner); - const tx = new DeployMethod(publicKey, client, PrivateTokenContractAbi, [33n, owner]).send(); - await tx.isMined(); - // eslint-disable-next-line no-console - console.log('Contract Deployed'); + const tx = new DeployMethod(publicKey, client, PrivateTokenContractAbi, [initialBalance, owner]).send(); + await tx.wait(); const receipt = await tx.getReceipt(); + console.log(`Contract Deployed: ${receipt.contractAddress}`); return receipt.txHash.toString(); }, SANDBOX_URL, @@ -134,17 +135,13 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { it("Gets the owner's balance", async () => { const result = await page.evaluate( - async (rpcUrl, privateKey, SchnorrSingleKeyAccountContractAbi, contractAddress, PrivateTokenContractAbi) => { - const { Contract, AztecAddress, Fr, PrivateKey, createAztecRpcClient, getAccountWallet, mustSucceedFetch } = + async (rpcUrl, privateKeyString, contractAddress, PrivateTokenContractAbi) => { + const { Contract, AztecAddress, PrivateKey, createAztecRpcClient, getUnsafeSchnorrWallet, mustSucceedFetch } = window.AztecJs; + const privateKey = PrivateKey.fromString(privateKeyString); const client = createAztecRpcClient(rpcUrl!, mustSucceedFetch); - const owner = (await client.getAccounts())[0]; - const wallet = await getAccountWallet( - client, - SchnorrSingleKeyAccountContractAbi, - PrivateKey.fromString(privateKey!), - Fr.ZERO, - ); + const [owner] = await client.getAccounts(); + const wallet = await getUnsafeSchnorrWallet(client, owner, privateKey); const contract = await Contract.create( AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, @@ -155,7 +152,6 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { }, SANDBOX_URL, privKey.toString(), - SchnorrSingleKeyAccountContractAbi, contractAddress.toString(), PrivateTokenContractAbi, ); @@ -165,35 +161,31 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { it('Sends a transfer TX', async () => { const result = await page.evaluate( - async ( - rpcUrl, - privateKey, - contractAddress, - transferAmount, - PrivateTokenContractAbi, - SchnorrSingleKeyAccountContractAbi, - ) => { - const { AztecAddress, Contract, Fr, PrivateKey, createAztecRpcClient, getAccountWallet, mustSucceedFetch } = - window.AztecJs; + async (rpcUrl, privateKeyString, contractAddress, transferAmount, PrivateTokenContractAbi) => { + console.log(`Starting transfer tx`); + const { + AztecAddress, + Contract, + PrivateKey, + createAztecRpcClient, + getUnsafeSchnorrAccount, + getUnsafeSchnorrWallet, + mustSucceedFetch, + } = window.AztecJs; const client = createAztecRpcClient(rpcUrl!, mustSucceedFetch); - await AztecJs.createAccounts(client, SchnorrSingleKeyAccountContractAbi, PrivateKey.random(), Fr.random()); - const [owner, receiver] = await client.getAccounts(); - // eslint-disable-next-line no-console + const privateKey = PrivateKey.fromString(privateKeyString); + const { address: receiver } = await getUnsafeSchnorrAccount(client, PrivateKey.random()) + .register() + .then(w => w.getCompleteAddress()); console.log(`Created 2nd Account: ${receiver.toString()}`); - const wallet = await getAccountWallet( - client, - SchnorrSingleKeyAccountContractAbi, - PrivateKey.fromString(privateKey!), - Fr.ZERO, - ); + const [owner] = await client.getAccounts(); + const wallet = await getUnsafeSchnorrWallet(client, owner, privateKey); const contract = await Contract.create( AztecAddress.fromString(contractAddress), PrivateTokenContractAbi, wallet, ); - const tx = contract.methods.transfer(transferAmount, owner, receiver).send({ origin: owner }); - await tx.isMined(); - // eslint-disable-next-line no-console + await contract.methods.transfer(transferAmount, owner, receiver).send({ origin: owner }).wait(); console.log(`Transfered ${transferAmount} tokens to new Account`); const [balance] = await contract.methods.getBalance(receiver).view({ from: receiver }); return balance; @@ -203,8 +195,7 @@ conditionalDescribe()('e2e_aztec.js_browser', () => { contractAddress.toString(), transferAmount, PrivateTokenContractAbi, - SchnorrSingleKeyAccountContractAbi, ); expect(result).toEqual(transferAmount); - }, 45_000); + }, 60_000); }); diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 0837e1764fa..0a003030f9f 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -1,12 +1,12 @@ import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { RpcServerConfig, createAztecRPCServer, getConfigEnvVars as getRpcConfigEnvVars } from '@aztec/aztec-rpc'; import { - AccountWallet, Account as AztecAccount, AztecAddress, Contract, ContractDeployer, EntrypointCollection, + EntrypointWallet, EthAddress, Wallet, createAztecRpcClient as createJsonRpcClient, @@ -168,7 +168,7 @@ export async function setupAztecRPCServer( await Promise.all(txs.map(tx => tx.wait({ interval: 0.1 }))); // Assemble them into a single wallet - const wallet = new AccountWallet(aztecRpcServer, await EntrypointCollection.fromAccounts(accounts)); + const wallet = new EntrypointWallet(aztecRpcServer, await EntrypointCollection.fromAccounts(accounts)); return { aztecRpcServer: aztecRpcServer!,