可升级合约中可以使用 immutable 变量么

  • Ashton
  • 更新于 2024-09-11 07:03
  • 阅读 321

可升级合约中可以使用 immutable 变量么? 让我们深入探索一番

前段时间 review 团队小伙伴合约代码的时候,提出有些变量是可以使用 immutable 来修饰的。但得到一个答复:我们这是可升级合约,不能用 immutable,真的是这样么?

0x01 OpenZeppelin 的警告

因为现在的可升级合约基本上都是使用的 OpenZeppelin 的合约模版,估计可升级合约不能用 immutable 变量的说法也是来源于 OpenZeppelin。 在 OpenZeppelin 的 Why can’t I use immutable variables? 这个文档里,确实解释了"为什么不能用 immutable 变量",主要有下面两个原因:

  1. 可升级合约没有构造函数,只有初始化函数,因此它们无法处理 immutable 变量。
  2. 由于不可变变量的值存储在字节码中,其值将在给定合约的所有代理之间共享。

只是这两个原因不能用 immutable 变量,我感觉是比较牵强的。

0x02 什么时候我们需要用 immutable 变量

immutable 是对变量的一种硬性约束,一旦初始化就不再改变。其中一个常见的场景是对固定合约地址的引用,比如对 USDT 合约地址的引用,我们明确知道这个合约地址在固定链上是不会发生改变的,但是因为我们有可能在不同链上部署我们的合约,起码要在一个网络的测试网和主网上部署我们的合约,直接用常量就很不方便,这个时候使用 immutable 变量,通过构造函数初始化后就不再改变,是最符合预期的。

当然,这种情况下我们也可以使用正常的变量,但这会引入额外两个问题:

  1. Gas 消耗更高
  2. 存在未来被恶意改变的风险 所以对一个变量来说,能使用 immutable 约束的时候我们还是希望能够尽量使用 immutable 约束。

0x03 可升级合约就一定不能使用构造函数么

非也。 这其实只是 OpenZeppelin 为了方便构造函数误用而额外加的规范性限制,在可升级合约的实现合约中,使用构造函数初始化正常变量可能会得到与预期不一致的结果,但初始化 immutable 变量得到的结果应该是完全符合预期的。在实现合约构造函数中初始化的 immutable 变量在可代理合约中都可以正常读取。

0x04 结论

immutable 变量在可升级合约中使用没任何问题:

  1. 尽管通常情况下,可升级合约的实现合约中我们遵循 OpenZeppelin 规范,使用初始化函数 initialize 去初始化常规变量。但在需要的时候我们仍然可以使用构造函数去初始化 immutable 变量。
  2. 我们使用 immutable 变量的本意就是其初始化后就不再改变,所以同一个实现合约的不同代理看到同样的值并没啥问题。如果需要不同的值,实例化不同的实现合约就好了。
  3. 在使用升级插件的时候还是要注意把 unsafeAllow: constructor 和 unsafeAllow:state-variable-immutable 的开关打开,否则估计会报错。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Ashton
Ashton
0x53b3...c54F
专注于 EVM 和比特币生态的区块链开发者