Skip to content

Commit f5b39b0

Browse files
committed
feat: contract class overrides for simulation
Adds StateOverrides.contractClasses for shadowing contract classes in the simulator's contract DB - the building block for simulating against mock implementations or unregistered classes.
1 parent 25879a3 commit f5b39b0

4 files changed

Lines changed: 33 additions & 3 deletions

File tree

docs/docs-developers/docs/aztec-js/how_to_test.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ Use `.simulate()` to test reverts without spending gas. The simulation will thro
7171

7272
`.simulate()` accepts a `stateOverrides` option that injects values into the simulator's ephemeral world-state fork before the call runs. The override is scoped to that single simulation; the real chain state is untouched.
7373

74+
Three override flavors are supported:
75+
76+
- `publicStorage`: write `(contract, slot, value)` into the public-data tree.
77+
- `contractClasses`: shadow contract classes in the simulator's contract DB (as if they had been published on the class registry).
78+
- `contractInstances`: shadow contract instances at specific addresses (as if they had been deployed, possibly pointing at one of the shadowed classes).
79+
7480
Override a public-storage slot:
7581

7682
```typescript
@@ -81,10 +87,23 @@ const result = await contract.methods.read_balance(account).simulate({
8187
});
8288
```
8389

90+
Override a contract's class — useful for testing against a mock implementation:
91+
92+
```typescript
93+
const result = await contract.methods.foo().simulate({
94+
stateOverrides: {
95+
contractClasses: [mockClass],
96+
contractInstances: [{ ...existingInstance, currentContractClassId: mockClass.id }],
97+
},
98+
});
99+
```
100+
84101
Use this to:
85102

86103
- Set up state preconditions without running a full setup transaction
87104
- Reproduce a bug from production by pinning storage to the values seen at a specific block
105+
- Swap a contract's bytecode for a mock implementation in tests
106+
- Simulate calls against contracts that aren't yet deployed
88107
- Test branches that depend on rare values without orchestrating the contract calls that produce them
89108

90109
### Fast-forwarding a contract update

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const result = await contract.methods.read_balance(account).simulate({
2323

2424
The same option flows through `wallet.simulateTx` and eventually to `simulatePublicCalls` RPC on `AztecNode`.
2525

26-
A second override flavor, `contractInstances`, shadows contract instances in the simulator's contract DB - useful for simulating a contract being on a different class than the one it was deployed with. To simulate a complete on-chain upgrade flow, use the `fastForwardContractUpdate` helper which produces a coherent set of `publicStorage` and `contractInstances` overrides:
26+
Additional override flavors `contractInstances` and `contractClasses` shadow contract instances and classes in the simulator's contract DB - useful for simulating mock implementations or contracts being on a different class than the one they were deployed with. To simulate a complete on-chain upgrade flow, use the `fastForwardContractUpdate` helper which produces a coherent set of `publicStorage` and `contractInstances` overrides:
2727

2828
```typescript
2929
import { fastForwardContractUpdate } from '@aztec/aztec.js';

yarn-project/simulator/src/public/public_db_sources.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,15 @@ export class PublicContractsDB implements PublicContractsDBInterface {
7676
this.addContractsFromLogs(contractDeploymentData.getRevertibleContractDeploymentData());
7777
}
7878

79-
/** Inserts typed contract instances directly into the current checkpoint. */
80-
public addContracts(contracts: { contractInstances?: ContractInstanceWithAddress[] }): void {
79+
/** Inserts typed contract classes and instances directly into the current checkpoint. */
80+
public addContracts(contracts: {
81+
contractClasses?: ContractClassPublic[];
82+
contractInstances?: ContractInstanceWithAddress[];
83+
}): void {
8184
const currentState = this.getCurrentState();
85+
for (const contractClass of contracts.contractClasses ?? []) {
86+
currentState.addClass(contractClass.id, contractClass);
87+
}
8288
for (const instance of contracts.contractInstances ?? []) {
8389
currentState.addInstance(instance.address, instance);
8490
}

yarn-project/stdlib/src/interfaces/state_overrides.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { z } from 'zod';
22

3+
import { type ContractClassPublic, ContractClassPublicSchema } from '../contract/interfaces/contract_class.js';
34
import {
45
type ContractInstanceWithAddress,
56
ContractInstanceWithAddressSchema,
@@ -11,16 +12,20 @@ import { type PublicStorageOverride, PublicStorageOverrideSchema } from './publi
1112
* provided overrides to the ephemeral world-state fork (and contract DB) before running the tx.
1213
*
1314
* - `publicStorage`: write specific (contract, slot, value) entries in the public-data tree
15+
* - `contractClasses`: shadow contract classes in the contract DB
1416
* - `contractInstances`: shadow contract instances in the contract DB
1517
*/
1618
export type StateOverrides = {
1719
/** Public-storage writes to apply before simulation. */
1820
publicStorage?: PublicStorageOverride[];
21+
/** Contract classes to shadow in the contract DB for the duration of the simulation. */
22+
contractClasses?: ContractClassPublic[];
1923
/** Contract instances to shadow in the contract DB for the duration of the simulation. */
2024
contractInstances?: ContractInstanceWithAddress[];
2125
};
2226

2327
export const StateOverridesSchema = z.object({
2428
publicStorage: z.array(PublicStorageOverrideSchema).optional(),
29+
contractClasses: z.array(ContractClassPublicSchema).optional(),
2530
contractInstances: z.array(ContractInstanceWithAddressSchema).optional(),
2631
});

0 commit comments

Comments
 (0)