Simulating Functions
This guide shows you how to use simulate to execute contract functions and read their return values without creating a transaction.
Prerequisites
- A deployed contract instance (see How to Deploy a Contract)
- A wallet connection (see How to Create an Account)
Overview
The simulate method executes a contract function locally and returns its result. It works with private, public, and utility functions. No transaction is created and no gas is spent.
const result = await contract.methods.myFunction(arg1, arg2).simulate({ from: callerAddress });
The from option specifies which address context to use for the simulation. This is required for all simulations.
Basic simulation
const balance = await contract.methods
.balance_of_public(newAccountAddress)
.simulate({ from: newAccountAddress });
expect(balance).toEqual(1n);
Handling return values
For functions returning multiple values, destructure the result:
const [value1, value2] = await contract.methods
.get_multiple_values()
.simulate({ from: callerAddress });
Including metadata
Set includeMetadata: true to get additional information about the simulation:
const result = await contract.methods
.balance_of_public(address)
.simulate({ from: callerAddress, includeMetadata: true });
// Result includes:
// - result: the function return value
// - stats: execution statistics (timing, circuit sizes)
// - offchainEffects: any offchain effects emitted
// - estimatedGas: gas limit estimates
console.log("Balance:", result.result);
console.log("Estimated gas:", result.estimatedGas);
Private function considerations
When simulating private functions, the caller must have access to any private state being read. The PXE only has visibility into notes belonging to registered accounts.
// This works if callerAddress owns the notes
const balance = await contract.methods
.balance_of_private(callerAddress)
.simulate({ from: callerAddress });
// This fails if callerAddress doesn't have access to otherAddress's notes
const otherBalance = await contract.methods
.balance_of_private(otherAddress)
.simulate({ from: callerAddress }); // Error: cannot access private state
Simulation runs locally without generating proofs. No correctness guarantees are provided on the result. See Call Types for more details.
Next steps
- Send transactions to modify contract state
- Learn about call types and when to use simulation vs transactions
- Explore testing patterns that use simulation