news 2026/7/6 21:48:04

在Remix和hardhat中集成UUPS升级合约

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在Remix和hardhat中集成UUPS升级合约

目录

1.在Remix中集成UUPS升级合约

2.在Hardhat中集成UUPS升级合约


上一篇讲了集成透明升级合约:

https://blog.csdn.net/fyihdg/article/details/155883276

这一篇讲一下UUPS升级合约

1.在Remix中集成UUPS升级合约

首先准备好代码:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; // 导入 ERC1967Utils import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; // 导入 ERC1967Proxy(用于测试,不引入这个,部署面板上不会显示ERC1967Proxy) import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; contract UUPSV1 is Initializable, UUPSUpgradeable, OwnableUpgradeable { uint public x; constructor(uint _var) { x = _var; } function _authorizeUpgrade(address implementation) internal override {} function initialize(uint _var) external initializer { x = _var; __Ownable_init(msg.sender); } function call() external { x = x + 1; } function showCode() external pure returns (bytes memory) { return abi.encodeWithSelector(this.initialize.selector, 1); } } contract UUPSV2 is Initializable, UUPSUpgradeable, OwnableUpgradeable { uint public x; constructor(uint _var) { x = _var; } function _authorizeUpgrade(address implementation) internal override {} function initialize(uint _var) external initializer { x = _var; __Ownable_init(msg.sender); } function showCode() external pure returns (bytes memory) { return abi.encodeWithSelector(this.initialize.selector, 1); } function call() external { x = x * 2; } }

然后分别部署

UUPSV1和UUPSV1,部署时,都写0.

然后选择ERC1967Proxy

点击“transact",ERC1967Proxy成功

合约地址其实没变,只是名称变了

此时测试,点 ”x:按钮,显示1,然后点”call"按钮,显示“2”

这个时候,我们升级,换成UUPSV2,复制UUPSV2的地址

照着图中操作,点upgradeToAndCall中的,"transact"‘然后再点,"x"还是显示2,状态没丢,然后点一下"call"按钮,再点“x"按钮,发现值变成4,升级成功了

2.在Hardhat中集成UUPS升级合约

把代码分别复制粘贴到vscode中,但是有编译错误

确认你已用 Yarn 正确安装依赖,执行命令(之前都是用yarn命令,所以必须一直保持一致):

yarn add @openzeppelin/contracts yarn add @openzeppelin/contracts-upgradeable

编译

npx hardhat compile # 或 yarn hardhat compile
D:\csdn\Hardhat2.22.17>yarn hardhat compile yarn run v1.22.22 $ D:\csdn\Hardhat2.22.17\node_modules\.bin\hardhat compile [dotenv@17.2.3] injecting env (2) from .env -- tip: 🛠️ run anywhere with `dotenvx run -- yourcommand` Generating typings for: 14 artifacts in dir: typechain-types for target: ethers-v6 Successfully generated 74 typings! Compiled 14 Solidity files successfully (evm target: paris). Done in 3.25s.

这说明你的合约代码已经能成功编译!VS Code 中的红色波浪线是“假报错”,虽然不影响编译,但红色波浪线很烦人。你可以通过以下任一方法修复:在项目根目录(和hardhat.config.js同级)创建一个文件,每行末尾的 / 不能省略!:

@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ @openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/

然后 重启 VS Code,新建UUPSV1.sol.UUPSV2.sol,注意要把构造函数去掉

// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; // 导入 ERC1967Proxy(用于测试,不引入这个,部署面板上不会显示ERC1967Proxy) import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; contract UUPSV1 is Initializable, UUPSUpgradeable, OwnableUpgradeable { uint public x; function _authorizeUpgrade(address implementation) internal override {} function initialize(uint _var) external initializer { x = _var; __Ownable_init(msg.sender); } function call() external { x = x + 1; } function showCode() external pure returns (bytes memory) { return abi.encodeWithSelector(this.initialize.selector, 1); } }

UUPSV2.js

// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; // 导入 ERC1967Proxy(用于测试,不引入这个,部署面板上不会显示ERC1967Proxy) import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; contract UUPSV2 is Initializable, UUPSUpgradeable, OwnableUpgradeable { uint public x; function _authorizeUpgrade(address implementation) internal override {} function initialize(uint _var) external initializer { x = _var; __Ownable_init(msg.sender); } function showCode() external pure returns (bytes memory) { return abi.encodeWithSelector(this.initialize.selector, 1); } function call() external { x = x * 2; } }

在test 目录新建,UUPS.js

const hre = require("hardhat"); async function deploy() { const _UUPSV1 = await hre.ethers.getContractFactory("UUPSV1"); const v1 = await hre.upgrades.deployProxy(_UUPSV1, [1], { initializer: "initialize", kind: "UUPS", }); await v1.waitForDeployment(); console.log(await v1.getAddress()); console.log(await v1.x()); await v1.call(); console.log(await v1.x()); const _UUPSV2 = await hre.ethers.getContractFactory("UUPSV2"); await hre.upgrades.upgradeProxy(await v1.getAddress(), _UUPSV2); console.log(await v1.x()); await v1.call(); console.log(await v1.x()); } deploy();

然后运行命令:

npx hardhat run test/UUPS.js

可能会报以下错误

D:\csdn\Hardhat2.22.17>npx hardhat run test/UUPS.js [dotenv@17.2.3] injecting env (2) from .env -- tip: 👥 sync secrets across teammates & machines: https://dotenvx.com/ops Compiled 2 Solidity files successfully (evm target: paris). [dotenv@17.2.3] injecting env (0) from .env -- tip: 🔑 add access controls to secrets: https://dotenvx.com/ops D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\utils\pick.ts:4 res[k] = obj[k]; ^ TypeError: Cannot read properties of undefined (reading 'address') at pick (D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\utils\pick.ts:4:17) at normalizeDeployment (D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\manifest.ts:347:14) at D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\manifest.ts:333:37 at Array.map (<anonymous>) at normalizeManifestData (D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\manifest.ts:333:28) at Manifest.write (D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\manifest.ts:272:24) at D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\manifest.ts:197:18 at async Manifest.lockedRun (D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\manifest.ts:282:14) at async Manifest.addProxy (D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\upgrades-core\src\manifest.ts:190:5) at async Proxy.deployProxy (D:\csdn\Hardhat2.22.17\node_modules\@openzeppelin\hardhat-upgrades\src\deploy-proxy.ts:114:5) D:\csdn\Hardhat2.22.17>

原因:Hardhat 2.22.17 + OpenZeppelin Hardhat Upgrades v3+ = 不兼容

如果报错,可以这样解决,修改UUPS.js内容为:

const hre = require("hardhat"); async function deploy() { try { // 1. 部署 UUPSV1 代理合约(保留小写 kind: "uups") const _UUPSV1 = await hre.ethers.getContractFactory("UUPSV1"); console.log("开始部署 UUPSV1 代理合约..."); const v1 = await hre.upgrades.deployProxy(_UUPSV1, [1], { initializer: "initialize", kind: "uups", }); await v1.waitForDeployment(); // Ethers v6 等待部署完成 // 2. Ethers v6 获取地址(必须用 getAddress()) const proxyAddress = await v1.getAddress(); console.log("代理合约地址:", proxyAddress); // 3. 验证初始值 const initialX = await v1.x(); console.log("初始 x 值:", initialX.toString()); // 预期输出 1 // 4. 调用 V1 的 call 方法 await v1.call(); const afterV1CallX = await v1.x(); console.log("V1 call 后 x 值:", afterV1CallX.toString()); // 预期输出 2 // 5. 升级到 UUPSV2 console.log("开始升级到 UUPSV2..."); const _UUPSV2 = await hre.ethers.getContractFactory("UUPSV2"); const v2 = await hre.upgrades.upgradeProxy(proxyAddress, _UUPSV2); await v2.waitForDeployment(); // 6. 验证升级后的值 const afterUpgradeX = await v2.x(); console.log("升级后 x 值:", afterUpgradeX.toString()); // 预期输出 2 await v2.call(); const afterV2CallX = await v2.x(); console.log("V2 call 后 x 值:", afterV2CallX.toString()); // 预期输出 4 } catch (error) { console.error("部署失败:", error); process.exit(1); } } deploy();

清理所有缓存

# 删除 OpenZeppelin 清单缓存 rmdir /s /q .openzeppelin # 删除编译缓存 npx hardhat clean # 删除节点缓存(适配 hardhat@2.22.17) rmdir /s /q %USERPROFILE%\.hardhat\node-cache

重新编译合约

npx hardhat compile

启动本地节点(新开管理员终端):

npx hardhat node

运行部署脚本(另一个终端):

npx hardhat run test/UUPS.js --network hardhat

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 11:53:49

MuJoCo弹性插件实战指南:从基础建模到工业级软体机器人仿真

MuJoCo弹性插件实战指南&#xff1a;从基础建模到工业级软体机器人仿真 【免费下载链接】mujoco Multi-Joint dynamics with Contact. A general purpose physics simulator. 项目地址: https://gitcode.com/GitHub_Trending/mu/mujoco 在当今机器人技术快速发展的时代&…

作者头像 李华
网站建设 2026/7/7 15:55:53

Windows 11 LTSC应用商店恢复终极指南:快速安装Microsoft Store完整教程

Windows 11 LTSC版本作为企业级系统&#xff0c;默认不包含Microsoft Store应用商店&#xff0c;这给需要安装UWP应用的用户带来了不便。LTSC-Add-MicrosoftStore工具提供了一种简单有效的解决方案&#xff0c;让LTSC系统重新支持应用商店功能。本教程将详细介绍如何快速完成Wi…

作者头像 李华
网站建设 2026/7/7 12:07:12

FF14插件自动跳过动画:告别重复观看,专注核心战斗

FF14插件自动跳过动画&#xff1a;告别重复观看&#xff0c;专注核心战斗 【免费下载链接】FFXIV_ACT_CutsceneSkip 项目地址: https://gitcode.com/gh_mirrors/ff/FFXIV_ACT_CutsceneSkip 还在为每次刷副本都要手动跳过相同的过场动画而烦恼吗&#xff1f;《最终幻想X…

作者头像 李华
网站建设 2026/7/7 18:25:42

Res-Downloader资源下载器:手把手教你轻松下载全网视频音频素材

还在为无法保存微信视频号、抖音快手无水印内容而烦恼吗&#xff1f;Res-Downloader这款跨平台资源下载工具&#xff0c;能够帮你轻松解决这些困扰。作为一款基于Go语言开发的专业下载器&#xff0c;它不仅支持多平台运行&#xff0c;还能智能识别各类网络资源&#xff0c;让资…

作者头像 李华
网站建设 2026/7/7 8:47:47

如何用SpiffWorkflow快速构建Python业务流程自动化系统

还在为复杂的业务审批流程头疼吗&#xff1f;每次需求变更都要重新编码&#xff0c;业务人员无法直接参与流程设计&#xff1f;别担心&#xff0c;今天我要分享一个纯Python工作流引擎的实战指南&#xff0c;帮你3分钟搞定业务流程自动化&#xff01; 【免费下载链接】SpiffWor…

作者头像 李华
网站建设 2026/7/7 7:38:18

vue基于Spring Boot的农产品运输服务管理系统的应用和研究_xl4yl4gr

目录具体实现截图项目介绍论文大纲核心代码部分展示项目运行指导结论源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作具体实现截图 本系统&#xff08;程序源码数据库调试部署讲解&#xff09;同时还支持java、ThinkPHP、Node.js、Spring B…

作者头像 李华