Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-7769: 用于 ERC-4337 的 JSON-RPC API

用于智能合约账户钱包和 ERC-4337 打包器之间通信的 JSON-RPC API 方法

Authors Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), Alex Forshtat (@forshtat)
Created 2024-08-23
Discussion Link https://ethereum-magicians.org/t/erc-7769-json-rpc-for-erc-4337-account-abstraction/21126
Requires EIP-155, EIP-4337, EIP-7562

摘要

定义新的 JSON-RPC API 方法,使 ERC-4337 钱包能够与 UserOpeation mempool 节点和打包器通信,从而匹配以太坊交易已有的功能。

此外,还定义了一组 debug JSON-RPC API 方法,以方便开发、测试和调试 ERC-4337 实现的问题。

动机

在 ERC-4337 中,以太坊中定义的用户交易被替换为 UserOperation 对象,其中包含执行用户请求的操作所需的所有信息。

但是,现有的以太坊 JSON-RPC API 方法不适合与 UserOperation 对象一起使用。 为了方便替代 UserOperation mempool 的运行,重要的是 ERC-4337 协议的所有实现都有一组可以互换使用的标准化 API。

规范

定义

  • 打包器 (bundler):公开 API 的节点,以便将 UserOperation 提交到网络。 打包器将一个或多个 UserOperation 收集到一个 bundle 中,并通过单个 handleOps 调用将它们一起提交到 EntryPoint

RPC 方法(eth 命名空间)

eth_sendUserOperation

eth_sendUserOperation 方法将 UserOperation 对象提交到 UserOperation mempool。 客户端必须验证 UserOperation,并相应地返回结果。

如果请求通过了模拟并且被客户端的 UserOperation 池接受,则结果应该设置为 userOpHash

如果验证、模拟或 UserOperation 池包含失败,则不应返回 userOpHash。相反,客户端应该返回失败原因。

参数:
  1. UserOperation 完整的 user-operation 结构体。 所有字段必须设置为十六进制值。 空的 bytes 块(例如,空的 initCode)必须设置为 "0x"
  2. factory (工厂)factoryData (工厂数据) 必须提供这两个参数,或者都不提供。
  3. paymaster (支付者)paymasterData (支付者数据)paymasterValidationGasLimit (支付者验证 Gas 限制)paymasterPostOpGasLimit (支付者后置操作 Gas 限制) 必须提供所有这些参数,或者都不提供。
  4. entryPoint (入口点) 请求应通过的 EntryPoint 合约地址。 这必须是由 supportedEntryPoints RPC 调用返回的入口点之一。
返回值:
  • 如果 UserOperation 有效,客户端必须返回为其计算的 userOpHash
  • 如果失败,必须返回一个带有 codemessageerror 结果对象。 错误代码和消息应设置如下:
    • code: -32602 - 无效的 UserOperation 结构/字段
    • code: -32500 - 交易被 EntryPoint 合约的 simulateValidation 函数在钱包创建或验证期间拒绝
      • message 字段必须设置为从 EntryPoint 发出的 FailedOp 事件的 “AAxx” 错误消息
    • code: -32501 - 交易被 paymaster 合约的 validatePaymasterUserOp 函数拒绝
      • message 字段应设置为来自 paymaster 合约的回滚消息
      • data 字段必须包含一个 paymaster
    • code: -32502 - 交易因违反 ERC-7562 操作码验证规则而被拒绝
    • code: -32503 - UserOperation 超出时间范围: 钱包或支付者返回了一个时间范围,并且该时间范围已过期或即将过期。
      • data 字段应包含 validUntilvalidAfter
      • 如果此错误是由 paymaster 合约触发的,则 data 字段应包含 paymaster 地址
    • code: -32504 - 交易被拒绝,因为 paymaster 因 ERC-7562 声誉规则而被限制或禁止
      • data 字段应包含 paymaster 地址
    • code: -32505 - 交易被拒绝,因为 paymaster 合约的 ERC-7562 质押或解除质押延迟太低
      • data 字段应包含 paymaster 地址
      • data 字段应包含 minimumStakeminimumUnstakeDelay
    • code: -32507 - 交易被拒绝,因为钱包签名检查失败
    • code: -32508 - 交易被拒绝,因为支付者余额无法覆盖所有待处理的 UserOperations
示例:

请求:

{
  "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
  ]
}

响应:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x123456789012345678901234567890123456789012345678901234567890abcd"
}
示例失败响应:
{
  "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
  }
}
支持 EIP-7702 授权

在激活了 EIP-7702 的网络上,UserOperation 对象也可以包含一个 eip7702Auth 元组。 请注意,根据 EIP-7702,只有在执行授权地址的更改时才必须提供 eip7702Auth 元组。 一旦必要的 eip7702Auth 元组被存储在链上, 用户不需要为任何后续的 UserOperation 提供相同的 eip7702Auth 元组。

另外,当使用 EIP-7702 授权的 sender 时,字段 factoryfactoryData 具有修改后的行为。

当使用 EIP-7702 授权的 sender 时,factory 字段应该设置为完全 INITCODE_EIP7702_MARKER = 0x7702 标志。 传递此标志指示 EntryPoint 合约验证 sender 地址是否包含有效的 EIP-7702 授权。

当指定 INITCODE_EIP7702_MARKER 时,factoryData 值将直接传递给 sender 合约, 而不是 factory 合约。 这是在调用 validateUserOp 之前作为单独的调用完成的, 这意味着在验证期间将调用 sender 合约两次。

factoryData 值可以留空。在这种情况下,将不执行调用。

factoryData 调用的目的是为 EIP-7702 sender 合约提供在通过 validateUserOp 函数接受 UserOperation 之前初始化其存储的能力。

eth_estimateUserOperationGas

估计 UserOperation 的 Gas 值。 给定一个可选的没有 Gas 限制和 Gas 价格的 UserOperation,返回所需的 Gas 限制。 签名字段被钱包忽略,因此该操作不需要用户的批准。 尽管如此,它可能需要放置一个“存根” signature 值,例如正确长度的 signature 字节数组。 如果 UserOperation 包含一个 eip7702Auth 元组,为了估计的目的,应该忽略签名,并且应该像它被 sender 签名一样评估该元组

参数

  • eth_sendUserOperation 相同 所有 Gas 限制和费用参数都是可选的,但如果指定则会使用。 maxFeePerGasmaxPriorityFeePerGas 默认为零,因此帐户和支付者都不需要付款。
  • 可选地接受 State Override Set (状态覆盖集合) 以允许用户在 Gas 估计期间修改状态。 此字段及其行为与为 eth_call RPC 方法定义的字段等效。

返回值:

  • preVerificationGas (预验证 Gas)UserOperation 的 Gas 开销
  • verificationGasLimit (验证 Gas 限制) 验证此 UserOperation 所需的 Gas 限制估计值
  • paymasterVerificationGasLimit (支付者验证 Gas 限制) 支付者验证所需的 Gas 限制估计值 仅当 UserOperation 指定 Paymaster 地址时才返回
  • callGasLimit (调用 Gas 限制) 内部帐户执行所需的 Gas 限制估计值

注意: 实际的 postOpGasLimit 无法可靠地估计。 支付者应向帐户提供此值,并要求在链上验证期间使用特定值。

错误代码:

eth_sendUserOperation 相同 如果对帐户合约的内部调用回退,或者支付者的 postOp 调用回退,则此操作也可能返回错误。

eth_getUserOperationByHash

基于 eth_sendUserOperation 返回的 userOpHash 值返回一个 UserOperation 对象。

参数

  • hash eth_sendUserOperation 返回的 userOpHash

返回值

  • 如果 UserOperation 包含在一个区块中:
    • 返回完整的 UserOperation,并添加 entryPointblockNumberblockHashtransactionHash
  • 否则,如果 UserOperation 在打包器的 mempool 中处于待处理状态:
    • 可以返回 null,也可以返回完整的 UserOperation,并添加 entryPoint 字段和 blockNumberblockHashtransactionHashnull 值。
  • 否则:
    • 返回 null

eth_getUserOperationReceipt

基于 eth_sendUserOperation 返回的 userOpHash 值返回一个 UserOperation 收据对象。

参数

  • hash eth_sendUserOperation 返回的 userOpHash

返回值

如果 UserOperation 尚未包含在区块中,则返回 null,或者:

  • userOpHash 请求哈希
  • entryPoint
  • sender
  • nonce
  • paymaster 用于此 userOp 的支付者(或为空)
  • actualGasCost - 为此 UserOperation 支付的实际金额(由帐户或支付者支付)
  • actualGasUsed - 此 UserOperation 使用的总 Gas,包括预验证、创建、验证和执行
  • success 布尔值 - 此执行是否在没有回退的情况下完成
  • reason - 如果 UserOperation 回退,则返回回退原因字节数组
  • logs - 此特定 UserOperation 生成的日志,不包括同一 bundle 中其他 UserOperations 的日志
  • receipt TransactionReceipt 对象。 请注意,返回的 TransactionReceipt 适用于整个 bundle,而不仅仅是此 UserOperation

eth_supportedEntryPoints

返回客户端支持的 EntryPoint 合约地址的数组。 数组的第一个元素应该是客户端首选的 EntryPoint 合约地址。

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

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

eth_chainId

返回 EIP-155 Chain ID。

# Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_chainId",
  "params": []
}

# Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}

RPC 方法(debug 命名空间)

此 API 必须仅在测试模式下可用,并且是兼容性测试套件所必需的。 在生产环境中,任何 debug_* RPC 调用都应被阻止。

debug_bundler_clearState

清除支付者/帐户/工厂的打包器 mempool 和声誉数据。

# Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_clearState",
  "params": []
}

# Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "ok"
}

debug_bundler_dumpMempool

转储当前的 UserOperation mempool

参数:

  • EntryPoint eth_sendUserOperation 使用的入口点

返回:

array - 当前在 mempool 中的 UserOperation 对象数组。

# 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

强制打包器从 mempool 构建并执行一个 bundle 作为 handleOps() 交易。

返回:transactionHash

# Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_sendBundleNow",
  "params": []
}

# Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xdead9e43632ac70c46b4003434058b18db0ad809617bd29f3448d46ca9085576"
}

debug_bundler_setBundlingMode

设置打包模式。

在将模式设置为“manual”后,需要显式调用 debug_bundler_sendBundleNow 才能发送 bundle。

参数:
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

设置给定地址的声誉。

参数:

  • 要添加/替换的声誉条目的数组,其中包含以下字段:

    • address - 要设置声誉的地址
    • opsSeen - 具有该实体的用户操作被看到并添加到 mempool 的次数
    • opsIncluded - 使用此实体的用户操作被包含在链上的次数
  • EntryPoint 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

返回所有观察到的地址的声誉数据。 返回声誉对象数组,每个对象都包含上面 debug_bundler_setReputation 中描述的字段。

参数:

  • EntryPoint eth_sendUserOperation 使用的入口点

返回值:

声誉条目的数组,其中包含以下字段:

  • address - 要设置声誉的地址
  • opsSeen - 具有该实体的用户操作被看到并添加到 mempool 的次数
  • opsIncluded - 使用此实体的用户操作被包含在链上的次数
  • status - (字符串) 地址在打包器中的状态 ('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

UserOperation 对象数组注入到 mempool 中。 假设给定的 UserOperation 对象都通过了验证,而没有实际验证它们, 并将它们直接接受到 mempool 中。

参数:

  • UserOperation 对象数组
# Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "debug_bundler_addUserOps",
  "params": [
    [
      { sender: "0xa...", ... },
      { sender: "0xb...", ... }
    ]
  ]
}

# Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "ok"
}

理由

  • 显式调试功能:打包器需要提供一组调试功能,以便可以使用“打包器规范测试套件”来验证其是否符合规范。

向后兼容性

此提案定义了一个新的 JSON-RPC API 标准,该标准不会造成任何向后兼容性挑战。

安全注意事项

防止 UserOperation mempool 上的 DoS 攻击

运行公共生产 ERC-4337 节点是一项计算密集型任务,并且可能是 DoS 攻击的目标。 这通过 ERC-7562 验证规则来解决,该规则定义了一种 ERC-4337 节点跟踪参与者 声誉以及防止节点接受恶意制作的 UserOperations 的方法。

强烈建议所有 ERC-4337 节点也实施 ERC-7562 验证规则,以最大限度地降低 DoS 风险。

在生产服务器中禁用 debug API

debug 命名空间中定义的 API 并非旨在公开可用。 ERC-4337 的生产实现绝不能默认使其可用, 事实上,启用它应该会导致明确警告暴露此 API 的潜在危险。

版权

通过 CC0 放弃版权及相关权利。

Citation

Please cite this document as:

Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), Alex Forshtat (@forshtat), "ERC-7769: 用于 ERC-4337 的 JSON-RPC API [DRAFT]," Ethereum Improvement Proposals, no. 7769, August 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7769.