diff --git a/ERCS/erc-7769.md b/ERCS/erc-7769.md new file mode 100644 index 0000000000..ab8646f78f --- /dev/null +++ b/ERCS/erc-7769.md @@ -0,0 +1,543 @@ +--- +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: + +```json +{ + "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: + +```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 + } +} +``` + +#### * 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` 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).