Abstract
This ERC proposes a new JSON-RPC method for simulating the execution of calls on a Wallet. The method is designed to be used by Wallets to simulate the execution of calls before sending them to the network, allowing for gas estimation, log extraction, and call validation.
Motivation
Applications are reliant on JSON-RPC communication to Wallets in order to execute actions. A seemingly core functionality for "offline" Applications (Servers, etc) is the ability to simulate if an action will succeed or fail and/or estimate the total fee and/or calculate balance changes, prior to signing and broadcasting to the network.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
wallet_simulateCalls
Instructs a Wallet to simulate execution of a set of calls, and return metadata such as: the gas used, logs, status, and failure reason (if applicable).
Request
type Request = {
method: 'wallet_simulateCalls',
params: [{
// Calls to simulate.
calls: {
to: `0x${string}`,
data?: `0x${string}`,
value?: `0x${string}`,
capabilities?: Record<string, any>;
}[],
// Target chain ID to simulate calls on.
chainId: `0x${string}`,
// ERC-5792 Capabilities request.
capabilities?: Record<string, any>;
// Sender address.
from?: `0x${string}`;
// Key (hint) that will be used to sign the call bundle.
key?: {
// Whether the digest will be prehashed by the key (default behavior of WebCrypto APIs).
prehash?: boolean,
// Public key.
publicKey: `0x${string}`,
// Key type.
type: 'secp256k1' | 'p256' | 'webauthn-p256'
};
// Version.
version: string;
}]
}Response
- The
statuscode complies with the ERC-5792 Status Codes. - A JSON-RPC
errorobject is returned if the simulation fails.- The
errorobject MAY be present if thestatuscode is4xx | 5xx | 6xx. error.messageMUST be a short human-readable message of the error.error.detailsMAY contain low-level details of the error.error.dataMUST be an hex-encoded error (selector) if thestatuscode is an onchain failure (5xx | 6xx).
- The
type Response = {
// Chain ID the calls were simulated on.
chainId: `0x${string}`;
// Estimated total gas used by the calls.
gasUsed: `0x${string}`;
// Logs emitted by the calls.
logs: {
address: `0x${string}`;
data: `0x${string}`;
topics: `0x${string}`[];
}[];
// ERC-5792 Capabilities results.
capabilities?: Record<string, any>;
// Error that occurred during simulation.
error?: {
message: string;
data?: `0x${string}`;
details?: string;
},
// Status code of the simulation.
status: number;
}Example
Successful Simulation
const response = await provider.request({
method: 'wallet_simulateCalls',
params: [{
calls: [{
to: '0xcafebabecafebabecafebabecafebabecafebabe',
data: '0xdeadbeef',
}],
chainId: '0x1',
from: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
version: '1',
}],
});
console.log(response);
/**
* {
* chainId: '0x1',
* gasUsed: '0xe208',
* logs: [{
* address: '0xcafebabecafebabecafebabecafebabecafebabe',
* data: '0x0000000000000000000000000000000000000000000000000000000000069420',
* topics: [
* '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
* '0x000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
* '0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe',
* ],
* }],
* status: 200,
* }
*/Failed Simulation (Onchain Failure)
const response = await provider.request({
method: 'wallet_simulateCalls',
params: [{
calls: [{
to: '0xba11ba11ba11ba11ba11ba11ba11ba11ba11ba11',
data: '0xbabebabe0000000000000000000000000000000000000000000000000000000000000001',
}],
chainId: '0x1',
from: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
version: '1',
}],
});
console.log(response);
/**
* {
* chainId: '0x1',
* gasUsed: '0xe208',
* error: {
* message: 'Token ID is taken.',
* details: 'execution reverted: TokenIdUnavailable()',
* data: '0x08c379a0',
* },
* status: 500,
* }
*/Failed Simulation (Offchain Failure)
const response = await provider.request({
method: 'wallet_simulateCalls',
params: [{
calls: [{
to: '0xba11ba11ba11ba11ba11ba11ba11ba11ba11ba11',
data: '0xbabebabe0000000000000000000000000000000000000000000000000000000000000001',
}],
chainId: '0x1',
from: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
version: '1',
}],
});
console.log(response);
/**
* {
* chainId: '0x1',
* gasUsed: '0xe208',
* error: {
* message: 'Paymaster balance is insufficient.',
* details: 'useroperation reverted during simulation with reason: aa26',
* },
* status: 400,
* }
*/traceAssetChanges Capability
Capability to trace ERC20 & Native asset changes. Needs discussion.
traceMalicious Capability
Capability to trace malicious calls. Needs discussion.
Rationale
eth_simulateV1 vs wallet_simulateCalls
The eth_simulateV1 Execution API method is very similar to wallet_simulateCalls in that they both simulate the execution of calls on a Wallet. However, the fundamental differences are that eth_simulateV1: is not account agnostic, is an Execution API method (not a Wallet API method), and assumes that regular transactions will be executed. It does not have knowledge of the implementation details of how the Wallet constructs transactions (ie. they could be wrapped as an ERC-4337 User Operation with a Paymaster, or wrapped as another type of abstraction, which consequently would affect the simulation results).
wallet_simulateCalls is account agnostic, meaning that it is aware of the implementation details of how the Wallet constructs transactions. As the Wallet handles wallet_simulateCalls and returns a response to the Application, this means that the Wallet can simulate the calls with the full context of its implementation details (ie. simulate as a regular transaction, a ERC-4337 User Operation, or something else entirely).
It is very well possible that a Wallet can "build" the transaction with their preferred account implementation, use eth_simulateV1 internally to simulate the calls, and use it to build the response for wallet_simulateCalls.
Backwards Compatibility
Needs discussion.
Security Considerations
Needs discussion.
Copyright
Copyright and related rights waived via CC0.
