这篇文章介绍了一种名为 fallback-extension 的模式,该模式可以解决 Solidity 智能合约 24kb 大小限制的问题。通过将某些函数放置在扩展合约中,并利用回调函数将调用转发至扩展合约,开发者可以增添更多的功能。此外,文中还讨论了存储布局一致性、函数选择器碰撞的风险及其对应的气体成本等重要注意事项。
回退扩展模式是一种绕过 24kb 智能合约大小限制的简单方法。
假设我们在主合约中有函数 foo()
和 bar()
,并且希望添加 baz()
,但由于空间不足无法添加。
我们在主智能合约中添加一个回退函数,将未知函数调用委托给一个扩展合约,类似于代理的工作方式。
我们将 baz()
放在扩展合约中。当我们在主合约上调用 baz()
时,它将无法匹配主合约中的任何函数选择器,从而触发回退函数。然后,baz()
将在扩展合约中被委托调用。
为了使此模式正常工作,主合约和扩展合约需要具有相同的存储布局。实现这一点的简单方法是将所有存储变量(无例外!)放入单个合约中。然后,主合约和扩展合约都继承自该合约。以下是一个示例:
在上面的示例中,扩展合约的地址存储在一个不可变变量中。我们可以向存储合约添加一个额外的存储变量,该变量保存扩展的地址,然后在想要更改合约的某些功能时更新扩展地址。
不建议采用这种方法——如果需要可升级性,那么最好使用成熟的代理模式。此外,从存储中读取扩展地址变量将额外花费 2,100 gas。
两个随机函数具有相同选择器的概率约为 1/400 万。然而,由于生日悖论,当我们有 n 个函数选择器且仅需要一个冲突就会导致意外行为时,这种概率会迅速增加。没有工具可以解决这个问题,开发人员必须手动检查函数选择器。
扩展本身可以遵循此模式并将其发送给另一个代理。事实上,我们这样做的次数没有理论上的限制。
然而,每次“跳转”都会额外增加 2,600 gas(向新地址发出 CALL
或 DELEGATECALL
所需的最低 gas),因此如果链很长,成本可能会很高。
由于扩展中的函数需要额外的 2,600 gas,我们希望将很少使用的函数或主要从链下调用的函数放在扩展中,因为在链下 gas 不重要。
与此模式结合使用访问列表交易在调用扩展中的函数时将节省 100 gas。一般来说,如果以太坊交易包含跨合约调用或委托调用,则应使用访问列表交易。
此模式可与常规代理一起使用。也就是说,代理合约可以委托给实现合约,然后实现合约再委托给扩展。请记住,用于升级的工具(如 OpenZeppelin 升级工具)并非设计用于与扩展回退模式配合使用,可能无法捕获与升级相关的问题。
请参阅我们的 Solidity 训练营 以了解更多信息。
最初发布于 2023 年 12 月 28 日
- 原文链接: rareskills.io/post/fallb...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!