Fireblocks gasless 合约审计

这篇文章对Fireblocks的Gasless合同进行了全面的审计分析,详细介绍了系统架构、功能、存在的低级别问题以及解决方案。文章强调了GaslessFactory合约的功能和限制,并提出了多项改进建议,以提升其可用性和效率。此外,审计发现了多个代码问题,并已通过合并请求予以解决。

目录

摘要

类型:DeFi
时间范围:从2024-12-02到2024-12-10
语言:Solidity
总问题数:10 (已解决8个)
严重性问题:

  • 重大严重性问题:0 (已解决0个)
  • 高严重性问题:0 (已解决0个)
  • 中等严重性问题:0 (已解决0个)
  • 低严重性问题:2 (已解决1个)
    备注与附加信息:8 (已解决7个)

范围

我们审核了fireblocks/fireblocks-smart-contracts仓库,最近提交为cf1bb85

审核范围包括以下文件:

 contracts
├── gasless-contracts
│   ├── AccessRegistry
│   │   ├── AccessListUpgradeableGasless.sol
│   │   ├── AllowListGasless.sol
│   │   └── DenyListGasless.sol
│   ├── ERC1155FGasless.sol
│   ├── ERC20FGasless.sol
│   ├── ERC721FGasless.sol
│   ├── GaslessFactory.sol
│   └── TrustedForwarder.sol
├── gasless-upgrades
│   ├── AccessRegistry
│   │   ├── AllowListV2.sol
│   │   └── DenyListV2.sol
│   ├── ERC1155FV2.sol
│   ├── ERC20FV2.sol
│   └── ERC721FV2.sol
└── library
    ├── MetaTx
    │   └── ERC2771ContextInitializableUpgradeable.sol
    └── Proxy
        └── Proxy.sol

系统概述

Fireblocks的Gasless合约是Fireblocks Upgradeable Tokens的一个扩展,允许通过利用ERC-2771标准来支持元交易。这些合约继承自ERC2771ContextInitializableUpgradeable合约,允许用户选择性地设置受信任的转发者,从而为用户提供选择gasless或非gasless版本的灵活性。

除了gasless版本的实用代币、访问控制和访问列表外,还引入了一个GaslessFactory合约。该合约是一个单例合约,允许其用户通过元交易部署确定性和不确定性合约。此外,还增加了一个TrustedForwarder合约,直接继承了OpenZeppelin合约库的ERC2771Forwarder合约。GaslessFactoryTrustedForwarder合约都是不可升级的。

该仓库还引入了另一组合约,称为Gasless upgrades,允许将合约的初始版本升级到gasless版本,从而引入对元交易的支持。这些合约的执行确保仅版本1的合约才能升级到版本2,确保这两个版本之间的兼容性,并避免存储碰撞。同时引入的还有一个Proxy合约,继承自OpenZeppelin合约库的ERC1967Proxy合约,确保Fireblocks库的用户拥有一个标准化的代理合约版本。

安全模型和信任假设

除了通过继承链继承的角色外,在以下合约中定义了CONTRACT_ADMIN_ROLE角色。该角色可以更新各自合约的受信任转发者。

  • AccessListUpgradeableGasless合约
  • AllowListV2合约
  • DenyListV2合约
  • ERC1155FGasless合约
  • ERC1155FV2合约
  • ERC20FGasless合约
  • ERC20FV2合约
  • ERC721FGasless合约
  • ERC721FV2合约

假定负责上述角色和行为的帐户始终按预期方式执行。

低严重性

GaslessFactory合约在部署期间资产转移能力的限制

GaslessFactory合约通过两个主要功能来促进新合约的部署:deploydeployDeterministicdeploy函数实现标准合约创建,而deployDeterministic则允许使用CREATE2操作码进行确定性合约部署。尽管这两个函数在合约部署中很有用,但它们都没有标记为payable,这限制了用户在合约部署交易中发送原生资产的能力。因此,任何需要新部署合约在创建时持有原生资产余额的场景都无法实现。

此外,这种限制还扩展到postConfig函数,该函数旨在作为新部署合约的初始配置的一部分调用函数。由于postConfig也不支持在函数调用中发送原生资产,限制了初始化能力,特别是对那些其设置函数需要进行原生资产转移的合约。

这一约束不仅限制了GaslessFactory合约在部署和配置广泛合约的多样性,还使得那些设计用于立即持有或管理原生资产的合约在部署过程中变得更加复杂。为了解决这一限制并提高GaslessFactory合约的功能,考虑将deploydeployDeterministic函数改为payable,并将金额传递给createcreate2函数。这将使两个函数能够在合约部署过程中接受原生资产转移,从而允许用户部署需要初始原生资产余额的合约。

通过实施上述建议,GaslessFactory合约将提高其实用性和灵活性,以适应更广泛的部署和配置场景,包括那些需要立即资产管理能力的场景。

更新:已确认,未解决。Fireblocks团队表示:

该设计是故意的,因为它保持了GaslessFactory代码的简洁和简单。我们没有payable函数的用例,并希望避免拥有额外的代码来处理像潜在资金丢失或恢复这样的边缘情况。

GaslessFactory的_execute函数中的提前事件发射

GaslessFactory合约的_execute函数旨在通过将函数调用委派给其他合约来促进无Gas交易。然而,这个函数中的操作顺序存在问题,特别是与FunctionExecuted事件的发射顺序有关。该事件在实际执行委派函数调用之前就被发射。这种提前的事件发射导致在事件日志中记录的结果为零,无论后续的函数调用的实际结果如何。

这种行为可能会误导监视这些事件的链下服务或用户,因为FunctionExecuted事件暗示了函数调用的完成,但并不能准确反映其结果。在区块链环境中,透明度和操作准确性至关重要,此类差异可能会破坏对系统可靠性的信任。

考虑调整_execute函数中的操作顺序。具体而言,FunctionExecuted事件应仅在委派的函数调用成功执行后发射。这确保事件准确反映函数调用的结果,与其发射时所设定的预期一致。通过实施此更改,GaslessFactory合约将提高其操作透明度和可靠性,通过事件发射为系统参与者提供准确的反馈。

更新:已在拉取请求#1中的提交43a713b得到解决。

备注与附加信息

对父合约的模糊调用

在代码库中,发现了多个对父合约模糊调用的实例:

为了避免对父合约的模糊调用,建议明确指明所调用的父合约函数。

更新:已在拉取请求#2中的提交2ef4c95得到解决。

GaslessFactory合约与ZkSync时代部署机制的兼容性问题

GaslessFactory合约旨在促进没有Gas费用的合约部署和操作,但在ZkSync时代平台上部署时遇到了显著的功能差异。这些差异源于两个核心问题:地址推导的方法和合约部署所需的字节码知识的要求。

  1. 在以太坊上,新合约的地址通过一种确定性过程得出,涉及部署者的地址和nonce(以部署者地址发送的交易数量)。然而,ZkSync时代采用一种不同的地址推导机制,并不符合以太坊的方法。这一差异意味着GaslessFactory合约对于地址推导的预期,因此与ZkSync时代的操作现实不符,这使得预测或与新部署合约地址的交互变得不成比例。

  2. ZkSync时代的合约部署独特地要求合约的字节码的哈希,以及编译器需事先了解此字节码的必要性。这一要求与以太坊的部署机制形成对比,在以太坊中,字节码是在部署时生成的,并且编译器不需要事先了解该字节码。GaslessFactory合约的deploy函数旨在动态部署合约,而无需预先知道编译的字节码,这使其与ZkSync时代的部署要求根本不兼容。这种不兼容性使得deploy函数在ZkSync时代无效,因为它无法满足该平台对于部署之前字节码的知晓。

为了解决这些兼容性问题,并使GaslessFactory合约能够在ZkSync时代正常运作,考虑实施以下修改:

  1. 地址推导适应性:在GaslessFactory合约中实现一种机制,以适应ZkSync时代的地址推导方法。这可能涉及将ZkSync时代的地址推导逻辑集成到合约中,或者通过合约调用能够在ZkSync时代环境中正确预测或确定地址。

  2. 预编译字节码管理:修订合约部署策略,以符合ZkSync时代对预编译字节码知识的要求。这可能涉及以下策略:

    • GaslessFactory合约中维护一份旨在部署的合约的预编译字节码注册表。
    • 开发一个工具或流程,以预编译合约字节码并在部署尝试之前将其注册到GaslessFactory合约或其他链上注册表中。
  3. 文档和开发者指导:更新GaslessFactory合约的文档,以明确描述其与ZkSync时代的兼容性,包括任何限制、部署的前提和地址推导及预编译字节码管理的指导。

通过实施上述建议,GaslessFactory合约可以适应ZkSync时代生态系统的有效操作,从而扩大其在这一新兴平台上的实用性,并确保其功能与ZkSync时代的合约部署和地址管理机制的独特要求保持一致。

更新:已确认,未解决。Fireblocks团队表示:

我们不打算为特定区块链定制调整我们的代码。此外,我们不打算在ZKSync上部署此工厂。值得注意的是,ZKSync上的部署不可能意外发生,因为这需要使用ZKSync编译器进行单独且故意的编译。

前缀递增运算符++i可以在循环中节省Gas

GaslessFactory.sol中,发现多次使用前缀递增运算符(++i)可以节省Gas的机会:

考虑在循环中使用前缀递增运算符(++i),而不是后缀递增运算符(i++),以节省Gas。此优化跳过了在递增之前存储值,因为表达式的返回值被忽略。

更新:已在拉取请求#3中的提交a8942e9中解决。

缺少文档字符串

Proxy.sol中,缺少对Proxy合约本身的文档字符串。

建议全面记录所有合约公共API中所有函数(及其参数)。实现敏感功能的函数,即便不是公共的,也应清晰文档化。在编写文档字符串时,考虑遵循以太坊自然规范格式 (NatSpec)。

更新:已在拉取请求#4中的提交5a1aa15中解决。

缺乏安全联系方式

在智能合约中提供具体的安全联系信息(例如电子邮件或ENS名称),能够显著简化个人识别代码中的漏洞时的沟通过程。这种做法非常有益,因为它允许代码所有者定义漏洞披露的交流渠道,消除了因不知道如何报告而导致的沟通失败或误传的风险。此外,如果合约中包含第三方库,并出现了漏洞,相关维护者也可以更容易地联系到合适的人,以提供缓解方面的指导。

考虑在Fireblocks仓库中添加安全联系信息,Given考虑到这些合约是供其他实体使用的模板。OpenZeppelin合约库中的SECURITY.md 可以作为此处的一个示例。

更新:已在拉取请求#8中的提交d3b7de0中解决。

增量更新未封装在unchecked块中

自Solidity版本0.8.0以来,算术运算包括对溢出和下溢的自动检查,这会增加Gas成本。在运用递增正变量的循环中,使用unchecked块可以优化Gas使用而不妨害安全性。在Solidity版本0.8.22之前,开发者手动实施此优化以绕过自动溢出检查的额外开销。

GaslessFactory.sol中,发现多次通过使用unchecked块来节省Gas的机会:

  • deploy函数中的i++递增操作
  • deployDeterministic函数中的i++递增操作

考虑更新到0.8.22版本的pragma,以利用自动溢出检查的优化,或者将增量更新封装在一个unchecked块中以节省Gas。

更新:已在拉取请求#5中的提交1e0c6f5中解决。

使用不同版本的OpenZeppelin合约库

Proxy合约使用的是OpenZeppelin合约库的版本4.9.3,而GaslessFactoryTrustedForwarder合约使用的是版本5.0.2

为了与其余代码库保持一致,考虑将Proxy合约中的依赖更新为版本5.0.2

更新:已在拉取请求#6中的提交0e3be3a中解决。

部署失败回滚原因未在deployDeterministic中传播

GaslessFactory合约利用deployDeterministic函数来创建新合约,该函数对于无需用户Gas即可部署合约至关重要,从而利用CREATE2操作码进行确定性地址生成。然而,由于使用的是OpenZeppelin合约库的版本5.0.x,导致不存在在合约创建失败时向上传播回滚原因的功能这在此版本中缺失。该功能仅在OpenZeppelin合约库的版本5.1中引入

缺乏回滚原因传播的功能意味着当合约部署失败时,deployDeterministic函数不会向调用者提供失败的具体原因。这种透明度的缺失可能会妨碍调试工作,并隐藏打算部署的合约代码中的潜在漏洞或逻辑错误。这也影响了用户体验,因为开发人员或与GaslessFactory交互的用户无法轻松确定部署失败的原因。

考虑将GaslessFactory所使用的OpenZeppelin合约库升级到版本5.1或更高。这个较新的版本包括增强功能,允许在合约创建失败时将回滚原因传播回调用者。实施此升级会使调试更为顺畅,并改善整体安全性,因为确保了部署失败的原因透明且易于处理。

更新:已在拉取请求#7中的提交ddd1e4a中解决。

结论

Fireblocks无Gas合约展示了元交易能力的良好结构化实现,将ERC-2771标准无缝集成到Fireblocks的ERC-20ERC-721ERC-1155可升级代币中。代码库展示了以全面的方式启用无Gas交易,同时保持用户的兼容性和灵活性,即通过GaslessFactory合约实现确定性和不确定性合约部署的充足设计原则。经审查,代码库规划合理、文档齐全,而Fireblocks团队也及时回答了我们对代码库的所有问题。

  • 原文链接: blog.openzeppelin.com/fi...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。