Skip to content

Commit 10e67ec

Browse files
committed
refactor: drop artifact field from SimulationOverrides
`SimulationOverrides.contracts` entries no longer carry a `ContractArtifact`. Simulation resolves the override-instance's `currentContractClassId` through PXE's locally registered classes, so callers register the target artifact via `pxe.registerContractClass(...)` once, then construct an instance with the desired `currentContractClassId` to drive dispatch. Migrates the in-tree account-stub flows (`cli-wallet`, `embedded_wallet`, `test_wallet`) to pre-register the stub class and bump `currentContractClassId` on the override instance. `proxied_contract_data_source` drops its `getFunctionArtifact*` overrides — function lookup falls through to the regular `ContractStore`.
1 parent f0fbc4a commit 10e67ec

6 files changed

Lines changed: 104 additions & 111 deletions

File tree

docs/docs-developers/docs/resources/migration_notes.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ Aztec is in active development. Each version may introduce breaking changes that
99

1010
## TBD
1111

12+
### [Stdlib] `SimulationOverrides.contracts` entries no longer carry an artifact
13+
14+
`ContractOverrides` entries are now `{ instance }` only. To override a contract's artifact, pre-register the target class via `pxe.registerContractClass(artifact)` and set the override instance's `currentContractClassId` to that class id:
15+
16+
```diff
17+
- const instance = await getContractInstanceFromInstantiationParams(stubArtifact, { salt: Fr.random() });
18+
+ const instance = await pxe.getContractInstance(addr);
19+
+ await pxe.registerContractClass(stubArtifact);
20+
+ const stubClassId = (await getContractClassFromArtifact(stubArtifact)).id;
21+
- overrides = { contracts: { [addr.toString()]: { instance, artifact: stubArtifact } } };
22+
+ overrides = { contracts: { [addr.toString()]: { instance: { ...instance, currentContractClassId: stubClassId } } } };
23+
```
24+
1225
### [PXE] `proveTx` takes an options bag
1326

1427
`PXE.proveTx` used to accept `scopes` as a positional argument; it now takes an options bag consistent with `simulateTx` and `profileTx`, and adds an optional `senderForTags` field. Update direct callers:

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import { StubEcdsaAccountContractArtifact, createStubEcdsaAccount } from '@aztec
44
import { StubSchnorrAccountContractArtifact, createStubSchnorrAccount } from '@aztec/accounts/stub/schnorr';
55
import { getIdentities } from '@aztec/accounts/utils';
66
import { type Account, type AccountContract, NO_FROM } from '@aztec/aztec.js/account';
7-
import {
8-
type InteractionFeeOptions,
9-
getContractInstanceFromInstantiationParams,
10-
getGasLimits,
11-
} from '@aztec/aztec.js/contracts';
7+
import { type InteractionFeeOptions, getContractClassFromArtifact, getGasLimits } from '@aztec/aztec.js/contracts';
128
import type { AztecNode } from '@aztec/aztec.js/node';
139
import { AccountManager, type Aliased, type SimulateOptions } from '@aztec/aztec.js/wallet';
1410
import { TxSimulationResultWithAppOffset } from '@aztec/aztec.js/wallet';
@@ -34,6 +30,8 @@ import { printGasEstimates } from './options/fees.js';
3430

3531
export class CLIWallet extends BaseWallet {
3632
private accountCache = new Map<string, Account>();
33+
/** Stub artifacts already registered with PXE this session. Keyed by class id string. */
34+
#registeredStubClasses = new Set<string>();
3735

3836
constructor(
3937
pxe: PXE,
@@ -205,8 +203,13 @@ export class CLIWallet extends BaseWallet {
205203
const isSchnorr = type === 'schnorr';
206204
const artifact = isSchnorr ? StubSchnorrAccountContractArtifact : StubEcdsaAccountContractArtifact;
207205
const stubAccount = isSchnorr ? createStubSchnorrAccount(originalAddress) : createStubEcdsaAccount(originalAddress);
208-
const instance = await getContractInstanceFromInstantiationParams(artifact, { salt: Fr.random() });
209-
return { account: stubAccount, instance, artifact };
206+
const stubClassId = (await getContractClassFromArtifact(artifact)).id;
207+
if (!this.#registeredStubClasses.has(stubClassId.toString())) {
208+
await this.pxe.registerContractClass(artifact);
209+
this.#registeredStubClasses.add(stubClassId.toString());
210+
}
211+
const instance = { ...contractInstance, currentContractClassId: stubClassId };
212+
return { account: stubAccount, instance };
210213
}
211214

212215
override async simulateTx(
@@ -249,9 +252,9 @@ export class CLIWallet extends BaseWallet {
249252
const entrypoint = new DefaultEntrypoint();
250253
txRequest = await entrypoint.createTxExecutionRequest(finalExecutionPayload, feeOptions.gasSettings, chainInfo);
251254
} else {
252-
const { account, instance, artifact } = await this.getFakeAccountDataFor(from);
255+
const { account, instance } = await this.getFakeAccountDataFor(from);
253256
overrides = {
254-
contracts: { [from.toString()]: { instance, artifact } },
257+
contracts: { [from.toString()]: { instance } },
255258
};
256259
const executionOptions: DefaultAccountEntrypointOptions = {
257260
txNonce: Fr.random(),

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { type PXEConfig, getPXEConfig } from '@aztec/pxe/config';
2525
import { PXE, type PXECreationOptions, createPXE } from '@aztec/pxe/server';
2626
import { AuthWitness } from '@aztec/stdlib/auth-witness';
2727
import { AztecAddress } from '@aztec/stdlib/aztec-address';
28-
import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
28+
import { getContractClassFromArtifact } from '@aztec/stdlib/contract';
2929
import { deriveSigningKey } from '@aztec/stdlib/keys';
3030
import type { NoteDao } from '@aztec/stdlib/note';
3131
import {
@@ -112,6 +112,9 @@ export class TestWallet extends BaseWallet {
112112
});
113113
}
114114

115+
/** Stub artifacts already registered with PXE this session. Keyed by class id string. */
116+
#registeredStubClasses = new Set<string>();
117+
115118
/**
116119
* Builds contract overrides for all provided addresses by replacing their account contracts with stub implementations.
117120
*/
@@ -133,16 +136,15 @@ export class TestWallet extends BaseWallet {
133136
}
134137

135138
const stubArtifact = this.getStubArtifactFor(address);
136-
const stubConstructorArgs =
137-
this.getTypeFor(address) === 'schnorr' ? [Fr.ZERO, Fr.ZERO] : [Buffer.alloc(32), Buffer.alloc(32)];
138-
const stubInstance = await getContractInstanceFromInstantiationParams(stubArtifact, {
139-
salt: Fr.random(),
140-
constructorArgs: stubConstructorArgs,
141-
});
139+
const stubClassId = (await getContractClassFromArtifact(stubArtifact)).id;
140+
141+
if (!this.#registeredStubClasses.has(stubClassId.toString())) {
142+
await this.pxe.registerContractClass(stubArtifact);
143+
this.#registeredStubClasses.add(stubClassId.toString());
144+
}
142145

143146
contracts[address.toString()] = {
144-
instance: stubInstance,
145-
artifact: stubArtifact,
147+
instance: { ...contractInstance, currentContractClassId: stubClassId },
146148
};
147149
}
148150

yarn-project/pxe/src/contract_function_simulator/proxied_contract_data_source.ts

Lines changed: 49 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,69 @@
11
import { FunctionSelector } from '@aztec/stdlib/abi';
2-
import { AztecAddress } from '@aztec/stdlib/aztec-address';
2+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
33
import type { ContractOverrides } from '@aztec/stdlib/tx';
44

55
import type { ContractStore } from '../storage/contract_store/contract_store.js';
66

77
/*
8-
* Proxy generator for a ContractStore that allows overriding contract instances and artifacts, so
9-
* the contract function simulator can execute different bytecode on certain addresses. An example use case
10-
* would be overriding your own account contract so that valid signatures don't have to be provided while simulating.
8+
* Proxy generator for a ContractStore that allows overriding contract instances at given addresses,
9+
* so the contract function simulator can execute different bytecode on certain addresses. An example
10+
* use case would be overriding your own account contract so that valid signatures don't have to be
11+
* provided while simulating.
12+
*
13+
* Function artifact lookups for an overridden address are routed via the override-instance's
14+
* `currentContractClassId` rather than the underlying ContractStore's address→class mapping —
15+
* `ContractStore.getFunctionArtifact` calls `this.getContractInstance` internally, which the proxy
16+
* cannot intercept (the binding sees the raw target, not the proxy). The target class must be
17+
* registered ahead of time via `pxe.registerContractClass(...)`.
1118
*/
1219
export class ProxiedContractStoreFactory {
1320
static create(contractStore: ContractStore, overrides?: ContractOverrides) {
1421
if (!overrides) {
1522
return contractStore;
1623
}
1724

25+
const findFunctionInArtifact = async (
26+
artifact: { name: string; functions: { name: string; parameters: any[] }[] },
27+
selector: FunctionSelector,
28+
contractAddress: AztecAddress,
29+
) => {
30+
for (const fn of artifact.functions) {
31+
const fnSelector = await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters);
32+
if (fnSelector.equals(selector)) {
33+
return { ...fn, contractName: artifact.name } as any;
34+
}
35+
}
36+
throw new Error(
37+
`Function with selector ${selector} not found in stub artifact for overridden contract at ${contractAddress}.`,
38+
);
39+
};
40+
1841
return new Proxy(contractStore, {
1942
get(target, prop: keyof ContractStore) {
20-
switch (prop) {
21-
case 'getContractInstance': {
22-
return async (address: AztecAddress) => {
23-
if (overrides[address.toString()]) {
24-
const { instance } = overrides[address.toString()]!;
25-
instance.address = address;
26-
const realInstance = await target.getContractInstance(address);
27-
if (!realInstance) {
28-
throw new Error(`Contract instance not found for address: ${address}`);
29-
}
30-
instance.currentContractClassId = realInstance.currentContractClassId;
31-
instance.originalContractClassId = realInstance.originalContractClassId;
32-
instance.initializationHash = realInstance.initializationHash;
33-
return instance;
34-
} else {
35-
return target.getContractInstance(address);
36-
}
37-
};
38-
}
39-
case 'getFunctionArtifact': {
40-
return async (contractAddress: AztecAddress, selector: FunctionSelector) => {
41-
if (overrides[contractAddress.toString()]) {
42-
const { artifact } = overrides[contractAddress.toString()]!;
43-
const functions = artifact.functions;
44-
for (let i = 0; i < functions.length; i++) {
45-
const fn = functions[i];
46-
const fnSelector = await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters);
47-
if (fnSelector.equals(selector)) {
48-
return fn;
49-
}
50-
}
51-
throw new Error(
52-
`Function with selector ${selector} not found in stub artifact for overridden contract at ${contractAddress}. The stub does not implement this function.`,
53-
);
54-
} else {
55-
return target.getFunctionArtifact(contractAddress, selector);
56-
}
57-
};
58-
}
59-
case 'getFunctionArtifactWithDebugMetadata': {
60-
return async (contractAddress: AztecAddress, selector: FunctionSelector) => {
61-
if (overrides[contractAddress.toString()]) {
62-
const { artifact } = overrides[contractAddress.toString()]!;
63-
const functions = artifact.functions;
64-
for (let i = 0; i < functions.length; i++) {
65-
const fn = functions[i];
66-
const fnSelector = await FunctionSelector.fromNameAndParameters(fn.name, fn.parameters);
67-
if (fnSelector.equals(selector)) {
68-
return fn;
69-
}
70-
}
71-
throw new Error(
72-
`Function with selector ${selector} not found in stub artifact for overridden contract at ${contractAddress}. The stub does not implement this function.`,
73-
);
74-
} else {
75-
return target.getFunctionArtifactWithDebugMetadata(contractAddress, selector);
76-
}
77-
};
78-
}
79-
default: {
80-
const value = Reflect.get(target, prop);
81-
if (typeof value === 'function') {
82-
return value.bind(target);
43+
if (prop === 'getContractInstance') {
44+
return (address: AztecAddress) => {
45+
const override = overrides[address.toString()];
46+
return override ? Promise.resolve(override.instance) : target.getContractInstance(address);
47+
};
48+
}
49+
if (prop === 'getFunctionArtifact' || prop === 'getFunctionArtifactWithDebugMetadata') {
50+
return async (contractAddress: AztecAddress, selector: FunctionSelector) => {
51+
const override = overrides[contractAddress.toString()];
52+
if (!override) {
53+
return (target[prop] as Function).call(target, contractAddress, selector);
54+
}
55+
const artifact = await target.getContractArtifact(override.instance.currentContractClassId);
56+
if (!artifact) {
57+
throw new Error(
58+
`No artifact registered for override class ${override.instance.currentContractClassId} ` +
59+
`at ${contractAddress}. Register it via pxe.registerContractClass(...) before simulating.`,
60+
);
8361
}
84-
return value;
85-
}
62+
return findFunctionInArtifact(artifact, selector, contractAddress);
63+
};
8664
}
65+
const value = Reflect.get(target, prop);
66+
return typeof value === 'function' ? value.bind(target) : value;
8767
},
8868
}) satisfies ContractStore;
8969
}

yarn-project/stdlib/src/tx/simulated_tx.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { FieldsOf } from '@aztec/foundation/types';
33

44
import { z } from 'zod';
55

6-
import { type ContractArtifact, ContractArtifactSchema } from '../abi/abi.js';
76
import {
87
type ContractInstanceWithAddress,
98
ContractInstanceWithAddressSchema,
@@ -24,18 +23,17 @@ import { NestedProcessReturnValues, PublicSimulationOutput } from './public_simu
2423
import { Tx } from './tx.js';
2524

2625
/*
27-
* If passed during the execution of a user circuit, the contract function simulator will replace the instance and class
28-
* of the contract with the one provided in the overrides for that address. An example use case
29-
* would be overriding your own account contract so that valid signatures don't have to be provided while simulating.
26+
* If passed during the execution of a user circuit, the contract function simulator will replace
27+
* the contract instance at that address with the one provided. An example use case would be
28+
* overriding your own account contract so that valid signatures don't have to be provided while
29+
* simulating. The override's `currentContractClassId` resolves through PXE's locally registered
30+
* classes, so pre-register the target artifact via `pxe.registerContractClass(...)`.
3031
*/
31-
export type ContractOverrides = Record<
32-
string /* AztecAddress as string */,
33-
{ instance: ContractInstanceWithAddress; artifact: ContractArtifact }
34-
>;
32+
export type ContractOverrides = Record<string /* AztecAddress as string */, { instance: ContractInstanceWithAddress }>;
3533

3634
/*
3735
* Optional values that can be overridden during simulation. In order to simulate a transaction with these
38-
* set, it *must* be run without the kernel circuits, or validations will fail
36+
* set, it *must* be run without the kernel circuits, or validations will fail.
3937
*/
4038
export class SimulationOverrides {
4139
public contracts?: ContractOverrides;
@@ -47,12 +45,7 @@ export class SimulationOverrides {
4745
static get schema() {
4846
return z
4947
.object({
50-
contracts: optional(
51-
z.record(
52-
z.string(),
53-
z.object({ instance: ContractInstanceWithAddressSchema, artifact: ContractArtifactSchema }),
54-
),
55-
),
48+
contracts: optional(z.record(z.string(), z.object({ instance: ContractInstanceWithAddressSchema }))),
5649
})
5750
.transform(({ contracts }) => {
5851
return new SimulationOverrides(contracts);

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { AztecAsyncKVStore } from '@aztec/kv-store';
1111
import type { PXEConfig, PXECreationOptions } from '@aztec/pxe/client/lazy';
1212
import type { PXE } from '@aztec/pxe/server';
1313
import { AztecAddress } from '@aztec/stdlib/aztec-address';
14-
import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
14+
import { getContractClassFromArtifact } from '@aztec/stdlib/contract';
1515
import { GasSettings } from '@aztec/stdlib/gas';
1616
import type { AztecNode } from '@aztec/stdlib/interfaces/client';
1717
import { deriveSigningKey } from '@aztec/stdlib/keys';
@@ -76,6 +76,9 @@ const DEFAULT_ESTIMATED_GAS_PADDING = 0.1;
7676
export class EmbeddedWallet extends BaseWallet {
7777
protected estimatedGasPadding = DEFAULT_ESTIMATED_GAS_PADDING;
7878

79+
/** Stub artifacts already registered with PXE this session. Keyed by class id string. */
80+
#registeredStubClasses = new Set<string>();
81+
7982
constructor(
8083
pxe: PXE,
8184
aztecNode: AztecNode,
@@ -206,6 +209,12 @@ export class EmbeddedWallet extends BaseWallet {
206209
const address = account.item;
207210
const { type } = await this.walletDB.retrieveAccount(address);
208211
const stubArtifact = await this.accountContracts.getStubAccountContractArtifact(type);
212+
const stubClassId = (await getContractClassFromArtifact(stubArtifact)).id;
213+
214+
if (!this.#registeredStubClasses.has(stubClassId.toString())) {
215+
await this.pxe.registerContractClass(stubArtifact);
216+
this.#registeredStubClasses.add(stubClassId.toString());
217+
}
209218

210219
const originalAccount = await this.getAccountFromAddress(address);
211220
const completeAddress = originalAccount.getCompleteAddress();
@@ -216,15 +225,8 @@ export class EmbeddedWallet extends BaseWallet {
216225
);
217226
}
218227

219-
const stubConstructorArgs = type === 'schnorr' ? [Fr.ZERO, Fr.ZERO] : [Buffer.alloc(32), Buffer.alloc(32)];
220-
const stubInstance = await getContractInstanceFromInstantiationParams(stubArtifact, {
221-
salt: Fr.random(),
222-
constructorArgs: stubConstructorArgs,
223-
});
224-
225228
contracts[address.toString()] = {
226-
instance: stubInstance,
227-
artifact: stubArtifact,
229+
instance: { ...contractInstance, currentContractClassId: stubClassId },
228230
};
229231
}
230232

0 commit comments

Comments
 (0)