---
eip: 7769
title: JSON-RPC API for ERC-4337
description: JSON-RPC API methods for communication between smart contract account wallets and ERC-4337 bundlers
author: Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), Alex Forshtat (@forshtat)
discussions-to: https://ethereum-magicians.org/t/erc-7769-json-rpc-for-erc-4337-account-abstraction/21126
status: Draft
type: Standards Track
category: ERC
created: 2024-08-23
requires: 155, 4337, 7562
---

## Abstract

Defines new JSON-RPC API methods which enable [ERC-4337](./eip-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 single `handleOps` 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:

1. **UserOperation** a full user-operation struct.\
  All fields MUST be set as hex values.\
  Empty `bytes` block (e.g. empty `initCode`) MUST be set to `"0x"`\
2. **factory** and **factoryData**\
  Must provide either both of these parameters, or none.
3. **paymaster**, **paymasterData**, **paymasterValidationGasLimit**, **paymasterPostOpGasLimit**\
  Must provide either all of these parameters, or none.
4. **entryPoint** the `EntryPoint` contract address the request should be sent through.\
  This MUST be one of the entry points returned by the `supportedEntryPoints` 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, with `code` and `message`.\
  The error code and message SHOULD be set as follows:
  * **code: -32602** - invalid `UserOperation` struct/fields
  * **code: -32500** - transaction rejected by `EntryPoint` contract's `simulateValidation` function
    during wallet creation or validation
    * The `message` field MUST be set to the emitted `FailedOp` event's "`AAxx`" error message from the `EntryPoint`
  * **code: -32501** - transaction rejected by `paymaster` contract's `validatePaymasterUserOp` function
    * The `message` field SHOULD be set to the revert message from the `paymaster` contract
    * The `data` field MUST contain a `paymaster` value
  * **code: -32502** - transaction rejected because of [ERC-7562](./eip-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 the `validUntil` and `validAfter` values
    * The `data` field SHOULD contain a `paymaster` address if this error was triggered by the `paymaster` contract
  * **code: -32504** - transaction rejected because `paymaster` is throttled or banned due to ERC-7562 reputation rules
    * The `data` field SHOULD contain a `paymaster` address
  * **code: -32505** - transaction rejected because `paymaster` contract's ERC-7562 stake or unstake-delay is too low
    * The `data` field SHOULD contain a `paymaster` address
    * The `data` field SHOULD contain a `minimumStake` and `minimumUnstakeDelay`
  * **code: -32507** - transaction rejected because of wallet signature check failed
  * **code: -32508** - transaction rejected because paymaster balance can't cover all pending `UserOperations`.

##### Example:

Request:

```js
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_sendUserOperation",
  "params": [
    {
      eip7702Auth, // an EIP-7702 authorization tuple
      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:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "message": "AA21 didn't pay prefund",
    "code": -32500
  }
}
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "message": "paymaster stake too low",
    "data": {
      "paymaster": "0x123456789012345678901234567890123456790",
      "minimumStake": "0xde0b6b3a7640000",
      "minimumUnstakeDelay": "0x15180"
    },
    "code": -32504
  }
}
```

##### Support for [EIP-7702](./eip-7702.md) authorizations

On networks with [EIP-7702](./eip-7702) activated, the `UserOperation` object may also contain an `eip7702Auth` tuple.
Notice that according to EIP-7702 an `eip7702Auth` tuple must be provided only to perform a **change** of the authorization address.
Once the necessary `eip7702Auth` tuple was stored on-chain,
users are not required to provide the same `eip7702Auth` tuple for any consequent
`UserOperation`.

Separately, the fields `factory` and `factoryData` have a modified behaviour when using an EIP-7702 authorized `sender`.

The `factory` field SHOULD be set to exactly the `INITCODE_EIP7702_MARKER = 0x7702` flag when using an EIP-7702 authorized `sender`.
Passing his flag instructs the `EntryPoint` contract to verify the `sender` address contains a valid EIP-7702 authorization.

When `INITCODE_EIP7702_MARKER` is specified, the `factoryData` value is passed directly to the `sender` contract,
instead of the `factory` contract.
This is done as a separate call before the `validateUserOp` is called,
meaning the `sender` contract will be called two times during validation.

The `factoryData` value can be left empty. In this case, the call will not be performed.

The purpose of this `factoryData` call is to provide the EIP-7702 `sender` contract an ability to initialize its
storage before accepting the `UserOperation` via the `validateUserOp` function.

#### 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.
If the UserOperation contains an `eip7702Auth` tuple, for the purpose of estimation the signature should be ignored, and the tuple should be evaluated as if it was signed by the `sender`

**Parameters**:
* Same as `eth_sendUserOperation`\
  All gas limits and fees parameters are optional, but are used if specified.\
  `maxFeePerGas` and `maxPriorityFeePerGas` 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 for `eth_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 the `UserOperation` specifies a `Paymaster` 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 by `eth_sendUserOperation`

**Return value**:

* If the `UserOperation` is included in a block:
  * Return a full UserOperation, with the addition of `entryPoint`, `blockNumber`, `blockHash` and `transactionHash`.

* Else if the `UserOperation` is pending in the bundler's mempool:
  *  MAY return `null`, or a full `UserOperation`, with the addition of the `entryPoint` field and a `null` value for `blockNumber`, `blockHash` and `transactionHash`.

* Else:
  * Return `null`

#### eth_getUserOperationReceipt

Return a `UserOperation` receipt object based on a `userOpHash` value returned by `eth_sendUserOperation`.

**Parameters**

* **hash** a `userOpHash` value returned by `eth_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 other `UserOperations` in the same bundle
* **receipt** the `TransactionReceipt` object.
  Note that the returned `TransactionReceipt` is for the entire bundle, not only for this `UserOperation`.

#### 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.

```json=
# Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_supportedEntryPoints",
  "params": []
}

# Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    "0xcd01C8aa8995A59eB7B2627E69b40e0524B5ecf8",
    "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6"
  ]
}
```

#### eth_chainId

Returns [EIP-155](./eip-155.md) Chain ID.

```json=
# 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.

```json
# 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.

```json=
# 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`

```json
# 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'

```json=
# 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 for
  * `opsSeen` - number of times a user operations with that entity was seen and added to the mempool
  * `opsIncluded` - number of times user operations that use this entity was included on-chain

* **EntryPoint** the entrypoint used by `eth_sendUserOperation`

```json=
# 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 for
* `opsSeen` - number of times a user operations with that entity was seen and added to the mempool
* `opsIncluded` - 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'`)

```json=
# 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

```json=
# 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](../LICENSE.md).
