智能合约 -透明可升级合约[ hardhat、openzeppelin 、ethers ]的演示 demo

文章目录

  • 前言
    • [智能合约 -透明可升级合约[ hardhat、openzeppelin 、ethers ]的演示 demo](#智能合约 -透明可升级合约[ hardhat、openzeppelin 、ethers ]的演示 demo)
      • [1. 测试内容](#1. 测试内容)
      • [2. 合约源码](#2. 合约源码)
      • [3. 脚本部署测试](#3. 脚本部署测试)
        • [3.1. 执行代理合约[逻辑合约BoxV1]的部署脚本](#3.1. 执行代理合约[逻辑合约BoxV1]的部署脚本)
        • [3.2. 执行代理合约[逻辑合约BoxV2]的升级脚本:](#3.2. 执行代理合约[逻辑合约BoxV2]的升级脚本:)

前言

如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。

而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!!


智能合约 -透明可升级合约[ hardhat、openzeppelin 、ethers ]的演示 demo

版本:

solidity ^0.8.28、hardhat 2.28.4、openzeppelin 5.6.1 ethers 6.16.0

1. 测试内容

测试内容: BoxV2合约升级内容为覆盖BoxV1合约的call()函数。

注意的是升级实现合约时,不需要重新部署代理合约,代理合约地址始终不变,仅需将代理指向新的实现合约地址即可。

所以针对 TPUProxy 这类可升级代理,必须写两个核心脚本:初次部署脚本(代理 + 实现合约 V1 的绑定初始化)和升级脚本(部署实现合约 V2 + 让代理指向新实现)。

2. 合约源码

下面是两个逻辑合约和代理合约的源码:

BoxV1 合约:

bash 复制代码
// 声明代码遵循 MIT 开源许可证
// SPDX-License-Identifier: MIT


pragma solidity ^0.8.28;
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
contract BoxV1 is Initializable{

    uint public x;

    // initialize 替代constructor
    function initialize(uint _val) external initializer{
        x = _val;
    }

    function call() external {
        x=x+1;
    }
    // 逻辑合约的初始化数据:供代理合约使用
    function showInvoke() external pure returns (bytes memory){
     
        return abi.encodeWithSelector(this.initialize.selector, 1);
    }
}

BoxV2 合约:

bash 复制代码
// 声明代码遵循 MIT 开源许可证
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

pragma solidity ^0.8.28;

contract BoxV2 is Initializable{

    uint public x;
   
    // initialize 替代constructor
    function initialize(uint _val) external initializer{
        x = _val;
    }

    function call() external {
        x=x+2;
    }

    // 逻辑合约的初始化数据:供代理合约使用
    function showInvoke() external pure returns (bytes memory){
     
        return abi.encodeWithSelector(this.initialize.selector, 1);
    }
}

TPUProxy 自定义透明可升级代理合约:

bash 复制代码
// 声明代码遵循 MIT 开源许可证,SPDX 标准许可证标识
// SPDX-License-Identifier: MIT
// 透明可升级代理合约,继承 OpenZeppelin 官方透明代理基础合约
pragma solidity ^0.8.28; // 限定 Solidity 编译器版本为 0.8.28 及兼容版本
// 引入 OpenZeppelin 官方透明可升级代理核心合约,提供代理底层逻辑
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";


// 自定义透明可升级代理合约,继承 TransparentUpgradeableProxy 实现功能扩展
contract TPUProxy is TransparentUpgradeableProxy {

    /**
     * @dev 构造函数,继承并调用父类 TransparentUpgradeableProxy 构造函数
     * @param _logic 实现合约地址(代理指向的业务逻辑合约)
     * @param initialOwner 代理合约初始管理员地址-区块链账户地址(拥有代理升级、管理权限)
     * @param _data 部署时传递给实现合约的初始化数据(通常是初始化函数的编码数据)
     */
    constructor(address _logic, address initialOwner, bytes memory _data) payable TransparentUpgradeableProxy(_logic, initialOwner, _data) {
        // 无自定义逻辑,仅透传参数调用父类构造函数完成代理初始化
    }

    /**
     * @dev 外部只读方法,查询代理合约的管理员地址
     * @return address 代理管理员地址(拥有 proxyAdmin 权限的账户)
     * 封装父类内部的 _proxyAdmin() 方法,对外提供查询接口
     */
    function proxyAdmin() external view returns(address) {
        return _proxyAdmin();
    }


    /**
     * @dev 外部只读方法,查询代理当前指向的实现合约地址
     * @return address 业务逻辑实现合约的当前地址
     * 封装父类内部的 _implementation() 方法,对外提供查询接口
     */
    function getImplementation() external view returns(address) {
        return _implementation();
    }
 
    receive() external payable {
        revert("TPUProxy: do not send ETH directly"); // 回滚交易并抛出明确错误信息
    }
}

3. 脚本部署测试

3.1. 执行代理合约[逻辑合约BoxV1]的部署脚本

源码:

bash 复制代码
// Hardhat 和 ethers.js 的「桥梁」,负责注册插件,让两者能协同工作;
import hre from "hardhat";
import "@nomicfoundation/hardhat-ethers";
import "@openzeppelin/hardhat-upgrades";

const CONTRACT_NAME = "BoxV1";

// 部署合约
async function contract(){
  console.log(`开始部署 ${CONTRACT_NAME} 代理合约...`);
  // 获取合约工厂
  const factory = await hre.ethers.getContractFactory("BoxV1");
  const proxy = await hre.upgrades.deployProxy(factory,[1],{
     initializer: "initialize"
  });

  // 获取代理合约地址方式
  const contractAddress = await proxy.getAddress();
  return { contractInstance: proxy, contractAddress }; // 同时返回实例和地址,方便后续调用方法

}


async function deploy() {

  const { contractInstance, contractAddress }  = await contract();
  console.log(`${CONTRACT_NAME} 部署完成!`);
  console.log(`代理合约地址: ${contractAddress}`);
  console.log(`验证: 调用代理合约-${CONTRACT_NAME} 的 call 方法`);
  try {
    await contractInstance.call();
    console.log(`验证: x:`, await contractInstance.x());
   
  } catch (error) {
    console.error(`代理合约-${CONTRACT_NAME}的 方法调用失败:`, error);
    process.exit(1);
  }
}

// 启动 本地网络  npx hardhat node
deploy()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error("部署失败:", error);
    process.exit(1);
  });

执行脚本。

npx hardhat run scripts/upgradable/deploy-BoxV1Proxy.ts --network localhost

0x0165878A594ca255338adfa4d48449f69242Eb8F 是合约代理地址,记下来,这个会在升级脚本的时候会用到。

下面是call合约的方法:

bash 复制代码
function call() external {
    x=x+1;
}

初始化的x为1,调用call函数后,加1,最后得出x为2,正确。

3.2. 执行代理合约[逻辑合约BoxV2]的升级脚本:

源码:

bash 复制代码
// Hardhat 和 ethers.js 的「桥梁」,负责注册插件,让两者能协同工作;
import hre from "hardhat";
import "@nomicfoundation/hardhat-ethers";
import "@openzeppelin/hardhat-upgrades";




const CONTRACT_NAME = "BoxV2";
const PROXY_ADDRESS = "0x0165878A594ca255338adfa4d48449f69242Eb8F";
// 部署合约
async function contract(){
  console.log(`开始升级 ${CONTRACT_NAME} 代理合约...`);
  // 获取合约工厂
  const factory = await hre.ethers.getContractFactory("BoxV2");
  const proxy = await hre.upgrades.upgradeProxy(PROXY_ADDRESS,factory);

  // 获取代理合约地址方式
  const contractAddress = await proxy.getAddress();
  return { contractInstance: proxy, contractAddress }; // 同时返回实例和地址,方便后续调用方法




}


async function deploy() {

  const { contractInstance, contractAddress }  = await contract();
  console.log(`${CONTRACT_NAME} 部署完成!`);
  console.log(`代理合约地址: ${contractAddress}`);
  console.log(`验证: 调用代理合约-${CONTRACT_NAME} 的 call 方法`);
  try {
    await contractInstance.call();
    console.log(`验证: x:`, await contractInstance.x());
   
  } catch (error) {
    console.error(`代理合约-${CONTRACT_NAME}的 方法调用失败:`, error);
    process.exit(1);
  }
}

// 启动 本地网络  npx hardhat node
deploy()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error("部署失败:", error);
    process.exit(1);
  });

执行脚本:

npx hardhat run scripts/upgradable/upgrade-TPUProxy.ts --network localhost

因为原来的x=2,看一下BoxV2合约的call方法:

bash 复制代码
    function call() external {
        x=x+2;
    }

调用call()函数后,加2,最后得出x=4,验证通过。

相关推荐
TechubNews10 小时前
從25Q4及全年財報數字看燦谷(Cango Inc)戰略轉向AI
网络·人工智能·web3·区块链
木西1 天前
Energy-Fi:基于 DePIN 的能源资产化协议设计与实现
web3·智能合约·solidity
lsrsyx1 天前
iCoin:构建更高效、安全的数字资产交易新体验
安全·区块链
暴躁小师兄数据学院1 天前
【WEB3.0零基础转行笔记】Go编程篇-第11讲:Gin框架
笔记·golang·web3·区块链·智能合约
庭前云落1 天前
Compound 6| Compound V2 和 V3 的对比讲解
区块链
ws2019071 天前
华南产业集群赋能:2026广州汽车电子展,技术迭代与全球机遇共振?
大数据·科技·汽车·区块链
lsrsyx1 天前
突围 2026:SunX 合约交易新选择
区块链
ETFOption1 天前
50ETF期权分仓技术讲解(三):分仓系统技术实现与平台选择指南
人工智能·区块链
木西2 天前
深度解析 AgentFi:基于 ERC-6551 与 AI 驱动的 DeFi 进化论
web3·智能合约·solidity