在我们进行合约开发时有一个痛点是,升级部署到链上后不能再更改,但如果了解Solidity比较深的小伙伴就知道,Solidity有个delegate方法,可以实现通过代理合约调用逻辑合约,我们的数据存储在代理合约中,执行的逻辑在逻辑合约中,我们想要升级合约时只需要部署新的逻辑合约即可。
一、简单可升级合约
// 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;
}
}
// 旧逻辑合约
contract Logic1 {
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器: 0xc2985578
function foo() public{
words = "old";
}
}
// 新逻辑合约
contract Logic2 {
// 状态变量和proxy合约一致,防止插槽冲突
address public implementation;
address public admin;
string public words; // 字符串,可以通过逻辑合约的函数改变
// 改变proxy中状态变量,选择器:0xc2985578
function foo() public{
words = "new";
}
}
代码中包含了3
个合约:
SimpleUpgrade
: 代理合约Logic1
: 旧逻辑合约Logic2
: 新逻辑合约
1. 代理合约SimpleUpgrade
代理合约包含3
个变量:
implementation
: 逻辑合约地址admin
: 合约管理员地址words
: 字符串,通过调用逻辑合约函数来改变
也包含了3
个函数:
- 构造函数: 初始化
admin
和implementation
地址 fallback
函数: 委托函数,会将函数调用委托给逻辑合约执行,需要通过函数选择器calldata
来调用upgrade
函数: 升级函数,只能由admin
调用,改变逻辑合约地址
2. 旧逻辑合约
旧逻辑合约中变量和代理合约保持一致(防止函数执行时插槽错误),通过代理合约调用时改变的状态变量是代理合约中的,有一个函数foo
,将代理合约中的words
值改为old
。
3. 新逻辑合约
和旧逻辑合约逻辑一直,foo
将代理合约中的words
改为new
。
4. 部署测试
到此,我们就完成了可