47. 可升级合约
WTF Solidity极简入门: 47. 可升级合约
我最近在重新学 Solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新 1-3 讲。
所有代码和教程开源在 github: github.com/AmazingAng/WTF-Solidity
这一讲,我们将介绍可升级合约(Upgradeable Contract)。教学用的合约由OpenZeppelin
中的合约简化而来,可能有安全问题,不要用于生产环境。
可升级合约
如果你理解了代理合约,就很容易理解可升级合约。它就是一个可以更改逻辑合约的代理合约。
简单实现
下面我们实现一个简单的可升级合约,它包含3
个合约:代理合约,旧的逻辑合约,和新的逻辑合约。
代理合约
这个代理合约比第46讲中的简单。我们没有在它的fallback()
函数中使用内联汇编
,而仅仅用了implementation.delegatecall(msg.data);
。因此,回调函数没有返回值,但足够教学使用了。
它包含3
个变量:
implementation
:逻辑合约地址。admin
:admin地址。words
:字符串,可以通过逻辑合约的函数改变。
它包含3
个函数:
- 构造函数:初始化admin和逻辑合约地址。
fallback()
:回调函数,将调用委托给逻辑合约。upgrade()
:升级函数,改变逻辑合约地址,只能由admin
调用。
1 | // SPDX-License-Identifier: MIT |
旧逻辑合约
这个逻辑合约包含3
个状态变量,与代理合约保持一致,防止插槽冲突。它只有一个函数foo()
,将代理合约中的words
的值改为"old"
。
1 | // 逻辑合约1 |
新逻辑合约
这个逻辑合约包含3
个状态变量,与代理合约保持一致,防止插槽冲突。它只有一个函数foo()
,将代理合约中的words
的值改为"new"
。
1 | // 逻辑合约2 |
Remix
实现
-
部署新旧逻辑合约
Logic1
和Logic2
。
-
部署可升级合约
SimpleUpgrade
,将implementation
地址指向把旧逻辑合约。
-
利用选择器
0xc2985578
,在代理合约中调用旧逻辑合约Logic1
的foo()
函数,将words
的值改为"old"
。
-
调用
upgrade()
,将implementation
地址指向新逻辑合约Logic2
。
-
利用选择器
0xc2985578
,在代理合约中调用新逻辑合约Logic2
的foo()
函数,将words
的值改为"new"
。
总结
这一讲,我们介绍了一个简单的可升级合约。它是一个可以改变逻辑合约的代理合约,给不可更改的智能合约增加了升级功能。但是,这个合约有选择器冲突
的问题,存在安全隐患。之后我们会介绍解决这一隐患的可升级合约标准:透明代理和UUPS
。