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