在我们进行合约开发时有一个痛点是,升级部署到链上后不能再更改,但如果了解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. 部署测试
到此,我们就完成了

本文介绍了如何通过hardhat和openzeppelin开发和部署可升级的智能合约,包括简单可升级合约、透明代理和通用可升级代理(UUPS)的实现。文章详细讲解了合约的结构、升级过程以及解决选择器冲突的方法,并提供了一个从初始化项目到编写升级脚本的完整流程示例。
最低0.47元/天 解锁文章
910

被折叠的 条评论
为什么被折叠?



