Skip to content

Commit 06fafc0

Browse files
committed
refactor(wallets): consolidate AccountType + stub dispatch in @aztec/accounts/stub
Lift AccountType (cli-wallet's 4-value superset, including the SSH variant) and the schnorr-vs-ecdsa stub partition into @aztec/accounts/stub, with eager (./stub) and lazy (./stub/lazy) entrypoints exposing getStubArtifactForAccountType + createStubAccountForAccountType. cli-wallet, embedded wallet, and test_wallet now share the same dispatch. cli-wallet drops its ad-hoc 'schnorr' | 'ecdsa' flavor union and keys its stub-class cache on AccountType, matching embedded; the resulting redundant hash + registerContractClass round-trips are bounded by the (small) AccountType cardinality and only cost on first-miss per type.
1 parent eafdf07 commit 06fafc0

10 files changed

Lines changed: 91 additions & 46 deletions

File tree

yarn-project/accounts/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"./ecdsa/lazy": "./dest/ecdsa/lazy.js",
1111
"./schnorr": "./dest/schnorr/index.js",
1212
"./schnorr/lazy": "./dest/schnorr/lazy.js",
13+
"./stub": "./dest/stub/index.js",
14+
"./stub/lazy": "./dest/stub/lazy.js",
1315
"./stub/schnorr": "./dest/stub/schnorr/index.js",
1416
"./stub/schnorr/lazy": "./dest/stub/schnorr/lazy.js",
1517
"./stub/ecdsa": "./dest/stub/ecdsa/index.js",
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { Account } from '@aztec/aztec.js/account';
2+
import type { CompleteAddress } from '@aztec/aztec.js/addresses';
3+
import type { ContractArtifact } from '@aztec/stdlib/abi';
4+
5+
import { StubEcdsaAccountContractArtifact, createStubEcdsaAccount } from './ecdsa/index.js';
6+
import { StubSchnorrAccountContractArtifact, createStubSchnorrAccount } from './schnorr/index.js';
7+
8+
/** Account-contract families recognized by the wallets in this monorepo. */
9+
export const AccountTypes = ['schnorr', 'ecdsasecp256r1', 'ecdsasecp256r1ssh', 'ecdsasecp256k1'] as const;
10+
11+
/** One of the account-contract families in {@link AccountTypes}. */
12+
export type AccountType = (typeof AccountTypes)[number];
13+
14+
/**
15+
* Returns the stub contract artifact whose constructor signature matches the given account type.
16+
* All ECDSA variants share one stub because their real account contracts share a constructor
17+
* signature (`[u8; 32], [u8; 32]`); schnorr has its own (`Field, Field`).
18+
*/
19+
export function getStubArtifactForAccountType(type: AccountType): ContractArtifact {
20+
return type === 'schnorr' ? StubSchnorrAccountContractArtifact : StubEcdsaAccountContractArtifact;
21+
}
22+
23+
/**
24+
* Creates a stub account that impersonates the given address, picking the stub whose constructor
25+
* signature matches the real account's so deployment-simulation selector lookups succeed.
26+
*/
27+
export function createStubAccountForAccountType(type: AccountType, address: CompleteAddress): Account {
28+
return type === 'schnorr' ? createStubSchnorrAccount(address) : createStubEcdsaAccount(address);
29+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { Account } from '@aztec/aztec.js/account';
2+
import type { CompleteAddress } from '@aztec/aztec.js/addresses';
3+
import type { ContractArtifact } from '@aztec/stdlib/abi';
4+
5+
import type { AccountType } from './index.js';
6+
7+
export { AccountTypes, type AccountType } from './index.js';
8+
9+
/**
10+
* Lazily resolves the stub contract artifact whose constructor signature matches the given
11+
* account type, dynamically importing the artifact JSON to keep it out of the initial bundle.
12+
*/
13+
export async function getStubArtifactForAccountType(type: AccountType): Promise<ContractArtifact> {
14+
if (type === 'schnorr') {
15+
const { getStubSchnorrAccountContractArtifact } = await import('./schnorr/lazy.js');
16+
return getStubSchnorrAccountContractArtifact();
17+
}
18+
const { getStubEcdsaAccountContractArtifact } = await import('./ecdsa/lazy.js');
19+
return getStubEcdsaAccountContractArtifact();
20+
}
21+
22+
/**
23+
* Lazily creates a stub account that impersonates the given address, dynamically importing the
24+
* matching stub artifact module.
25+
*/
26+
export async function createStubAccountForAccountType(type: AccountType, address: CompleteAddress): Promise<Account> {
27+
if (type === 'schnorr') {
28+
const { createStubSchnorrAccount } = await import('./schnorr/lazy.js');
29+
return createStubSchnorrAccount(address);
30+
}
31+
const { createStubEcdsaAccount } = await import('./ecdsa/lazy.js');
32+
return createStubEcdsaAccount(address);
33+
}
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export const MIN_FEE_PADDING = 0.5;
1+
export { AccountTypes, type AccountType } from '@aztec/accounts/stub';
22

3-
export const AccountTypes = ['schnorr', 'ecdsasecp256r1', 'ecdsasecp256r1ssh', 'ecdsasecp256k1'] as const;
4-
export type AccountType = (typeof AccountTypes)[number];
3+
export const MIN_FEE_PADDING = 0.5;

yarn-project/cli-wallet/src/utils/wallet.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { EcdsaRAccountContract, EcdsaRSSHAccountContract } from '@aztec/accounts/ecdsa';
22
import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
3-
import { StubEcdsaAccountContractArtifact, createStubEcdsaAccount } from '@aztec/accounts/stub/ecdsa';
4-
import { StubSchnorrAccountContractArtifact, createStubSchnorrAccount } from '@aztec/accounts/stub/schnorr';
3+
import { createStubAccountForAccountType, getStubArtifactForAccountType } from '@aztec/accounts/stub';
54
import { getIdentities } from '@aztec/accounts/utils';
65
import { type Account, type AccountContract, NO_FROM } from '@aztec/aztec.js/account';
76
import { type InteractionFeeOptions, getContractClassFromArtifact, getGasLimits } from '@aztec/aztec.js/contracts';
@@ -32,10 +31,10 @@ import { printGasEstimates } from './options/fees.js';
3231
export class CLIWallet extends BaseWallet {
3332
private accountCache = new Map<string, Account>();
3433
/**
35-
* Per-stub-flavor cache of the stub class id and preimage. The Promise is stored (not the resolved
34+
* Per-account-type cache of the stub class id and preimage. The Promise is stored (not the resolved
3635
* value) so concurrent first-time callers dedupe on the same hashing + registration work.
3736
*/
38-
#stubClasses = new Map<'schnorr' | 'ecdsa', Promise<ContractClassWithId & ContractClassIdPreimage>>();
37+
#stubClasses = new Map<AccountType, Promise<ContractClassWithId & ContractClassIdPreimage>>();
3938

4039
constructor(
4140
pxe: PXE,
@@ -204,27 +203,26 @@ export class CLIWallet extends BaseWallet {
204203
throw new Error(`No contract instance found for address: ${originalAddress.address}`);
205204
}
206205
const { type } = await this.db!.retrieveAccount(address);
207-
const isSchnorr = type === 'schnorr';
208-
const stubAccount = isSchnorr ? createStubSchnorrAccount(originalAddress) : createStubEcdsaAccount(originalAddress);
209-
const { id: stubClassId } = await this.#getStubClass(isSchnorr ? 'schnorr' : 'ecdsa');
206+
const stubAccount = createStubAccountForAccountType(type, originalAddress);
207+
const { id: stubClassId } = await this.#getStubClass(type);
210208
const instance = { ...contractInstance, currentContractClassId: stubClassId };
211209
return { account: stubAccount, instance };
212210
}
213211

214212
/**
215-
* Lazily hashes and registers the stub class for the given flavor, caching the result so
213+
* Lazily hashes and registers the stub class for the given account type, caching the result so
216214
* subsequent simulations skip the artifact-hashing + registration round-trip.
217215
*/
218-
#getStubClass(flavor: 'schnorr' | 'ecdsa'): Promise<ContractClassWithId & ContractClassIdPreimage> {
219-
let cached = this.#stubClasses.get(flavor);
216+
#getStubClass(type: AccountType): Promise<ContractClassWithId & ContractClassIdPreimage> {
217+
let cached = this.#stubClasses.get(type);
220218
if (!cached) {
221219
cached = (async () => {
222-
const artifact = flavor === 'schnorr' ? StubSchnorrAccountContractArtifact : StubEcdsaAccountContractArtifact;
220+
const artifact = getStubArtifactForAccountType(type);
223221
const stubClass = await getContractClassFromArtifact(artifact);
224222
await this.pxe.registerContractClass(artifact);
225223
return stubClass;
226224
})();
227-
this.#stubClasses.set(flavor, cached);
225+
this.#stubClasses.set(type, cached);
228226
}
229227
return cached;
230228
}

yarn-project/end-to-end/src/test-wallet/test_wallet.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { EcdsaKAccountContract, EcdsaRAccountContract } from '@aztec/accounts/ecdsa';
22
import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
3-
import { StubEcdsaAccountContractArtifact, createStubEcdsaAccount } from '@aztec/accounts/stub/ecdsa';
4-
import { StubSchnorrAccountContractArtifact, createStubSchnorrAccount } from '@aztec/accounts/stub/schnorr';
3+
import { type AccountType, createStubAccountForAccountType, getStubArtifactForAccountType } from '@aztec/accounts/stub';
54
import { type Account, type AccountContract, NO_FROM } from '@aztec/aztec.js/account';
65
import type { CompleteAddress } from '@aztec/aztec.js/addresses';
76
import {
@@ -42,7 +41,6 @@ import {
4241
} from '@aztec/stdlib/tx';
4342
import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx';
4443
import { BaseWallet, type SimulateViaEntrypointOptions } from '@aztec/wallet-sdk/base-wallet';
45-
import type { AccountType } from '@aztec/wallets/embedded';
4644

4745
import { DEFAULT_MIN_FEE_PADDING } from '../fixtures/fixtures.js';
4846
import { AztecNodeProxy, ProvenTx } from './utils.js';
@@ -130,7 +128,7 @@ export class TestWallet extends BaseWallet {
130128
let cached = this.#stubClasses.get(type);
131129
if (!cached) {
132130
cached = (async () => {
133-
const stubArtifact = type === 'schnorr' ? StubSchnorrAccountContractArtifact : StubEcdsaAccountContractArtifact;
131+
const stubArtifact = getStubArtifactForAccountType(type);
134132
const stubClass = await getContractClassFromArtifact(stubArtifact);
135133
await this.pxe.registerContractClass(stubArtifact);
136134
return stubClass;
@@ -177,9 +175,7 @@ export class TestWallet extends BaseWallet {
177175
}
178176

179177
private getStubAccountFor(address: AztecAddress, completeAddress: CompleteAddress) {
180-
return this.getTypeFor(address) === 'schnorr'
181-
? createStubSchnorrAccount(completeAddress)
182-
: createStubEcdsaAccount(completeAddress);
178+
return createStubAccountForAccountType(this.getTypeFor(address), completeAddress);
183179
}
184180

185181
/**

yarn-project/pxe/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@
4040
"path": "../stdlib"
4141
},
4242
{
43-
"path": "../world-state"
43+
"path": "../noir-test-contracts.js"
4444
},
4545
{
46-
"path": "../noir-test-contracts.js"
46+
"path": "../world-state"
4747
}
4848
],
4949
"include": ["src"]

yarn-project/wallets/src/embedded/account-contract-providers/bundle.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { EcdsaKAccountContract, EcdsaRAccountContract } from '@aztec/accounts/ecdsa';
22
import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
3-
import { StubEcdsaAccountContractArtifact, createStubEcdsaAccount } from '@aztec/accounts/stub/ecdsa';
4-
import { StubSchnorrAccountContractArtifact, createStubSchnorrAccount } from '@aztec/accounts/stub/schnorr';
3+
import { createStubAccountForAccountType, getStubArtifactForAccountType } from '@aztec/accounts/stub';
54
import type { Account, AccountContract } from '@aztec/aztec.js/account';
65
import type { Fq } from '@aztec/foundation/curves/bn254';
76
import { getCanonicalMultiCallEntrypoint } from '@aztec/protocol-contracts/multi-call-entrypoint';
@@ -29,11 +28,11 @@ export class BundleAccountContractsProvider implements AccountContractsProvider
2928
}
3029

3130
getStubAccountContractArtifact(type: AccountType): Promise<ContractArtifact> {
32-
return Promise.resolve(type === 'schnorr' ? StubSchnorrAccountContractArtifact : StubEcdsaAccountContractArtifact);
31+
return Promise.resolve(getStubArtifactForAccountType(type));
3332
}
3433

3534
createStubAccount(address: CompleteAddress, type: AccountType): Promise<Account> {
36-
return Promise.resolve(type === 'schnorr' ? createStubSchnorrAccount(address) : createStubEcdsaAccount(address));
35+
return Promise.resolve(createStubAccountForAccountType(type, address));
3736
}
3837

3938
getMulticallContract(): Promise<{ instance: ContractInstanceWithAddress; artifact: ContractArtifact }> {

yarn-project/wallets/src/embedded/account-contract-providers/lazy.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { createStubAccountForAccountType, getStubArtifactForAccountType } from '@aztec/accounts/stub/lazy';
12
import type { Account, AccountContract } from '@aztec/aztec.js/account';
23
import type { Fq } from '@aztec/foundation/curves/bn254';
34
import { getCanonicalMultiCallEntrypoint } from '@aztec/protocol-contracts/multi-call-entrypoint/lazy';
@@ -27,24 +28,12 @@ export class LazyAccountContractsProvider implements AccountContractsProvider {
2728
return new EcdsaKAccountContract(signingKey);
2829
}
2930

30-
async getStubAccountContractArtifact(type: AccountType): Promise<ContractArtifact> {
31-
if (type === 'schnorr') {
32-
const { getStubSchnorrAccountContractArtifact } = await import('@aztec/accounts/stub/schnorr/lazy');
33-
return getStubSchnorrAccountContractArtifact();
34-
} else {
35-
const { getStubEcdsaAccountContractArtifact } = await import('@aztec/accounts/stub/ecdsa/lazy');
36-
return getStubEcdsaAccountContractArtifact();
37-
}
31+
getStubAccountContractArtifact(type: AccountType): Promise<ContractArtifact> {
32+
return getStubArtifactForAccountType(type);
3833
}
3934

40-
async createStubAccount(address: CompleteAddress, type: AccountType): Promise<Account> {
41-
if (type === 'schnorr') {
42-
const { createStubSchnorrAccount } = await import('@aztec/accounts/stub/schnorr/lazy');
43-
return createStubSchnorrAccount(address);
44-
} else {
45-
const { createStubEcdsaAccount } = await import('@aztec/accounts/stub/ecdsa/lazy');
46-
return createStubEcdsaAccount(address);
47-
}
35+
createStubAccount(address: CompleteAddress, type: AccountType): Promise<Account> {
36+
return createStubAccountForAccountType(type, address);
4837
}
4938

5039
getMulticallContract(): Promise<{ instance: ContractInstanceWithAddress; artifact: ContractArtifact }> {

yarn-project/wallets/src/embedded/wallet_db.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import { type AccountType, AccountTypes } from '@aztec/accounts/stub';
12
import type { Aliased } from '@aztec/aztec.js/wallet';
23
import { Fq, Fr } from '@aztec/foundation/curves/bn254';
34
import type { LogFn } from '@aztec/foundation/log';
45
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
56
import { AztecAddress } from '@aztec/stdlib/aztec-address';
67

7-
export const AccountTypes = ['schnorr', 'ecdsasecp256r1', 'ecdsasecp256k1'] as const;
8-
export type AccountType = (typeof AccountTypes)[number];
8+
export { AccountTypes, type AccountType };
99

1010
function accountKey(field: string, address: AztecAddress | string): string {
1111
return `${field}:${address.toString()}`;

0 commit comments

Comments
 (0)