【翻译】升级智能合约(Hardhat)

这篇文章主要介绍了【翻译】升级智能合约(Hardhat) ,文中通过代码以及文档配合进行讲解,很详细,它对在座的每个人的研究和工作具有很经典的参考价值。 如果需要,让我们与区块链资料网一起学习。

https://www.interchains.cc/19694.html

【翻译】升级智能合约(Hardhat)是很好的区块链资料,他说明了区块链当中的经典原理,可以给我们提供资料,【翻译】升级智能合约(Hardhat)学习起来其实是很简单的,

不多的几个较为抽象的概念也很容易理解,之所以很多人感觉【翻译】升级智能合约(Hardhat)比较复杂,一方面是因为大多数的文档没有做到由浅入深地讲解,概念上没有注意先后顺序,给读者的理解带来困难

【翻译】升级智能合约(Hardhat)

  • OpenZeppelin
  • Hardhat

使用OpenZeppelin升级插件部署的智能合约可以通过升级来修改代码,同时保留原合约地址、状态和余额。这让帮助我们为项目添加新功能,或修复在生产中可能发现的任何错误。

升级智能合约(Hardhat)

原文链接

使用OpenZeppelin升级插件部署的智能合约可以通过升级来修改代码,同时保留原合约地址、状态和余额。这让帮助我们为项目添加新功能,或修复在生产中可能发现的任何错误。

在本指南中,我们将学习:

  • 为什么升级很重要

  • 使用升级插件升级我们的盒子。

  • 了解升级在引擎盖下是如何工作的

  • 学习如何编写可升级合约

什么是可升级的合约

以太坊eth中的智能合约默认情况下是不可更改的。一旦创建了就无法改变,有效地为合约参与者扮演了不可篡改的合约的角色。

然而某些场景下,我们希望能够修改它们。想想传统合约:如果参与双方都同意改变它,就可以去对齐进行改变。同样在以太坊eth上,我们也希望能够修改智能合约,以修复他们发现的bug(这甚至可能导致黑客窃取他们的资金!),增加额外的功能,或者仅仅是改变它所执行的规则。

以下是你需要做的事情,以修复你无法升级的合约中的错误。

  1. 部署一个新版本的合约

  2. 手动将所有的状态从旧的合约迁移到新的合约(这可能是非常昂贵的gas费用!)

  3. 更新所有与旧合约交互的合约,使用新合约的地址

  4. 联系你的所有用户,并说服他们开始使用新的部署(并处理两个合约同时使用的问题,因为用户迁移速度较慢)

为了避免出现这种乱象,我们将合约升级直接内置到我们的插件中。这让我们可以改变合约代码,同时保留状态、余额和地址。让我们来看看如何实现。

使用升级插件来升级合约

使用OpenZeppelin升级插件中的deployProxy部署一个新的合约时,该合约实例就可以实现可升级的功能。默认情况下,只有最初部署合约的地址才有权限执行升级操作。

deployProxy将创建以下事务;

  1. 部署执行合约(我们的Box合约)

  2. 部署ProxyAdmin合约(代理的管理员)

  3. 部署代理合约并运行初始化函数

让我们看看它是如何工作的,通过部署我们的Box合约的可升级版本,使用与之前部署时相同的设置:

// contracts/Box.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.0;  contract Box {     uint256 private value;      // Emitted when the stored value changes     event ValueChanged(uint256 newValue);      // Stores a new value in the contract     function store(uint256 newValue) public {         value = newValue;         emit ValueChanged(newValue);     }      // Reads the last stored value     function retrieve() public view returns (uint256) {         return value;     } }

首先需要安装升级插件(Upgrades Plugin)。

安装Hardhat Upgrades插件。

npm install --save-dev @openzeppelin/hardhat-upgrades

我们需要配置Hardhat使用我们的@openzeppelin/hardhat-upgrades插件。可以通过在hardhat.config.js文件中添加以下代码来添加插件。

// hardhat.config.js require('@nomiclabs/hardhat-ethers'); require('@openzeppelin/hardhat-upgrades');  module.exports = { ... };

为了升级像Box这样的合约,我们需要首先将其部署为一个可升级的合约,这与我们之前看到的部署过程不同。通过调用store来初始化Box合约,其值为42。

Hardhat目前没有原生的部署系统,所以需要使用脚本来部署合约。

创建一个脚本,使用deployProxy部署可升级的Box合约。把文件保存为scripts/deploy_upgradeable_box.js

// scripts/deploy_upgradeable_box.js const { ethers, upgrades } = require("hardhat");  async function main() {   const Box = await ethers.getContractFactory("Box");   console.log("Deploying Box...");   const box = await upgrades.deployProxy(Box, [42], { initializer: 'store' });   await box.deployed();   console.log("Box deployed to:", box.address); }  main();

下面我们就可以部署我们的可升级的合约。

使用run命令,可以部署Box合约到development网络。

$ npx hardhat run --network localhost scripts/deploy_upgradeable_box.js All contracts have already been compiled, skipping compilation. Deploying Box... Box deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0

我们可以通过Box 合约来retrieve我们在初始化时存入的值。

我们使用Hardhat console来与升级合约Box交互。

我们需要在部署Box合约的时候指定代理合约的地址。

$ npx hardhat console --network localhost All contracts have already been compiled, skipping compilation. > const Box = await ethers.getContractFactory("Box") undefined > const box = await Box.attach("0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0") undefined > (await box.retrieve()).toString() '42'

为了方便举例,假设我们想要添加一个新功能:在新版的Box中创建一个自增函数,将存储的value之加一。

// contracts/BoxV2.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.0;  contract BoxV2 {     // ... code from Box.sol      // Increments the stored value by 1     function increment() public {         value = value + 1;         emit ValueChanged(value);     } }

在创建Solidity文件后,我们现在使用upgradeProxy函数升级之前部署的实例。

upgradeProxy将创建以下事务:

  1. 部署执行合约(我们的BoxV2合约)

  2. 调用ProxyAdmin来更新代理合约以应用新的实现

创建一个脚本,使用 upgradeProxyBox 合约升级为使用 BoxV2。把这个文件保存为scripts/upgrade_box.js。需要指定部署Box合约时的代理合约地址。

// scripts/upgrade_box.js const { ethers, upgrades } = require("hardhat");  async function main() {   const BoxV2 = await ethers.getContractFactory("BoxV2");   console.log("Upgrading Box...");   const box = await upgrades.upgradeProxy("0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", BoxV2);   console.log("Box upgraded"); }  main(); 

然后就可以部署我们的可升级合约。

使用run命令,可以在development网络中部署升级Box合约。

$ npx hardhat run --network localhost scripts/upgrade_box.js All contracts have already been compiled, skipping compilation. Box deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0

完成! 我们的Box实例已经升级到了最新版本的代码,同时保持了它的状态和之前的地址。我们不需要在新的地址部署一个新的合约,也不需要手动将旧Boxvalue复制到新Box中。

通过调用新的increment函数来尝试一下,并在检查value值。

需要指定我们部署Box合约时的代理合约地址。

$ npx hardhat console --network localhost All contracts have already been compiled, skipping compilation. > const BoxV2 = await ethers.getContractFactory("BoxV2") undefined > const box = await BoxV2.attach("0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0") undefined > await box.increment() ... > (await box.retrieve()).toString() '43'

就是这样! 请注意,在整个升级过程中,Boxvalue以及它的地址被保存下来了。而且无论你是在本地区块链blockchain,测试网,还是主网络上工作,这个过程都是一样的。

让我们来看看OpenZeppelin升级插件是如何实现的。

升级是如何工作的

这一节会比其他章节理论性更强:可以跳过,如果感兴趣再回来读。

当创建一个新的可升级合约实例时,OpenZeppelin升级插件实际上部署了三个合约。

  1. 你写的合约,也就是所谓的包含逻辑合约实现

  2. 一个ProxyAdmin,作为代理的管理员。

  3. 一个指向实现合约代理,也就是你实际交互的合约。

在这里,代理是一个简单的合约,只是将所有的调用委托给一个实现合约。委托调用(delegate call)类似于普通的调用,只是所有的代码都是在调用者的上下文中执行的,而不是被调用者的上下文。正因为如此,在执行合约的代码中的transfer实际上会转transfer理的余额,对合约存储的任何读或写都会从代理自己的存储中读或写。

这使得我们可以将合约的状态和代码解耦:代理持有状态,而实现合约提供代码。而且它还允许我们改变代码,只需让代理委托给不同的实现合约即可。

升级则包括以下步骤。

  1. 部署新的实现合约

  2. 向代理发送一个事务,将其实现地址更新为新的实现地址。

注意 你可以让多个代理使用同一个实现合约,所以如果你计划部署同一个合约的多个副本,你可以使用这个模式来节省gas。

智能合约的用户总是与代理进行交互,代理永远不会改变其地址。这使您可以推出升级或修复错误,而无需要求用户在他们的端部改变任何东西 – 他们只是一如既往地与相同的地址进行交互。

注意 如果你想了解更多关于OpenZeppelin代理的工作原理,请查看Proxies。

可升级合约的局限

虽然任何智能合约都可以进行升级,但Solidity语言的一些限制需要解决。在编写初始版本的合约和我升级新版本时,都会出现这些问题。

初始化

可升级合约不能有构造函数constructor。为了帮助你初始化代码, OpenZeppelin Contracts提供了Initializable 基础合约,通过在方法上添加initializer标签,确保只被初始化一次。

举例说明,我们通过initializer来写一个新版本的Box合约,设置一个admin为唯一一个可以修改内容的地址。

// contracts/AdminBox.sol // SPDX-License-Identifier: MIT pragma solidity ^0.6.0;  import "@openzeppelin/contracts/proxy/Initializable.sol";  contract AdminBox is Initializable {     uint256 private value;     address private admin;      // Emitted when the stored value changes     event ValueChanged(uint256 newValue);      function initialize(address _admin) public initializer {         admin = _admin;     }      // Stores a new value in the contract     function store(uint256 newValue) public {         require(msg.sender == admin, "AdminBox: not admin");         value = newValue;         emit ValueChanged(newValue);     }      // Reads the last stored value     function retrieve() public view returns (uint256) {         return value;     } }

部署合约时,我们需要指定initializer函数名(只有当名字不是initialize时需要),并提供一个管理员地址。

// scripts/deploy_upgradeable_adminbox.js const { ethers, upgrades } = require("hardhat");  async function main() {   const AdminBox = await ethers.getContractFactory("AdminBox");   console.log("Deploying AdminBox...");   const adminBox = await upgrades.deployProxy(AdminBox, ['0xACa94ef8bD5ffEE41947b4585a84BdA5a3d3DA6E'], { initializer: 'initialize' });   await adminBox.deployed();   console.log("AdminBox deployed to:", adminBox.address); }  main();

出于实践目的,initializer作为构造函数。然而,请记住,由于它是一个常规函数,你将需要手动调用所有基础合约(base contract)的初initializer(如果有的话)。

要了解更多关于这一点以及编写可升级合约时的其他注意事项,请查看我们的Writing Upgradeable Contracts 指南。

升级

由于技术上的限制,当你将一个合约升级到新版本时,你不能改变该合约的存储布局(storage layout)

这意味着,如果你已经在合约中声明了一个状态变量,你就不能删除它,不能改变它的类型,也不能在它之前声明其他变量。在我们的Box例子中,这意味着我们只能在value之后添加新的状态变量。

// contracts/Box.sol contract Box {     uint256 private value;      // We can safely add a new variable after the ones we had declared     address private owner;      // ... }

幸运的是,这种限制只影响状态变量。你可以随心所欲地改变合约的功能和事件。

注意 如果你不小心弄乱了合约的存储布局,当尝试升级时,升级插件提出警告。

前往Modifying Your Contracts 指南了解更多限制。

测试

为了测试可升级的合约,我们应该为实现合约创建单元测试,同时创建更高级别的测试,来测试与代理的交互。可以在测试中使用deployProxy,就像我们部署时一样。

当要升级时,我们应该为新的实现合约创建单元测试,同时创建更高级别的测试,以便在升级后使用 upgradeProxy 通过代理测试交互,检查在升级过程中是否保持状态一致。

接下来的步骤

现在你已经知道如何升级智能合约,并且可以迭代开发你的项目,是时候把你的项目带到测试网和正式网中去了。你可以放心,如果出现bug,你有工具来修改你的合约并修复它。

  • 发表于 20小时前
  • 阅读 ( 14 )
  • 学分 ( 0 )
  • 分类:Solidity

部分转自网络,侵权联系删除www.interchains.cchttps://www.interchains.cc/19694.html

区块链毕设网(www.interchains.cc)全网最靠谱的原创区块链毕设代做网站 部分资料来自网络,侵权联系删除! 最全最大的区块链源码站 ! QQ3039046426
区块链知识分享网, 以太坊dapp资源网, 区块链教程, fabric教程下载, 区块链书籍下载, 区块链资料下载, 区块链视频教程下载, 区块链基础教程, 区块链入门教程, 区块链资源 » 【翻译】升级智能合约(Hardhat)

提供最优质的资源集合

立即查看 了解详情