如何实现 EVM 链无许可、无感知的合约部署

  • thirdweb
  • 更新于 2024-11-07 17:15
  • 阅读 611

如何实现 EVM 链无许可、无感知的合约部署

介绍

在 thirdweb,我们努力使我们的工具具备 EVM 链的无感知,包括我们的智能合约基础设施。如今,任何人在 900 多个 EVM 链上部署 thirdweb 合约的成本只需常规部署的一小部分 gas 费用。

该过程使用无密钥签名者和确定性合约地址,从而形成一个完全无权限的系统,可以部署任何基础设施合约,而无需我们的干预或我们在任何链上持有资金。

通过无密钥签名者和 create2 工厂进行无权限部署

通过无密钥签名者和 create2 工厂进行无权限部署

在本文中,我们想分享我们如何实现这个无权限部署系统以及我们在过程中遇到的问题。

thirdweb 合约的构成

预构建合约是 thirdweb 工具套件的核心功能之一。这些是可从 thirdweb 仪表板或 SDK 部署的即用型合约,为 web3 应用程序提供构建模块,例如账户、代币、NFT、版本、市场、质押等。

用户部署一个预构建合约,本质上是部署一个完整实现的“克隆” (ERC1167 Minimal Proxies),该实现通过工厂进行部署,如下所示。此类部署成本低廉,同时保持原始实现的完整功能和不可变性,并由部署者完全拥有。

通过工厂合约克隆预构建合约降低部署成本

通过工厂合约克隆预构建合约降低部署成本

然而,这种部署合约的方式需要在任何支持的链上预先存在部署的基础设施。我们需要在每条链上预先部署的克隆工厂和实现合约,并保持最新状态。这意味着需要复杂的部署脚本,最重要的是需要为每条链提供资金。

我们如何将这个过程扩展到 900 多个 EVM 链?让我们深入探讨。

旧流程 - 我们曾经如何处理部署

下图所示的架构在几个月前仍在使用。它包括:

  • thirdweb 部署者钱包,这是一个 EOA

  • 基础设施合约

  • 预构建实现合约

  • 一种保存链外地址的方法

以前:通过 Thirdweb 部署者 EOA 部署基础设施合约

以前:通过 Thirdweb 部署者 EOA 部署基础设施合约

基础设施以及实现合约都是通过我们控制的专用 EOA 手动部署的。然后,所有实现合约的地址都在一个克隆工厂工厂合约中注册,并与类型和版本相关联。这个过程必须在每个支持的链上重复。

每当用户想要部署合约时,克隆工厂将获取该链目标实现合约的保存地址并指向它部署一个代理。

这种方法在许多方面限制了我们,如下所述。

旧流程的缺点

  1. 不可扩展

    我们在 thirdweb 的重点是支持任何 EVM 链。然而,每次有新的合约,或者合约需要更新/修复 bug 时,手动将合约部署到数百条链上是不切实际的。

  2. 难以维护,容易出错

    所有已部署的地址必须保存以供后续使用。此方法容易出错,因为很容易将地址映射到错误的网络。此外,跟踪旧地址也是一项繁重的工作。

  3. 局限于单一钱包

    所有部署都依赖于 thirdweb 团队控制的单个钱包。这一专用部署者钱包在合约能以多快/多可靠地在所有链上推出方面造成了瓶颈,而不是让任何钱包都能在链上引导 thirdweb 堆栈。

为了克服这些限制,并真正支持任何 EVM 网络,我们设计了一种新方法,在接下来的部分中将讨论。

在新方法中,我们仍然有与先前方法相似的基础设施和预构建合约。然而,有两个关键的新增内容:

  • 无密钥签名者(一个可重复使用的 EOA,其私钥无人知晓)

  • 一个 Create2 工厂

  • 一个 发布的合约 的集合(基础设施与实现)

后:使用无密钥签名者进行无权限部署

后:使用无密钥签名者进行无权限部署

无密钥部署方法被称为 Nick 的方法。这个想法源于你不需要知道私钥即可创建签名交易。可以使用产生可预测的公签名者地址的任意签名来制作交易。

我们使用这种方法来 在每条链上的确定性地址处部署 stateless Create2 factory。无密钥交易是预先构造的,包括任意签名值。由于签名中只能恢复出一个签名者,因此这笔预先构造的交易将始终具有相同的签名者地址,称为无密钥签名者。

这是如何构造部署交易的:

// 要发送的交易
transaction = {
  gasPrice: 100 * 10 ** 9,
  gasLimit: 100000,
  nonce: 0,
  data: CREATE2_FACTORY_BYTECODE,
  ...
}

// 任意签名,例如
signature = {
  v: 27,
  r: "0x2222222222222222222222222222222222222222222222222222222222222222",
  s: "0x2222222222222222222222222222222222222222222222222222222222222222",
};

// 序列化要发送的交易
serialized = serializeTransaction(transaction, signature)

// 从序列化交易中恢复无密钥签名者(EC 恢复)
signer = recoverAddress(transactionDigest, signature)

这笔交易可以直接发送到网络,网络将它视为由无密钥签名者 EOA 发送。我们只需要根据信中选择的 gas 值来为无密钥签名者提供资金,并广播交易以执行它。

这样,我们可以在每条链上确定性地部署 Create2 工厂,只需在进入新链时部署一次即可。一旦工厂被部署,我们就可以通过这个工厂部署所有其他合约,并获得每个合约的确定性地址。这包括基础设施和实现合约(如前几节中所述)。

 Create2 工厂进而为所有基础设施合约提供可预测的地址

Create2 工厂进而为所有基础设施合约提供可预测的地址

当终端用户部署合约时,SDK 上的部署逻辑(代码示例如下)将根据目标实现的字节码计算其地址,而不是从硬编码地址集中读取。

新流程的优势

这种方法克服了上述旧方法的限制。

  1. 可预测性

    我们为每个合约获取确定性的地址。这意味着所有在任意链上的 thirdweb 合约都将具有可以计算的可预测地址。不再需要在任何地方硬编码地址。

  2. 无许可性

    兼容 EVM 的新链现在可以引导所有 thirdweb 合约,而不需要 thirdweb 团队进行部署。该流程是无状态的,可以由任何钱包发起。最终地址仅依赖于无钥签名者Create2 工厂地址,而不是用户的钱包。这意味着我们不需要为任何链持有资金,任何用户都可以无许可地引导一个链。

  3. 可扩展性

    首次(通常是链所有者)在某条链上部署合约的用户,承担了为该链所有其他用户部署基础设施合约的费用。第二个部署到同一链的用户只需部署一个非常便宜的最小克隆代理。

合约版本控制和管理的扩展

通过这种新流程,只需在我们的仪表板上点击一个按钮,即可将我们的所有基础设施引导到一条新链上。但更新呢?我们的合约发布工具是该系统的关键。

每次我们创建或更新一个预构建的合约时,我们使用我们的 publish CLI 发布一个新版本,并带有版本跟踪和更改日志。发布并不意味着部署,而是意味着通过 thirdweb 仪表板提供合约供部署合约元数据在链上发布在 Polygon 合约上,任何人都可以阅读。

这就是我们如何动态获取任何给定合约的最新字节码的方法,然后用来计算应该在任意链上部署的预测地址。

推送合约更新只需发布一个新版本,这将触发首次有人尝试部署时的新实现部署,所有后续部署将只需要一个便宜的代理部署。

用于确定性部署的简单 API

一旦合约发布,就可以使用易用的 API 在任意链上预测其地址并确定性地进行部署。

// 确定性地部署已发布的合约
// 还可以提供可选输入,例如 salt、发布者地址
const deployedContractAddress =
  await sdk.deployer.deployPublishedContractDeterministic(
    contractName,
    constructorArgs
  );

// 预测已发布合约的地址
const predictedAddress =
  await sdk.deployer.predictAddressDeterministic(
    contractName,
    constructorArgs
  );

任何人都可以用于自己的已发布合约。对于未发布的合约,你可以直接传递字节码和 ABI。参见完整文档

我们还在仪表板上直接集成了此功能,以便在任何链上为任何已发布合约获取可预测地址。

新流程的挑战

在构建此系统的过程中,我们学到了很多,并且必须克服一些有趣的挑战。

当在任何网络上发送相同的无钥交易时,这是最大的挑战。 重放保护(EIP-155) 指南由 EVM 链实施,现在需要在交易中包含链 ID。虽然一些链仍允许 pre-EIP155 交易,但大多数链现在已经转向执行重放保护。

这意味着我们不能再将 Create2 部署到相同的地址。此工厂的 pre-EIP155 部署可以在不同链上的此地址找到——0x4e59b44847b379578588920cA78FbF26c0B4956C示例 )。然而,对于大多数链来说,EIP-155 执行会导致该工厂在每条链上的地址不同,由链 ID 属性决定。

另一个相关问题是,没有标准化的方法来知道哪个链实施了 EIP-155,哪个没有。不同 EVM 实现中的错误信息也不一致。

作为一种解决方法,我们维护了所有(大多数)与 EIP-155 相关的错误的列表。要确定某条链是否实施 EIP-155,会向链发送一个虚拟交易,如果实施了 EIP-155,则会抛出错误。然后我们在列表中找到完全匹配或部分匹配的错误,并根据 EIP-155 或 pre-EIP155 构建该交易。

另一种方法可能是始终发送 EIP-155 交易。然而,我们希望尽可能在相同地址部署 Create2 工厂。

还值得注意的是,大多数流行的链选择在创世块的 pre-EIP155 地址部署 Create2 工厂( 示例 ),完全避免了该问题。

链特定问题

每个 EVM 实现可能在基本费用、错误代码、有权限的部署等属性方面存在差异。虽然并不是所有问题都能处理,但我们确实处理了基本费用差异。

每当链出现非标准或极高/低的基本费用值,与上述无钥交易使用的标准基本费用(100 gwei)不同时,我们需要为该链添加一个例外,并保存该链的自定义基本费用。这不仅对于成功部署 Create2 工厂很重要,而且每次在该链上部署合约时还要计算相同的地址。

对于有权限的部署链,我们正在探索允许自定义 create2 工厂的方法,以检查发送部署交易的用户是否被授权。

总结

总之,我们通过依赖 Create2 和无钥签名方法构建了一种无许可、可扩展的方式来在任何 EVM 链上部署 thirdweb 合约。这使我们能够简化部署,为我们的所有基础设施获取确定性地址,并使流程在任何 EVM 上工作,而无需我们的团队负责部署。

如上所述,我们找到了解决各种问题的方法,并不断更新/优化该方法以应对链上的其他独特挑战。

要体验部署方法,可以查看 thirdweb 仪表板上的预构建合约产品,部署到你选择的任何链(包括本地节点!)。或者,你可以尝试使用 thirdweb CLI 发布自己的合约。

共同撰写者 @joenrv@yash09061

我是 AI 翻译官,为大家转译优秀英文文章,如有翻译不通的地方,在这里修改,还请包涵~

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
thirdweb
thirdweb
The full-stack web3 development platform