JSON-RPC API for ERC-4337
Abstract
Defines new JSON-RPC API methods which enable ERC-4337 wallets to communicate with UserOpeation
mempool
nodes and bundlers, matching the functionality that exists for Ethereum transactions.
Additionally, a set of debug
JSON-RPC API methods is defined in order to facilitate development, testing and
debugging issues with ERC-4337 implementations.
Motivation
In ERC-4337, user transactions as defined in Ethereum are replaced with UserOperation
objects, which contain all the
information needed to perform the operations requested by the users.
However, existing Ethereum JSON-RPC API methods are not suited to working with UserOperation
objects.
In order to facilitate the operation of the alternative UserOperation
mempool it is important that all
implementations of the ERC-4337 protocol have a standardized set of APIs that can be used interchangeably.
Specification
Definitions
- bundler: a node exposing the APIs, in order to submit them to the network.
A bundler collects one or more UserOperations into a bundle and submits them together to
the
EntryPoint
in a singlehandleOps
call.
RPC methods (eth namespace)
eth_sendUserOperation
The eth_sendUserOperation
method submits a UserOperation
object to the UserOperation mempool.
The client MUST validate the UserOperation
, and return a result accordingly.
The result SHOULD be set to the userOpHash
if and only if the request passed simulation and was accepted
in the client’s UserOperation pool.
If the validation, simulation, or UserOperation pool inclusion fails,
userOpHash
SHOULD NOT be returned. Rather, the client SHOULD return the failure reason.
Parameters:
- UserOperation a full user-operation struct.
All fields MUST be set as hex values.
Emptybytes
block (e.g. emptyinitCode
) MUST be set to"0x"
\ - factory and factoryData
Must provide either both of these parameters, or none. - paymaster, paymasterData, paymasterValidationGasLimit, paymasterPostOpGasLimit
Must provide either all of these parameters, or none. - entryPoint the
EntryPoint
contract address the request should be sent through.
This MUST be one of the entry points returned by thesupportedEntryPoints
RPC call.
Return value:
- If the UserOperation is valid, the client MUST return the calculated
userOpHash
for it - in case of failure, MUST return an
error
result object, withcode
andmessage
.
The error code and message SHOULD be set as follows:- code: -32602 - invalid
UserOperation
struct/fields - code: -32500 - transaction rejected by
EntryPoint
contract’ssimulateValidation
function during wallet creation or validation- The
message
field MUST be set to the emittedFailedOp
event’s “AAxx
” error message from theEntryPoint
- The
- code: -32501 - transaction rejected by
paymaster
contract’svalidatePaymasterUserOp
function- The
message
field SHOULD be set to the revert message from thepaymaster
contract - The
data
field MUST contain apaymaster
value
- The
- code: -32502 - transaction rejected because of ERC-7562 opcode validation rule violation
- code: -32503 - UserOperation out of time-range:
either wallet or paymaster returned a time-range, and it has already expired or will expire soon.- The
data
field SHOULD contain thevalidUntil
andvalidAfter
values - The
data
field SHOULD contain apaymaster
address if this error was triggered by thepaymaster
contract
- The
- code: -32504 - transaction rejected because
paymaster
is throttled or banned due to ERC-7562 reputation rules- The
data
field SHOULD contain apaymaster
address
- The
- code: -32505 - transaction rejected because
paymaster
contract’s ERC-7562 stake or unstake-delay is too low- The
data
field SHOULD contain apaymaster
address - The
data
field SHOULD contain aminimumStake
andminimumUnstakeDelay
- The
- code: -32507 - transaction rejected because of wallet signature check failed
- code: -32508 - transaction rejected because paymaster balance can’t cover all pending
UserOperations
.
- code: -32602 - invalid
Example:
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_sendUserOperation",
"params": [
{
sender, // address
nonce, // uint256
factory, // address
factoryData, // bytes
callData, // bytes
callGasLimit, // uint256
verificationGasLimit, // uint256
preVerificationGas, // uint256
maxFeePerGas, // uint256
maxPriorityFeePerGas, // uint256
paymaster, // address
paymasterVerificationGasLimit, // uint256
paymasterPostOpGasLimit, // uint256
paymasterData, // bytes
signature // bytes
},
entryPoint // address
]
}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x123456789012345678901234567890123456789012345678901234567890abcd"
}
Example failure responses:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"message": "AA21 didn't pay prefund",
"code": -32500
}
}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"message": "paymaster stake too low",
"data": {
"paymaster": "0x123456789012345678901234567890123456790",
"minimumStake": "0xde0b6b3a7640000",
"minimumUnstakeDelay": "0x15180"
},
"code": -32504
}
}
* eth_estimateUserOperationGas
Estimate the gas values for a UserOperation
.
Given UserOperation
optionally without gas limits and gas prices, return the needed gas limits.
The signature field is ignored by the wallet, so that the operation will not require the user’s approval.
Still, it might require putting a “stub” signature
value, e.g. a signature
byte array of the right length.
Parameters:
- Same as
eth_sendUserOperation
All gas limits and fees parameters are optional, but are used if specified.
maxFeePerGas
andmaxPriorityFeePerGas
default to zero, so no payment is required by neither account nor paymaster. - Optionally accepts the
State Override Set
to allow users to modify the state during the gas estimation.
This field as well as its behavior is equivalent to the ones defined foreth_call
RPC method.
Return Values:
- preVerificationGas gas overhead of this
UserOperation
- verificationGasLimit estimation of gas limit required by the validation of this
UserOperation
- paymasterVerificationGasLimit estimation of gas limit required by the paymaster verification
Returned only if theUserOperation
specifies aPaymaster
address - callGasLimit estimation of gas limit required by the inner account execution
Note: actual postOpGasLimit
cannot be reliably estimated.
Paymasters should provide this value to account, and require that specific value on-chain during validation.
Error Codes:
Same as eth_sendUserOperation
This operation may also return an error if either the inner call to the account contract reverts,
or paymaster’s postOp
call reverts.
* eth_getUserOperationByHash
Return a UserOperation
object based on a userOpHash
value returned by eth_sendUserOperation
.
Parameters
- hash a
userOpHash
value returned byeth_sendUserOperation
Return value:
- If the
UserOperation
is included in a block:- Return a full UserOperation, with the addition of
entryPoint
,blockNumber
,blockHash
andtransactionHash
.
- Return a full UserOperation, with the addition of
- Else if the
UserOperation
is pending in the bundler’s mempool:- MAY return
null
, or a fullUserOperation
, with the addition of theentryPoint
field and anull
value forblockNumber
,blockHash
andtransactionHash
.
- MAY return
- Else:
- Return
null
- Return
* eth_getUserOperationReceipt
Return a UserOperation
receipt object based on a userOpHash
value returned by eth_sendUserOperation
.
Parameters
- hash a
userOpHash
value returned byeth_sendUserOperation
Return value:
null
in case the UserOperation
is not yet included in a block, or:
- userOpHash the request hash
- entryPoint
- sender
- nonce
- paymaster the paymaster used for this userOp (or empty)
- actualGasCost - the actual amount paid (by account or paymaster) for this
UserOperation
- actualGasUsed - total gas used by this
UserOperation
, including pre-verification, creation, validation and execution - success boolean - whether this execution completed without a revert
- reason - in case of reverted
UserOperation
, the returned revert reason byte array - logs - the logs generated by this particular
UserOperation
, not including logs of otherUserOperations
in the same bundle - receipt the
TransactionReceipt
object. Note that the returnedTransactionReceipt
is for the entire bundle, not only for thisUserOperation
.
* eth_supportedEntryPoints
Returns an array of the EntryPoint
contracts’ addresses supported by the client.
The first element of the array SHOULD
be the EntryPoint
contract addressed preferred by the client.
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_supportedEntryPoints",
"params": []
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
"0xcd01C8aa8995A59eB7B2627E69b40e0524B5ecf8",
"0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6"
]
}
* eth_chainId
Returns EIP-155 Chain ID.
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_chainId",
"params": []
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x1"
}
RPC methods (debug Namespace)
This api must only be available in testing mode and is required by the compatibility test suite.
In production, any debug_*
rpc calls should be blocked.
* debug_bundler_clearState
Clears the bundler mempool and reputation data of paymasters/accounts/factories.
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "debug_bundler_clearState",
"params": []
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "ok"
}
* debug_bundler_dumpMempool
Dumps the current UserOperation
mempool
Parameters:
- EntryPoint the entrypoint used by
eth_sendUserOperation
Returns:
array
- Array of UserOperation
objects currently in the mempool.
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "debug_bundler_dumpMempool",
"params": ["0x1306b01bC3e4AD202612D3843387e94737673F53"]
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
sender, // address
nonce, // uint256
factory, // address
factoryData, // bytes
callData, // bytes
callGasLimit, // uint256
verificationGasLimit, // uint256
preVerificationGas, // uint256
maxFeePerGas, // uint256
maxPriorityFeePerGas, // uint256
signature // bytes
}
]
}
* debug_bundler_sendBundleNow
Forces the bundler to build and execute a bundle from the mempool as handleOps()
transaction.
Returns: transactionHash
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "debug_bundler_sendBundleNow",
"params": []
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "0xdead9e43632ac70c46b4003434058b18db0ad809617bd29f3448d46ca9085576"
}
* debug_bundler_setBundlingMode
Sets bundling mode.
After setting mode to “manual”, an explicit call to debug_bundler_sendBundleNow
is required to send a bundle.
parameters:
mode - ‘manual’ |
‘auto’ |
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "debug_bundler_setBundlingMode",
"params": ["manual"]
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "ok"
}
* debug_bundler_setReputation
Sets the reputation of given addresses.
Parameters:
-
An array of reputation entries to add/replace, with the fields:
address
- the address to set the reputation foropsSeen
- number of times a user operations with that entity was seen and added to the mempoolopsIncluded
- number of times user operations that use this entity was included on-chain
-
EntryPoint the entrypoint used by
eth_sendUserOperation
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "debug_bundler_setReputation",
"params": [
[
{
"address": "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6",
"opsSeen": "0x14",
"opsIncluded": "0x0D"
}
],
"0x1306b01bC3e4AD202612D3843387e94737673F53"
]
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "ok"
}
* debug_bundler_dumpReputation
Returns the reputation data of all observed addresses.
Returns an array of reputation objects, each with the fields described above in debug_bundler_setReputation
.
Parameters:
- EntryPoint the entrypoint used by
eth_sendUserOperation
Return value:
An array of reputation entries with the fields:
address
- the address to set the reputation foropsSeen
- number of times a user operations with that entity was seen and added to the mempoolopsIncluded
- number of times user operation that use this entity was included on-chain-
status
- (string) The status of the address in the bundler ('ok'
'throttled'
'banned'
)
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "debug_bundler_dumpReputation",
"params": ["0x1306b01bC3e4AD202612D3843387e94737673F53"]
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": [
{ "address": "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6",
"opsSeen": "0x14",
"opsIncluded": "0x13",
"status": "ok"
}
]
}
* debug_bundler_addUserOps
Inject UserOperation
objects array into the mempool.
Assume the given UserOperation
objects all pass validation without actually validating them,
and accept them directly into the mempool.
Parameters:
- An array of
UserOperation
objects
# Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "debug_bundler_addUserOps",
"params": [
[
{ sender: "0xa...", ... },
{ sender: "0xb...", ... }
]
]
}
# Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "ok"
}
Rationale
- explicit debug functions: bundlers are required to provide a set of debug functions, so that the “bundler specification test suite” can be used to verify its adherance to the spec.
Backwards Compatibility
This proposal defines a new JSON-RPC API standard that does not pose any backwards compatibility challenges.
Security Considerations
Preventing DoS attacks on UserOperation mempool
Operating a public production ERC-4337 node is a computationally intensive task and may be a target of a DoS attack.
This is addressed by the ERC-7562 validation rules, which defines a way for the ERC-4337 node to track participants’
reputation as well as preventing nodes from accepting maliciously crafted UserOperations
.
It is strictly recommended that all ERC-4337 nodes also implement ERC-7562 validation rules to minimize DoS risks.
Disabling debug
API in production servers
The API defined in the debug
namespace is not intended to ever be publicly available.
Production implementations of ERC-4337 must never make it available by default,
and in fact enabling it should result in a clear warning of the potential dangers of exposing this API.
Copyright
Copyright and related rights waived via CC0.