openzeppelin可升级合约详解

安装 @openzeppelin/truffle-upgrades

npm i @openzeppelin/truffle-upgrades

编写合约和脚本

为了更加清晰的认识可升级合约,这里一次部署两个合约

// contracts/Test1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test1 {
    
    
    function get() external pure returns(uint a) {
    
    
       a = 1;
    }
    uint public x;
    function add() external {
    
    
      x++;
    }
}
// contracts/Test2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test1 {
    
    
    function get() external pure returns(uint a) {
    
    
       a = 1;
    }
    uint public x;
    function add() external {
    
    
      x++;
    }
}
const {
    
     deployProxy } = require('@openzeppelin/truffle-upgrades');
const Test1 = artifacts.require("Test1");
const Test2 = artifacts.require("Test2");

module.exports = async function (deployer) {
    
    
  await deployProxy(Test1);
  await deployProxy(Test2);
};

部署、验证合约

truffle migrate --network ropsten
truffle run verify Test1 Test2 --network ropsten

可升级合约解析

简单介绍

通过刚才的部署,可以看到我们部署了5个合约,分别是 :
Test1:我们编写的 Test1 合约
TransparentUpgradeableProxy:Test1 的代理合约
Test2:我们编写的 Test 合约
TransparentUpgradeableProxy:Test2 的代理合约
ProxyAdmin:管理员合约,用于管理所有代理合约,后续 Test1、Test2 合约升级就是改变对应代理合约的指向。
这便可以清晰的认识到,我们编写的每个合约,都会对应一个代理合约,
发布两个合约的作用已经起到,后面讲解便忽略 Test2 合约

ProxyAdmin 合约

三个读函数
getProxyAdmin:获取代理合约管理员地址,输入 Test1 代理合约地址,返回本合约(ProxyAdmin )地址
getProxyImplementation :获取代理合约实现合约地址,输入 Test1 代理合约地址,指向 Test1 合约地址
owner:合约owner,部署合约的地址,也就是项目网络配置中 mnemonic 对应的账户地址

在这里插入图片描述

五个写函数,
1.更换代理合约管理员
2.放弃合约所有权
3.更换合约owner
4.升级合约
5.升级并调用合约

在这里插入图片描述

TransparentUpgradeableProxy:Test1 的代理合约

该合约没有读合约,略过,看写合约,五个函数:
1.管理员:该函数实际上是一个读函数,但函数并没有加 view 关键字,并且加了 ifAdmin 修改器,只有管理员可以访问,一定程度上起到了保护 admin 合约地址的作用
2.变更管理员:需要admin地址访问
3.代理实现:同admin函数
4.升级合约:需要admin地址访问
5.升级并调用合约:需要admin地址访问
综上所述,这五个函数都是为管理员合约提供的专用函数,无法直接访问

在这里插入图片描述
后面的代理函数,则是我们 Test1 合约的函数,先调用写合约add函数,再查询结果
在这里插入图片描述

合约升级

工作原理

熟悉委托调用的朋友都知道,合约A中函数委托调用合约B中函数,只是使用合约B中函数的逻辑,改变的是合约A中的变量,这里代理合约便是委托调用我们编写的逻辑合约,所以合约升级只是更换我们的逻辑合约,原数据还在代理合约中不会丢失。接下来顺便验证这一点

修改合约 和脚本

// contracts/Test1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Test1 {
    
    
    function get() external pure returns(uint a) {
    
    
       a = 10;
    }
    uint public x;
    function add() external {
    
    
      x+= 10;
    }
}

deployProxy部署合约会部署一个代理合约和一个逻辑合约,
升级合约只部署新的逻辑合约并修改指向便可,采用普通的部署方式

const Test1 = artifacts.require("Test1");

module.exports = async function (deployer) {
    
    
  deployer.deploy(Test1);
};

部署验证略过

修改代理实现

调用 ProxyAdmin 合约 upgrade 函数
第一个参数为 TransparentUpgradeableProxy 合约地址
第二个参数为 刚刚部署的 Test1 合约地址
需要连接 ProxyAdmin 合约的owner地址

在这里插入图片描述

测试

查看 TransparentUpgradeableProxy 合约
x 的值还是1 并没有丢失
调用add方法后 再次查看 x 的值变为11,说明我们合约已经升级成功。

在这里插入图片描述

验证

查看新部署的Test1合约,x的值依旧是0,验证了代理合约只是使用逻辑合约的逻辑,改变了代理合约的变量,逻辑合约内变量没有变化
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42704356/article/details/126022955