前言合约一旦署上链是不可修改,为了解决后续合约的新增功能,本文将介绍智能合约可升级的的方案,实现升级智能合约的方案主要通过使用代理合约来实现合约的升级;通用可升级代理合约作用:因为智能合约一旦部署就不可修改,为了解决合约可升级可修改的,才使用代理合约来实现,一句话总结允许在不更改合约地址
合约一旦署上链是不可修改,为了解决后续合约的新增功能,本文将介绍智能合约可升级的的方案,实现升级智能合约的方案主要通过使用代理合约来实现合约的升级;
通用可升级代理合约
作用:因为智能合约一旦部署就不可修改,为了解决合约可升级可修改的,才使用代理合约来实现, 一句话总结允许在不更改合约地址的情况下更新合约的逻辑部分;
特点:
- 升级逻辑在逻辑合约中:这种设计使得逻辑合约本身可以被升级;
- 部署成本较低:由于不需要在代理合约中实现复杂的升级逻辑,部署成本相对较低;
- 维护复杂性:维护起来更具挑战性,因为升级逻辑需要在逻辑合约中实现;
注意事项
1.存储布局
- 不要修改已有的存储变量顺序
- 只能在末尾添加新的存储变量
- 不要修改已有的函数签名
2.初始化
- 使用 initialize 而不是 constructor
- 确保初始化函数只能调用一次
- 正确初始化所有继承的合约
3.实现合约
- 禁用实现合约的初始化
- 实现必要的授权检查
- 保持合约逻辑的独立性
4.升级过程
- 仔细测试新版本
- 保持向后兼容性
- 考虑紧急回退机制
herdhat.config文件配置
- 使用
@openzeppelin/contracts-upgradeable
和@openzeppelin/hardhat-upgrades
- 下载
npm i -D @openzeppelin/contracts-upgradeable @openzeppelin/hardhat-upgrades
- 在hardhat.config.js中导入
require(@openzeppelin/hardhat-upgrades)
合约开发
代理合约
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract Box is Initializable, OwnableUpgradeable, UUPSUpgradeable { uint256 private _value;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() public initializer {
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
}
// 存储值
function store(uint256 value) public {
_value = value;
}
// 读取值
function retrieve() public view returns (uint256) {
return _value;
}
// 必需的函数,用于授权升级
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
#### 可升级合约
**说明**:升级合约添加了两个功能:加值和减值的方法;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.22;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract BoxV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable { uint256 private _value;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() public initializer {
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
}
// 存储值
function store(uint256 value) public {
_value = value;
}
// 读取值
function retrieve() public view returns (uint256) {
return _value;
}
// 新增:递增值
function increment() public {
_value += 1;
}
//减值
function decrement() public {
_value -= 1;
}
// 必需的函数,用于授权升级
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
# 合约测试
const { ethers, upgrades } = require("hardhat"); const { expect } = require("chai");
describe("代理合约", function () { let box; let boxV2; //部署代理合约 beforeEach(async function () { const Box = await ethers.getContractFactory("Box"); const BoxV2 = await ethers.getContractFactory("BoxV2");
// 部署初始合约
box = await upgrades.deployProxy(Box, [], {
initializer: "initialize",
kind: "uups"
});
await box.waitForDeployment();
});
describe("部署合约", function () {
it("正确的地址部署", async function () {
console.log("box合约地址",await box.getAddress())
expect(await box.getAddress()).to.be.properAddress;
});
it("返回值", async function () {
expect(await box.retrieve()).to.equal(0);
});
it("存储值", async function () {
await box.store(42);
expect(await box.retrieve()).to.equal(42);
});
});
describe("升级", function () {
// 升级合约
beforeEach(async function () {
const BoxV2 = await ethers.getContractFactory("BoxV2");
boxV2 = await upgrades.upgradeProxy(await box.getAddress(), BoxV2);
});
it("验证新功能方法", async function () {
//新增
await boxV2.store(42);
await boxV2.increment();
expect(await boxV2.retrieve()).to.equal(43);
//减少
await boxV2.store(42);
await boxV2.decrement();
expect(await boxV2.retrieve()).to.equal(41);
});
it("存储内容", async function () {
const BoxV2 = await ethers.getContractFactory("BoxV2");
console.log("box合约地址",await box.getAddress())
await box.store(42);
boxV2 = await upgrades.upgradeProxy(await box.getAddress(), BoxV2);
expect(await boxV2.retrieve()).to.equal(42);
});
});
describe("验证升级权限", function () {
it("非所有者升级", async function () {
const [owner, addr1] = await ethers.getSigners();
const BoxV2 = await ethers.getContractFactory("BoxV2", addr1);
// console.log("报错", await upgrades.upgradeProxy(await box.getAddress(), BoxV2))
await expect(
upgrades.upgradeProxy(await box.getAddress(), BoxV2)
).to.be.revertedWithCustomError(
box,
"OwnableUnauthorizedAccount"
);
});
});
});
# 部署合约
#### 代理合约
const { ethers, upgrades } = require("hardhat");
async function main() { const Box = await ethers.getContractFactory("Box");
const box = await upgrades.deployProxy(Box, [], {
initializer: "initialize",
kind: "uups"
});
await box.waitForDeployment();
console.log("代理合约地址:", await box.getAddress());
}
main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
#### 升级合约
const { ethers, upgrades } = require("hardhat"); async function main() { //部署代理合约 const Box = await ethers.getContractFactory("Box"); const box = await upgrades.deployProxy(Box, [], { initializer: "initialize", kind: "uups" }); await box.waitForDeployment(); console.log("代理合约地址:", await box.getAddress()); //升级合约 const BoxV2 = await ethers.getContractFactory("BoxV2"); console.log("Upgrading Box...");
// 替换为你的代理合约地址
// const PROXY_ADDRESS = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512";//your_proxy_address_here
await upgrades.upgradeProxy(await box.getAddress(), BoxV2);
console.log("Box upgraded");
}
main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
# 总结
以上就是借助openzeppelin的库实现通用可升级代理合约的方法包含了开发、测试、部署以及此方法的概念、特点和相关注意事项。`注意`:编写合约一定要按照注意事项的规定;
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!