{"content":{"title":"zksync Era 官方文档抽象账户部分中文翻译","body":"# zksync Era 中的抽象账户与 EIP-4337 的抽象账户\r\n\r\nZKsync 和以太坊 EIP 4337 的原生账户抽象旨在增强账户的灵活性和用户体验，但它们在以下关键方面有所不同：\r\n\r\n1. 实现层级\r\n\r\n    * ZKsync 的账户抽象在协议层级（我的理解是从底层）进行集成；但`EIP-4337` 避开了协议级别的实现（众所周知，`EIP-4337`期望在应用层实现）。\r\n2. 账户类型\r\n\r\n    * 在 ZKsync Era中，智能合约账户和`paymasters`都是第一类型账户。在底层中，所有的账户（包括 EOA）的行为也和智能合约账户类似（都是合约函数的执行），同时所有的账户都支持使用`paymasters`\r\n3. 交易处理\r\n\r\n    * `EIP-4337`为智能合约账户引入了单独的交易流程：通过依赖于单独的内存池以及`Bundlers`节点（能够打包用户操作，并将其发送给`EntryPoint`合约 ）进行用户的操作，这使得出现了两个独立交易流程（一个是正常以太坊上的交易流程，一个是依靠`Bundlers`等建立的交易流程）\r\n    * ZKsync Era 上存在一个统一的内存池给账户（包括 EOA 和智能合约账户）交易使用。ZKsync Era 中 Operator （定序器）承当了`Bundlers`的角色，对于用户（EOA 和合约账户一样）发起的交易将被发送到`Bootloader`（类似于`EntryPoint`合约的角色），从而产生一个`mempool`与交易流。\r\n4. Paymasters 支持：\r\n\r\n    * ZKsync Era 通过单一的交易流程（不再像`EIP-4337`中分为两个部分），允许 EOA 和智能合约账户从`paymasters`中受益\r\n    * `EIP-4337`中对 EOA 而言并不支持`paymasters`，因为`paymasters`仅在与智能合约交互中实现。\r\n\r\n\r\n# 介绍\r\n\r\n以太坊中有两种类型的账户：\r\n\r\n* 外部拥有账户（EOA，即用户使用私钥直接控制进行交易的账户）\r\n* 合约账户（拥有代码的账户，不存在私钥，所以用户不能直接控制进行交易）\r\n\r\n在 ZKsync Era 中实现这样的账户：可以直接发起一笔交易（如 EOA 一般），也能在其中实现一些逻辑（即放入智能合约代码），这种账户称为抽象账户（AA 账户）。\r\n\r\n由于用户的账户可以是 AA 账户，所以可以对自己的账户进行编程，然后用户自己调用自己账户上的函数进行交易，因此，用户可以自定义签名算法、多签、支出限制等。同时，用户可以通过`paymasters`来帮助交易，即交易的`gas`由`paymasters`付出（前提是用户在`paymasters`有存款或授权一定数量的 ERC20 代币）。这使得用户在区块链上能有更好的体验了。\r\n\r\n\r\n# 设计\r\n\r\nZKsync 上的帐户抽象协议与 `EIP-4337`非常相似，但为了效率和更好的用户体验，仍然有所不同。\r\n\r\n## Nonce 的唯一性\r\n\r\n* 当前的模型不允许自定义钱包同时发送多笔交易并保持确定的顺序\r\n* 对于 EOA，Nonce 的计数会依次增长（每笔交易都会递增）；对于自定义账户，无法保证交易顺序\r\n* 未来计划切换到可以在顺序或任意 Nonce 之间选择的模型\r\n\r\n每个区块中，对于每个交易都有一个重要的不变量，唯一的交易哈希值。虽然账户可以接收多个完全相同的交易，但对于抽象账户而言并不容易。尽管这些交易在技术上是合规的，但索引器和其他工具很难处理违反了哈希唯一的情况。\r\n\r\n需要有一种的协议级别的方法，来保证交易哈希不重复的方法，最简单又便宜的方法就是让让交易对（发送者、Nonce）始终唯一。\r\n\r\n就采用了以下协议：\r\n\r\n* 每个交易开始前，系统会通过`NonceHolder`（会为每个账户记录使用过的 Nonce）检查 Nonce 是否已被使用\r\n* 如果 Nonce 尚未使用，则开始对交易的验证（验证交易是否满足执行条件），在验证期间，提供的随机数被标记为`used`。\r\n* 验证完成后，系统检查 Nonce 是否标记为已使用。\r\n\r\n用户可以使用一个任意的 256 bit 的数字作为 Nonce，并且可以在系统合约中对应的 key 下放置任意的非零值。但仅有协议层面支持，但在 server 端并不支持这么做，目前仅支持通过顺序使用 Nonce 值（使用`incrementMinNonceIfEquals`方法，重新标记能使用的最低 Nonce）。\r\n\r\n\r\n## 标准化交易哈希\r\n\r\n在未来，计划在 ZKsync 上支持高效的交易包含证明。这需要我们在`Bootloader`中计算交易哈希。因为这种计算需要消耗`gas`，所以将计算交易哈希交给 AA 方法的接口才公平（防止账户可能由于某些原因需要该值），这也是为何下面描述的`IAccount`与`IPaymaster`接口的所有的方法都包含交易哈希以及推荐的签名摘要（由 EOA 对本次交易进行签名的摘要）。\r\n\r\n\r\n## Interface\r\n\r\n### IAccount interface\r\n\r\n这个就是建议每个账户都需要实现的 IAccount interface。它包含五个接口：\r\n\r\n> 个人认为下面的”系统“通常指`Bootloader`，但又有时候表示更大的概念，即整个 zksync 系统\r\n\r\n* `validateTransaction`：是必须实现的，系统需要它来确定 AA 逻辑是否同意继续进行交易。当交易不合规（如签名错误），该方法将会`revert`；如果调用成功，则认为已实现的账户逻辑已同意交易，系统将继续进行交易。\r\n* `executeTransaction`：是必须实现的，当用户支付费用（应该就是`gas`费）后，系统将会调用。该函数会执行交易的实施。\r\n* `payForTransaction`：是可选实现的，当交易没有`paymaster`时，系统将会调用它。这个方法是由当前账户支付交易费用，如果当前账户永远不会支付任何费用，并且始终都会依赖`paymaster`进行费用支付，那么可以不实现。这个方法将会发送至少`tx.gasprice * tx.gasLimit`的 ETH 给`Bootloader`（如果被调用的话）。\r\n* `prepareForPaymaster`：是可选实现的，如果交易有`paymaster`代为支付交易费用，那么系统将会调用它。这个方法用来准备和`paymaster`进行交互（最著名的示例就是使用 ERC20 让`paymaster`代为支付交易费用）。\r\n* `executeTransactionFromOutside`：是可选实现的，但强烈推荐实现，因为在 priority 模式下的某些情况时（如 operator 没有响应），需要从你那来自”外部“的账户开始交易（即从 EOA 开始一个智能合约的交易，如以太坊上一样）。\r\n\r\n\r\n### IPaymaster interface\r\n\r\n与`EIP-4337`相同，ZKsync 同样支持`paymaster`：用于代付其他账户交易执行费用的账户。每个`paymaster`都需要实现 IPaymaster interface，其包含以下两个方法：\r\n\r\n* `validateAndPayForPaymasterTransaction`：是必须实现的，系统需要使用它来确认`paymaster`是否同意代付这笔交易。如果同意，则这个方法至少发送`tx.gasprice * tx.gasLimit`的 ETH 给 operator 。它需要返回`context`来作为`postTransaction`方法的调用参数之一。\r\n* `postTransaction`：是可选实现的，在交易执行后被调用。与`EIP-4337`不同，并不能保证会调用此方法，尤其是交易因为`out of gas`错误而失败。其需要四个参数：\r\n\r\n  > 还有一个变量`_suggestedSignedHash`不知道为啥官方文档里不讲，表示交易哈希，由 EOA 进行签名\r\n  >\r\n\r\n  * `_context`：`validateAndPayForPaymasterTransaction`的返回\r\n  * `_txHash`：交易本身\r\n  * `_txResult`：指示交易是否执行成功\r\n  * `_maxRefundedGas`：可以退还给`paymaster`的`gas`的最大数量上限\r\n\r\n\r\n## Transaction 结构体的保留字段\r\n\r\n对于上面的每个方法都会接收 Transaction 结构，虽然大多数字段不言自明，但仍旧有 6 个`reserved`字段（在下面结构中貌似被单独命名了出来，并没有如文档所讲放在`reserved`中），不命名的原因在于：未来的某些交易类型中可能不需要他们。目前为：\r\n\r\n* `reserved[0]`：是 Nonce\r\n* `reserved[1]`：是随交易传递的`msg.value`\r\n\r\n```solidity\r\nstruct Transaction {\r\n    // The type of the transaction.\r\n    uint256 txType;\r\n    uint256 from;\r\n    uint256 to;\r\n    uint256 gasLimit;\r\n    uint256 gasPerPubdataByteLimit;\r\n    uint256 maxFeePerGas;\r\n    uint256 maxPriorityFeePerGas;\r\n    uint256 paymaster;\r\n    uint256 nonce;\r\n    uint256 value;\r\n    uint256[4] reserved;\r\n    bytes data;\r\n    bytes signature;\r\n    bytes32[] factoryDeps;\r\n    bytes paymasterInput;\r\n    bytes reservedDynamic;\r\n}\r\n```\r\n\r\n\r\n## 交易流程\r\n\r\n每笔交易都会有如下流程处理\r\n\r\n### 验证\r\n\r\n验证过程就是：系统确定交易是否能够执行。如果这笔交易在任何验证点失败，则不会向用户收取任何费用，并且交易不会包含在区块中（应该是指这笔交易不会有任何的记录，即相当于没有这笔交易）。\r\n\r\n验证过程的步骤：\r\n\r\n1. Nonce 验证：验证交易的 Nonce 是否被使用过\r\n2. 交易验证：调用账户上的`validateTransaction`方法，检查交易是否合规。方法执行成功且没有 revert 则进行下一步\r\n3. 标记 Nonce：验证通过后，标记交易的 Nonce 已经使用\r\n4. 费用处理：这个部分分为两种（根据是否使用`paymaster`，但结果应该都是向`Bootloader`支付费用）：\r\n\r\n    * Standard Transactions：在账户上使用`payForTransaction`方法（账户自己付款），当此方法没有 revert ，则继续\r\n    * Paymaster Transactions：首先交易发送者调用`prepareForPaymaster`，成功执行后调用（应该是由系统调用）`paymaster`的`validateAndPayForPaymasterTransaction`函数。如果两个函数都没有 revert，则继续\r\n5. 资金验证：系统确定`Bootloader`至少收到`tx.gasPrice * tx.gasLimit`的 ETH。如果所需的资金已经保存了，则交易被视为已经验证，准备进行下一步\r\n\r\n\r\n### 执行\r\n\r\n执行步骤负责执行实际的交易操作，并将任何为使用的`gas`退还给用户，即使此步骤中出现 revert 情况，交易人就包含在区块中。\r\n\r\n执行过程步骤：\r\n\r\n1. 交易执行：调用账户上的`executeTransaction`方法，执行交易\r\n2. `paymaster`的交易后处理（仅在涉及`paymaster`时适用）：调用`paymaster`的`postTransaction`方法。这个方法通常将未使用的`gas`退还给发送者（应该就是当前账户了），尤其是`paymaster`采用的是 ERC20 代币作为代付费用（即向当前账户收取 ERC20 ，然后代付`gas`）。\r\n\r\n\r\n### 费用\r\n\r\n不同协议之间处理对于交易费的处理不同，如 `EIP-4337` 和 ZKsync 之间。\r\n\r\n#### EIP-4337 的 Gas Limit\r\n\r\n`EIP-4337`定义了三种`Gas Limit`来管理不同交易阶段的成本（应该是指将不同阶段的`gas`费用分开统计）：\r\n\r\n* `verificationGas`：包含交易验证所需的`gas`\r\n* `executionGas`：交易执行所需的`gas`\r\n* `preVerificationGas`：在主要验证前所需的`gas`（这个不太能理解，但看下面的描述，是转账 ERC20 的费用？）\r\n\r\n\r\n#### ZKsync Era 中统一的 Gas Limit\r\n\r\n在 ZKsync Era 中，使用单个`Gas Limit`字段处理所有与交易相关的成本，所以这个统一的`Gas Limit`需要包含：\r\n\r\n* 交易验证\r\n* 代支付逻辑费用（包括任意 ERC20 转账的费用）\r\n* 交易本身的执行\r\n\r\n\r\n#### 估算 Gas\r\n\r\n默认情况下，`estimateGas`函数计算需要的`gas`量，并包含一个常数。这个常数用于 EOA 交易的支付费用和签名验证（没看懂这句话）。\r\n\r\n\r\n## 使用 SystemContractsCaller 库\r\n\r\n为了安全起见，`NonceHolder`（管理账户 Nonce）和`ContractDeployer`（负责部署合约）的系统合约都只能在拥有`isSystem`的特殊标志时调用。想要拥有此特殊标志来进行调用，需要使用 SystemContractsCaller 库中的`systemCall`或`systemCallWithPropagatedRevert`或`systemCallWithReturndata`（后面两种内部都是调用了`systemCall`）方法。\r\n\r\n当开发自定义的账户时，基本上都必定使用这个库，因为这是调用`NonceHolder`系统合约的非视图的唯一方法。此外，如果想允许用户自己部署自己的合约，则必须使用这个库。可以使用 EOA 账户的实现（默认账户实现，所有没有包含代码的账户默认继承的）作为参考。\r\n\r\n\r\n# 扩展 EIP-4337\r\n\r\n主要是 `EIP-4337`对 ZKsync 原生帐户抽象的扩展概述。\r\n\r\n为了向 operator 提供 Dos 保护，`EIP-4337`对账户的验证步骤施加了多项限制（如不允许访问能够变化的信息，如当前块时间、数字、哈希）。其中大多数，尤其是那些和禁止操作码相关的内容，仍然具有意义，但为了用户有更好的体验，其中的一些限制已经取消\r\n\r\n## 拓展允许的操作码\r\n\r\n允许使用已部署的`call`/`delegateCall`/`staticcall`合约。和以太坊不同，我们无法编辑已部署的代码或通过自毁删除合约，所以我们可以确保合约执行期间的代码是相同的。\r\n\r\n## 扩展属于用户的 slots 集\r\n\r\n> 这一部分就没看懂\r\n\r\n在原本的`EIP`中，AA 的`validateTransaction`步骤（检查交易是否合规）只允许读取自己存储的 slots 。但有些 slots 在语义上属于该用户，但实际上位于另一个合约的地址上，例如 ERC20 的余额。\r\n\r\n此限制通过确保各个账户用于验证的 slots 不重叠来提供 DDos 安全性，因此他们不需要实际上属于账户的存储空间中。\r\n\r\n为了能够在验证步骤中读取用户的 ERC20 余额或授权额度，在验证步骤中地址为 A 的账户将允许使用以下类型的 slots：\r\n\r\n* 属于地址 A 的 slots\r\n* 任何其他地址上的 slots A（这个不懂）\r\n* 然和其他地址上的`keccak256(A || X)`类型的 slots（包阔`mapping(address => value)`，通常用于 ERC20 代币中的余额）\r\n\r\n\r\n## 未来会允许什么？\r\n\r\n未来可能允许有时限的交易，例如允许检查`block.timestamp <= value`是否返回`false`等等。这将需要部署此类可信方法的库，但它将大大增强账户的功能。\r\n\r\n\r\n# 建立智能账户\r\n\r\n想在 zksync 上构建自定义账户（应该就是合约账户，带有合约的抽象账户），开发人员必须实现特定的接口，并遵循推荐的账户部署和管理的最佳实践。\r\n\r\n## 接口实现\r\n\r\n每个自定义账户都应该实现 IAccount interface（这个可以在 [DefaultAccount.sol ](https://github.com/matter-labs/era-contracts/blob/main/system-contracts/contracts/DefaultAccount.sol)中找到示例）。\r\n\r\n当外部地址调用时，此实现会返回空值，这可能不是您的自定义帐户所希望的行为。\r\n\r\n\r\n## EIP-1271 集成\r\n\r\n对于智能钱包，强烈鼓励实施`EIP-1271`签名验证方案（让合约能够和EOA账户一样能够对消息进行签名，同时可以验证签名）。该标准得到了 ZKsync 团队的认可，并且是签名验证库的组成部分。\r\n\r\n\r\n## 部署流程\r\n\r\n部署账户的逻辑与部署标准智能合约的过程类似，但为了区分不打算被视为账户的智能合约，需要使用`ContractDeployer`系统合约的`createAccount`/`create2Account`方法，而不是使用`create`/`create2`（从代码中看没啥区别，因为`create`中调用了`createAccount`）。\r\n\r\n### 使用 zksync-ethers SDK 的示例 (v5)\r\n\r\n```ts\r\nimport { ContractFactory } from \"zksync-ethers\";\r\n\r\nconst contractFactory = new ContractFactory(abi, bytecode, initiator, \"createAccount\");\r\nconst aa = await contractFactory.deploy(...args);\r\nawait aa.deployed();\r\n```\r\n\r\n\r\n## 验证步骤限制\r\n\r\n目前自定义账户验证规则尚未完全强制执行，将来可能变化：\r\n\r\n* 账户只能与属于他们自己的 slots 进行交互\r\n* 账户逻辑中禁止使用上下文变量（如`block.number`）\r\n* 账户必须将 Nonce 加一，以保持哈希冲突的抵抗力\r\n\r\n\t这些限制尚未在 电路/VM 级别强制执行，并且不适用于 L1->L2 事务。\r\n\r\n\r\n## Nonce 管理\r\n\r\n交易和部署的 Nonce 都被整合到`NonceHolder`中进行优化。使用`incrementMinNonceIfEquals`函数安全的增加账户的 Nonce。\r\n\r\n\r\n## 发送交易\r\n\r\n目前仅支持`EIP-712`（对结构化数据进行哈希和签名的标准）格式的交易从自定义账户发送。交易必须指定`from`字段作为账户地址，并在`customData`中包含`customSignature`。\r\n\r\n### 交易提交示例\r\n\r\n```ts\r\nimport { utils } from \"zksync-ethers\";\r\n\r\n// Here, `tx` is a `TransactionRequest` object from `zksync-ethers` SDK.\r\n// `zksyncProvider` is the `Provider` object from `zksync-ethers` SDK connected to the ZKSync network.\r\ntx.from = aaAddress;\r\ntx.customData = {\r\n  ...tx.customData,\r\n  customSignature: aaSignature,\r\n};\r\nconst serializedTx = utils.serialize({ ...tx });\r\n\r\nconst sentTx = await zksyncProvider.sendTransaction(serializedTx);\r\n```\r\n\r\n\r\n# Paymasters\r\n\r\n`Paymasters`时专门设计用来资助用户交易费用的账户，增强协议的可用性和灵活性，方便用户使用 ERC20 代币而不是 ETH 支付费用。\r\n\r\n## 与 Paymasters 交互\r\n\r\n要使用`paymaster`，用户必须在其`EIP-712`交易中指定非零的`paymaster`地址，并在`paymasterInput`字段中指定相关数据。\r\n\r\n### Paymasters 验证规则\r\n\r\n* 验证规则尚未完全强制执行\r\n* 不遵守这些规则的`paymaster`将来可能会停止正常运行\r\n\r\n为了减少恶意`paymaster`潜在的 Dos 攻击，使用了类似于`EIP-4337`的声誉评分系统。但与`EIP-4337`不同，zksync 中的`paymaster`可以与任何存储 slots 交互，并且在特定条件下不会受到限制，如自上次成功验证以来经过的时间或连续的（consistent，可能也可以翻译为一致的） slots 访问模式\r\n\r\n## 内置 Paymasters 流程\r\n\r\n`paymaster`可以自动操作或需要用户交互，这取决于其设计。例如将 ERC20 代币兑换成 ETH 的`paymaster`将要求用户授予必要限额。\r\n\r\n账户抽象协议本身是通用的，允许账户和`paymaster`实现任意交互。但默认账户（EOA）的代码不变，但人就希望他们能够参与自定义账户和`paymaster`的生态中。这就是为什么对交易的`paymasterInput`字段进行了标准化，以涵盖大多数常见的`paymaster`功能用例。\r\n\r\n你的账户可以自由选择实现或不实现这些流程的支持，但强烈建议保持 EOA 和自定义账户的接口相同。\r\n\r\n\r\n### 一般 Paymasters 流程\r\n\r\n当`paymaster`不需要用户执行任何初始操作时，使用此流程：\r\n\r\n`paymasterInput`字段必须为具有以下接口的函数的调用进行编码：\r\n\r\n```solidity\r\nfunction general(bytes calldata data);\r\n```\r\n\r\n对于 EOA 账户，此输入通常不起作用，但`paymaster`可以根据需求进行说明（不太清楚这个函数具体有什么作用）。\r\n\r\n### 基于授权的 Paymasters 流程\r\n\r\n当用户必须为`paymaster`设置 token 限额时，此流程十分重要。`paymasterInput`字段必须为对具有以下签名的函数的调用进行编码：\r\n\r\n```solidity\r\nfunction approvalBased(\r\n    address _token,\r\n    uint256 _minAllowance,\r\n    bytes calldata _innerInput\r\n);\r\n```\r\n\r\nEOA 将确保支付给`paymaster`的`_token`的限额至少设为`_minAllowance`。`_innerInput`参数是一个额外的有效负载，可以发送给`paymaster`，以实现任意逻辑（如可以由`paymaster`验证的额外签名或密钥）。\r\n\r\n如果正在开发`paymaster`，则不应该相信交易的发送者会诚实行事（如通过`approvalBased`流程提供所需的限额）。这些流程主要作为对 EOA 的指示，而`paymaster`应始终仔细检查这些要求。\r\n\r\n\r\n## 测试网 Paymasters\r\n\r\n为保证用户在测试网上体验到`paymaster`，同样继续支持使用 ERC20 代币支付费用，Matter Labs 团队提供了测试网`paymaster`，可以将 ERC20 代币与 ETH 以 1:1 的汇率（1 ERC20 代币等于 1 wei 的 ETH）支付费用。\r\n\r\n`paymaster`仅支持基于授权的`paymaster`流程，并要求`token`参数等于正在交换的`token`，并且`minAllowance`等于`tx.maxFeePerGas * tx.gasLimit`。此外测试网`paymaster`不适用`_innerInput`参数，因此不应提供任何内容（空`bytes`）。\r\n\r\n\r\n## 与 Paymasters 交互时估算 Gas 费用\r\n\r\n由于额外的计算和操作，与`paymaster`的交互通常比标准交易消耗更多的`gas`，导致`gas`增加的主要原因是：\r\n\r\n1. 内部计算：包括`paymaster`的`validateAndPayForPaymasterTransaction`和`postTransaction`的内部操作。\r\n2. 资金转账：`paymaster`将资金发送到`bootloader`时消耗的`gas`\r\n3. ERC20 token 限额的管理：是可选项产生的`gas`，如果用户使用 ERC20 代币补偿`paymaster`，管理代币的限额会消耗额外的`gas`。\r\n\r\n* 用于内部计算的`gas`一般是最少的，具体取决于`paymaster`的实现\r\n* 转移资金的成本与用户能为类似交易支付的费用相当\r\n* 管理 ERC20 限额会显著影响`gas`的使用量，尤其是用户第一次设置限额。这个过程可能需要发布一个 32 byte 的存储密钥标识符，可能会以 50 gwei 的 L1 gas 价格使用多达 400k 的`gas`。需要注意，虽然交易流程在执行结束时，将存储 slot 清空（因此”授予`X`限额+`paymaster`花费所有限额），但初始成本是在执行期间预先收取的，只有在交易结束时 slot 归零后才会退款给用户。\r\n\r\n\r\n### 准确估计 Gas 的重要性\r\n\r\n准确的估计`gas`至关重要，尤其是对于涉及大量`pubdata`的操作，例如写入存储。你应该在估算过程中包含`paymasterInput`来保证准确考虑了`paymaster`的参与。以下代码片段来自定义的`paymaster`教程，演示了如何进行此估算：\r\n\r\n```ts\r\nconst gasLimit = await erc20.estimateGas.mint(wallet.address, 5, {\r\n  customData: {\r\n    gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT,\r\n    paymasterParams: paymasterParams,\r\n  },\r\n});\r\n```\r\n\r\n此处`paymasterParams`包括`paymaster`的地址和他的输入。然而`paymasterInput`通常包含难以提前预测的参数，例如用户所需的确切的代币数量。\r\n\r\n此外，`paymaster`可能需要验证价格数据或转换率，可能需要服务端的签名。\r\n\r\n\r\n### 处理复杂的依赖关系\r\n\r\n对于如涉及依赖交易内容的这些签名的这种复杂依赖关系，产生了挑战：\r\n\r\n* 从`validateAndPayForPaymasterTransaction`中返回`magic = 0`可以模拟有效签名验证的`gas`消耗。这确保了尽管交易会由于`magic = 0`而在主网上失败，但仍旧可以估计出正确的`gas`数量。\r\n* `gas`的估算本质上时对防止交易失败的最低`gas`量的二分搜索。如果验证始终失败，那么`gas`估计也会失败，因为系统将不断尝试增加`gas limit`。\r\n\r\n\r\n### 提供授权估算的策略\r\n\r\n1. 粗略估计：如果对于涉及的资金有一个大概的认识，用它来进行估计。由于缓冲已经包含在估计中，所以微小的差异通常不会导致交易失败。但如果用户的余额在估计和交易执行之间发生意外的变化，可能会出现差异\r\n2. 单独估计限额的设置：或者估算用户为交易的`gas`单独设置的限额。将此估算添加到原始交易的估算的成本中。这种方法考虑了 Nonce 的更改和一般验证逻辑，但可能会带来明显的开销。\r\n\r\n每种方法都有其优点和缺点，选择正确的方法取决于交易的具体情况和`paymaster`的需求。\r\n\r\n# 签名验证\r\n\r\n推荐的签名验证方法。如果项目准备支持原生 AA ，那么强烈建议这么做，这将允许自主的成千上万用户（许多新钱包都默认是智能账户，为用户提供更加流畅的体验）。我们预计未来会有更多用户转向智能钱包。\r\n\r\n建立的各种不同类型的帐户之间最显着的区别是不同的签名方案。我们希望帐户支持`EIP-1271`标准。\r\n\r\n[`@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5ed5a86d1d22f387ce69ab4e0ace405de8bc888d/contracts/utils/cryptography/SignatureChecker.sol#L22)库提供了一种验证不同账户实现的签名方法。当需要检查账户签名是否正确时，强烈建议使用这个库。\r\n\r\n## 将库添加到项目中\r\n\r\n```bash\r\nnpm add @openzeppelin/contracts\r\n```\r\n\r\n## 使用库的示例\r\n\r\n```solidity\r\npragma solidity ^0.8.0;\r\n\r\nimport { SignatureChecker } from \"@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol\";\r\n\r\ncontract TestSignatureChecker {\r\n    using SignatureChecker for address;\r\n\r\n    function isValidSignature(\r\n        address _address,\r\n        bytes32 _hash,\r\n        bytes memory _signature\r\n    ) public pure returns (bool) {\r\n        return _address.isValidSignatureNow(_hash, _signature);\r\n    }\r\n}\r\n```\r\n\r\n## 验证 AA 签名\r\n\r\n不建议使用`ethers.js`库来验证用户签名。官方 SDK 通过`utils`提供了两种方法来验证账户的签名：\r\n\r\n```ts\r\nexport async function isMessageSignatureCorrect(address: string, message: ethers.Bytes | string, signature: SignatureLike): Promise<boolean>;\r\n\r\nexport async function isTypedDataSignatureCorrect(\r\n  address: string,\r\n  domain: TypedDataDomain,\r\n  types: Record<string, Array<TypedDataField>>,\r\n  value: Record<string, any>,\r\n  signature: SignatureLike\r\n): Promise<boolean>;\r\n```\r\n\r\n目前这些方法仅支持验证 ECDSA 签名，但很快将支持`EIP-1271`签名验证。这两种方法都会返回`true`或`false`，具体取决于消息签名是否正确。\r\n\r\n# 参考文章\r\n\r\n[ZKsync Era Protocol](https://docs.zksync.io/build/developer-reference/protocol)"},"author":{"user":"https://learnblockchain.cn/people/19699","address":"0xbe3eB708d6F16Bb0EB751e1f3d49449590eE29A9"},"history":"bafkreiaa2vbwdbntks2oijqdjcw5mszbimcltzgwmfu3goagihtt2srjje","timestamp":1721374015,"version":1}