【从0学习Solidity】47. 可升级合约
- 博主简介:不写代码没饭吃,一名全栈领域的创作者,专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构,分享一些项目实战经验以及前沿技术的见解。
- 关注我们的主页,探索全栈开发,期待与您一起在移动开发的世界中,不断进步和创造!
- 本文收录于 不写代码没饭吃 的学习汇报系列,大家有兴趣的可以看一看。
- 欢迎访问我们的微信公众号:不写代码没饭吃,获取更多精彩内容、实用技巧、行业资讯等。您关注的是我们前进的动力!
这一讲,我们将介绍可升级合约(Upgradeable Contract)。教学用的合约由OpenZeppelin
中的合约简化而来,可能有安全问题,不要用于生产环境。
可升级合约
如果你理解了代理合约,就很容易理解可升级合约。它就是一个可以更改逻辑合约的代理合约。
简单实现
下面我们实现一个简单的可升级合约,它包含3
个合约:代理合约,旧的逻辑合约,和新的逻辑合约。
代理合约
这个代理合约比[第46讲]中的简单。我们没有在它的fallback()
函数中使用内联汇编
,而仅仅用了implementation.delegatecall(msg.data);
。因此,回调函数没有返回值,但足够教学使用了。
它包含3
个变量:
-
implementation
:逻辑合约地址。 -
admin
:admin地址。 -
words
:字符串,可以通过逻辑合约的函数改变。
它包含3
个函数
- 构造函数:初始化admin和逻辑合约地址。
-
fallback()
:回调函数,将调用委托给逻辑合约。 -
upgrade()
:升级函数,改变逻辑合约地址,只能由admin
调用。
// SPDX-License-Identifier: MIT
// wtf.academy
pragma solidity ^0.8.4;
// 简单的可升级合约,管理员可以通过升级函数更改逻辑合约地址,从而改变合约的逻辑。
// 教学演示用,不要用在生产环境
contract SimpleUpgrade {
address public implementation; // 逻辑合约地址
address public admin; // admin地址
string public words; // 字符串,可以通过逻辑合约的函数改变
// 构造函数,初始化admin和逻辑合约地址
constructor(address _implementation){
admin = msg.sender;
implementation = _implementation;
}
// fallback函数,将调用委托给逻辑合约
fallback() external payable {
(bool success, bytes memory data) = implementation.delegatecall(msg.data);
}
// 升级函数,改变逻辑合约地址,只能由admin调用
function upgrade(address newImplementation) external {
require(msg.sender == admin);
implementation = newImplementation;
}
}
旧逻辑合约
这个逻辑合约包含3
个状态变量,与保持代理合约一致,防止插槽冲突。它只有一个函数foo()
,将代理合约中的words
的值改为"old"
。
// 逻辑合约1
contract Logic1 {
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器: 0xc2985578
function foo() public{
words = "old";
}
}
新逻辑合约
这个逻辑合约包含3
个状态变量,与保持代理合约一致,防止插槽冲突。它只有一个函数foo()
,将代理合约中的words
的值改为"new"
。
// 逻辑合约2
contract Logic2 {
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器:0xc2985578
function foo() public{
words = "new";
}
}
Remix
实现
-
部署新旧逻辑合约
Logic1
和Logic2
。 -
部署可升级合约
SimpleUpgrade
,将implementation
地址指向把旧逻辑合约。 -
利用选择器
0xc2985578
,在代理合约中调用旧逻辑合约Logic1
的foo()
函数,将words
的值改为"old"
。 -
调用
upgrade()
,将implementation
地址指向新逻辑合约Logic2
。 -
利用选择器
0xc2985578
,在代理合约中调用新逻辑合约Logic2
的foo()
函数,将words
的值改为"new"
。
总结
这一讲,我们介绍了一个简单的可升级合约。它是一个可以改变逻辑合约的代理合约,给不可更改的智能合约增加了升级功能。但是,这个合约有选择器冲突
的问题,存在安全隐患。之后我们会介绍解决这一隐患的可升级合约标准:透明代理和UUPS
。
文章来源:https://uudwc.com/A/edDBm
如果这份博客对大家有帮助,希望各位给作者一个免费的点赞?作为鼓励,并评论收藏一下⭐,谢谢大家!!!
制作不易,如果大家有什么疑问或给作者的意见,欢迎评论区留言。文章来源地址https://uudwc.com/A/edDBm