eip | title | description | author | discussions-to | status | type | category | created |
---|---|---|---|---|---|---|---|---|
5792 |
Wallet Call API |
Adds JSON-RPC methods for sending multiple calls from the user's wallet, and checking their status |
Moody Salem (@moodysalem), Lukas Rosario (@lukasrosario), Wilson Cusack (@wilsoncusack), Dror Tirosh (@drortirosh), Jake Moxey (@jxom) |
Draft |
Standards Track |
Interface |
2022-10-17 |
Defines new JSON-RPC methods which enable apps to ask a wallet to process a batch of onchain write calls and to check on the status of those calls. Calls can be specified to make use of various wallet-defined capabilities, and there is also a new RPC to enable apps to discover which capabilities a wallet supports.
The current methods used to send transactions from the user wallet and check their status are eth_sendTransaction
and eth_getTransactionReceipt
.
The current methods used to send transactions from the user wallet and check their status do not meet modern developer demands and cannot accomodate new transaction formats. Even the name–eth_sendTransaction
–is an artifact of a time when nodes served as wallets.
Today, developers want to send batch transactions, which many smart accounts can support atomically. Developers also want to make use of features afforded by new transaction formats, like paymasters in ERC-4337 transactions. eth_sendTransaction
offers no way to do these things.
In updating to a new set of wallet_
RPCs, our main goals are to enforce a clean separation of wallet and app concerns, enable developers to make use of things like paymasters and batch transactions, and to create a clear way for more features to be added over time with minimal coordination.
Four new JSON-RPC methods are added. Apps may begin using these methods immediately, falling back to eth_sendTransaction
and eth_getTransactionReceipt
when they are not available.
We define one capability related to call execution atomicity.
RPC for an application to receive information about the capabilities that a connected wallet supports (e.g. batch transactions, paymaster communication).
The key of each item per chain is the name of a capability and the value can be of any shape. Capabilities are returned per chain because wallets may support different capabilities across chains.
We expect the community to align on the definition of capabilities in separate ERCs.
type GetCapabilitiesParams = [];
type GetCapabilitiesResult = Record<`0x${string}`, <Record<string, any>>; // Hex chain id
The capabilities below are for illustrative purposes.
{
"0x2105": {
"paymasterService": {
"supported": true
},
"sessionKeys": {
"supported": true
}
},
"0x14A34": {
"paymasterService": {
"supported": true
}
}
}
Requests that a wallet submits a batch of calls. From and chain ID are top level because all calls should be sent from the same sender on the same chain. The items in the calls field are only those that are shared by all transaction types. Any other fields that a wallet may need to submit a transaction should be handled by the wallet.
The capabilities field is how an app can communicate with a wallet about capabilities a wallet supports. For example, this is where an app can specify a paymaster service URL from which an ERC-4337 wallet can request a paymasterAndData
input for a user operation.
The wallet:
- MUST send these calls in the order specified in the request.
- MUST send the calls on the request chain ID.
- MUST NOT send any calls from the request if the user rejects the request
- MAY revert all calls if any call fails
- MAY send all the function calls as part of one transaction or multiple transactions, depending on wallet capability.
- SHOULD stop executing the calls if any call fails
- MAY reject the request if the request chain ID does not match the currently selected chain ID.
- MAY reject the request if the from address does not match the enabled account.
- MAY reject the request if one or more calls in the batch is expected to fail, when simulated sequentially
type SendCallsParams = {
version: string;
chainId: `0x${string}`; // Hex chain id
from: `0x${string}`;
calls: {
to?: `0x${string}` | undefined;
data?: `0x${string}` | undefined;
value?: `0x${string}` | undefined; // Hex value
}[];
capabilities?: Record<string, any> | undefined;
};
type SendCallsResult = string;
[
{
"version": "1.0",
"chainId": 0x01,
"from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"calls": [
{
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"value": "0x9184e72a",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
},
{
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"value": "0x182183",
"data": "0xfbadbaf01"
}
],
"capabilities": {
"paymasterService": {
"url": "https://..."
}
}
}
]
The identifier can be any string. The only requirement is that for a given session, users should be able to call wallet_getCallsReceipt
with this value and get a call batch status.
"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
Returns the status of a call batch that was sent via wallet_sendCalls
. The identifier of the transaction is the value returned from the wallet_sendCalls
RPC. Note this method only returns a subset of fields that eth_getTransactionReceipt
returns, excluding any fields that may differ across wallet implementations.
- If a wallet does not execute multiple calls atomically (i.e. in multiple transactions), the receipts in the
receipts
field MUST be in order of the calls sent. - If a wallet executes multiple calls atomically (i.e. in a single transaction),
wallet_getCallsReceipt
MUST return a single receipt, corresponding to the transaction in which the calls were included. - The
logs
in the receipt objects MUST only include logs relevant to the calls submitted usingwallet_sendCalls
. For example, in the case of a transaction submitted onchain by an ERC-4337 bundler, the logs must only include those relevant to the user operation constructed using the calls submitted viawallet_sendCalls
. I.e. the logs should not include those from other unrelated user operations submitted in the same bundle.
type GetCallsParams = string
type GetCallsResult = {
status: 'PENDING' | 'CONFIRMED'
receipts?: {
logs: {
address: `0x${string}`
data: `0x${string}`
topics: `0x${string}`[]
}[]
status: `0x${string}` // Hex 1 or 0 for success or failure, respectively
blockHash: `0x${string}`
blockNumber: `0x${string}`
gasUsed: `0x${string}`
transactionHash: `0x${string}`
}[]
}
As with the return value of wallet_sendCalls
, the batch identifier may be any string.
[
"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
]
{
"status": "CONFIRMED",
"receipts": [
{
"logs": [
{
"address": "0xa922b54716264130634d6ff183747a8ead91a40b",
"topics": [
"0x5a2a90727cc9d000dd060b1132a5c977c9702bb3a52afe360c9c22f0e9451a68"
],
"data": "0xabcd"
}
],
"status": "0x1",
"blockHash": "0xf19bbafd9fd0124ec110b848e8de4ab4f62bf60c189524e54213285e7f540d4a",
"blockNumber": "0xabcd",
"gasUsed": "0xdef",
"transactionHash": "0x9b7bb827c2e5e3c1a0a44dc53e573aa0b3af3bd1f9f5ed03071b100bb039eaff"
}
]
}
Indicates that a wallet can execute multiple calls atomically as part of a single transaction.
If a wallet indicates it supports the atomicBatch
capability, it MUST submit calls submitted with wallet_sendCalls
as part of a single transaction.
type AtomicBatchCapability = {
supported: true
}
For each chain on which a wallet can submit multiple calls atomically, the wallet SHOULD include an atomicBatch
capability with a supported
field equal to true
.
{
"0x2105": {
"atomicBatch": {
"supported": true
},
},
"0x14A34": {
"atomicBatch": {
"supported": true
}
}
}
We considered modifying eth_sendTransaction
to add support for these new capabilities, but the method is ultimately an artifact of when nodes were used to sign transactions. We decided it is better to move forward with wallet
namespaced methods that better describe what they are used for.
We also debated on if the methods should be called wallet_sendTransaction
, wallet_sendCalls
, or something else. We ultimately landed on wallet_sendCalls
because in the case of EOA wallets the wallet_send*
method might send more than one transaction. Similarly, we decided against wallet_sendTransactions
because in the case of other wallet implementations (e.g. ERC-4337) multiple calls could result in a single transaction.
The wallet_sendCalls
method accepts an array of calls
. However, this proposal does not require that these calls are executed as part of a single transaction. This is so that EOA wallets can accept multiple calls too. That said, we expect that app developers might want to only submit batched calls if and only if they are executed atomically. This would reduce the number of error cases an app developer would need to handle.
We initially proposed that multiple calls must be executed atomically, but after some debate we ultimately decided this was too opinionated. Instead, we are including a specification for an atomicBatch
capability. This allows for EOA wallets to accept multiple calls and still gives developers the option to only submit batched calls if they are executed atomically.
Our initial proposal included an optional gas
field for each call in the calls
field accepted by the walletSendCalls
method. However, we realized this could be misleading because in the case of ERC-4337 wallets you cannot specify a gas limit per call, only a single gas limit for all calls in the user operation. We then proposed a single gas
value that would apply to all of the calls. This works for ERC-4337 wallets, but not for EOA wallets. When we decided that EOA wallets should be able to handle multiple calls, we decided to just remove the gas
field altogether.
Wallets that do not support the following methods should return error responses to the new JSON-RPC methods. Apps MAY attempt to send the same batch of calls via eth_sendTransaction
when they receive a not implemented call, or otherwise indicate to the user that their wallet is not supported.
App developers MUST treat each call in a batch as if the call was an independent transaction. In other words, there may be additional untrusted transactions between any of the calls in a batch. The calls in the batch may also be included in separate, non-contiguous blocks. There is no constraint over how long it will take a batch to be included. Apps must encode deadlines in the smart contract calls as they do today. Unless a wallet indicates it can submit batched calls atomically via its capabilities, app developers MUST NOT assume that all calls are sent in a single transaction.
Copyright and related rights waived via CC0.