Skip to main content

Command Palette

Search for a command to run...

如何部署 Solidity 可升级合约?

Published
2 min read
如何部署 Solidity 可升级合约?

智能合约工程师, 业余Youtuber, 投机套利爱好者.

今天我将讨论一个我认为对所有 Solidity 开发人员都非常有用的主题,即可升级的智能合约。

在我们开始之前,我们需要区分“可升级”并不意味着“可变”,区块链中的代码本质上是不可变的,一旦部署,它将永远存在。

究竟什么是可升级合约?

当我们说合约是可升级的时,我们指的是使用代理模式的合约。

此过程涉及部署两个合约:

  • 代理合约

  • 实现合约

代理模式的结构类似于:

image.png

与客户端(RPC)直接与实现合约交互(常见的不可升级合约)不同,可升级合约在两者之间有一个代理合约,用于指出智能合约实现的不同版本。

简而言之,这种模式实际上所做的是,每次我们需要升级合约时,我们都会部署一个新的实现合约,并更改指向具有新地址的实现合约的代理合约的值。

这种模式给人一种智能合约可升级的感觉,因为用户与之交互的智能合约 (代理合约地址) 地址永远不会改变。

有两种流行的代理模式:

  • Transparent Proxy 透明代理

  • UUPS (通用可升级代理标准)

可升级 VS 不可升级

除了一个可升级而另一个不可升级这一显而易见的事实之外,不可升级的智能合约与可升级的智能合约之间的另一个主要区别是缺少构造函数

相反,我们使用了一个名为initialize的函数(请注意,这个名称不是强制性的,你可以使用任何名称)。

具体演示

让我们深入研究代码以获得更直观的理解。

在这个例子中,我将使用 UUPS 代理模式 实现一个简单的 ERC1115 合约,并使用 openzeppelin库来简化编写过程。

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/token/ERC1155/ERC1155Upgradeable.sol";
import "github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/OwnableUpgradeable.sol";
import "github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/proxy/utils/Initializable.sol";
import "github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol";


contract ERC1155UpgradeableV1 is 
    Initializable, 
    OwnableUpgradeable, 
    ERC1155Upgradeable, 
    UUPSUpgradeable
{

    function initialize() public initializer {
        __ERC1155_init("");
        __Ownable_init();
        __UUPSUpgradeable_init();
    }
}

首先,让我们看一下我们将要导入的依赖库。

  • ERC1115Upgradable:ERC1115 合约的可升级版本。

  • OwnableUpgradable:Ownable合约的可升级版本

  • Initializable:这是一个基础合约,可帮助将部署在代理后面的任何类型的合约。

  • UUPSUpgradable:UUPS 模式合约。

现在,让我谈谈初始化函数: initialize()

在这个函数中,我们需要初始化我们导入的合约。

__ERC1115_init:设置代币 URI 并初始化代币合约。 __Ownable_init__UUPSUpgradeable_init:它们都初始化了各自的合约 Initializable 合约不需要初始化,因为我们只需要initializer修饰符。

非常重要的一点是,由于我们不使用构造函数,我们必须使用initializer修饰符保护我们的初始化函数,以确保它只能被调用一次以避免代理黑客攻击。

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/token/ERC1155/ERC1155Upgradeable.sol";
import "github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/OwnableUpgradeable.sol";
import "github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/proxy/utils/Initializable.sol";
import "github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol";


contract ERC1155UpgradeableV1 is 
    Initializable, 
    OwnableUpgradeable, 
    ERC1155Upgradeable, 
    UUPSUpgradeable
{

    function initialize() public initializer {
        __ERC1155_init("");
        __Ownable_init();
        __UUPSUpgradeable_init();
    }

    function setURI(string memory newuri) public {
        _setURI(newuri);
    }

    function mint(
        address account,
        uint256 id,
        uint256 amount
    ) public {
        _mint(account, id, amount, "");
    }

    function mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public {
        _mintBatch(to, ids, amounts, data);
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}

其余的合约函数与普通的 ERC1115合约是一样的,唯一的区别是_authorizeUpgrade函数。

由于我们的合约继承自 UUPSUpgradable 合约,我们需要定义并覆盖这个函数

在我的例子中,我没有实现这个函数的内容,因为它通常用于检查谁被授权更新合约,所以我使用 OwnableUpgradable 合约中的onlyOwner修饰符来实现这个目的。

一般来说,使用 openzeppelin 编写代理合约会让你感觉就像在编写普通合约,只是有一些不同之处。

现在我们的合约 v1 已经准备好部署了,所以让我们编写我们的部署脚本,它会略有不同,因为我们需要一次部署两个合约。

Openzeppelin 有两个升级插件来帮助我们部署和升级我们的可升级智能合约。一个用于 Hardhat,另一个用于 Truffle

我将使用 Truffle 插件。

你可以查看官方的 openzeppelin文档进行安装和设置。

安装后,我们现在可以编写我们的脚本了。

const { deployProxy } = require("@openzeppelin/truffle-upgrades");

const ERC1155UpgradeableV1 = artifacts.require("ERC1155UpgradeableV1");

module.exports = async function (deployer) {
  const instance = await deployProxy(ERC1155UpgradeableV1, [], {
    deployer,
    kind: "uups",
  });
};

这就是我们的部署脚本的样子,正如您所见,它与普通的部署脚本略有不同。

我们不使用deployer.deploy函数,而是使用 Truffle 插件中的deployProxy函数。

该函数接收三个参数:

  • 合约

  • 初始化函数的参数(在我们的例子中,我们将此参数传递为空,因为我们的合约初始化函数initialize()不接收任何参数)。

  • 包含 deployer 参数和一组可选参数的对象,在这种情况下,我们传递带有值“uups”的kind参数,声明我们的代理模式是 UUPS 模式

现在我们的可升级合约的部署脚本已经完成。

在部署它之前,请确保对智能合约运行各种测试,虽然它是可升级的,并不意味着我们希望它有任何错误。

结论

这是对可升级合约如何工作以及如何部署合约的快速教程。

希望这可以帮助到你.

关注我们, 获得更多优质内容

参考

  • https://docs.openzeppelin.com/learn/upgrading-smart-contracts

  • https://docs.openzeppelin.com/upgrades-plugins/1.x/

  • https://docs.openzeppelin.com/contracts/4.x/upgradeable 85

893 views

More from this blog

现在谁继续赚取数百万美元?正确答案:鲸鱼和内幕钱包。

现在谁继续赚取数百万美元?正确答案:鲸鱼和内幕钱包。 因为市场完全依赖于他们的操纵, 他们在任何游戏中,拥有绝对优势。 因为Axiom等工具具备监控功能,所以他们也会经常替换钱包: 💰钱包实时监控:https://axiom.trade/@0xcii 📱钱包监控:https://debot.ai?inviteCode=174709 分享一批上周的链上顶级内幕钱包🧵👇 我花了很多时间在链上筛选这些钱包! 请关注我 @AntCaveClub,点赞并转发——确保不再...

Apr 18, 20251 min read34

这个钱包昨天一笔交易用 0.9 Eth 赚了 8.4 Eth

几乎是交易市场的一条定律: 不管新闻是真假, 它带来的影响是真的就行。 昨天,我们知道 特朗普团队发币这一消息后, 第一时间关注市场上的新币。 发现了 ETH 链上的 $DJT 代币, 当时市值仅 $450K , 而现在达到了 $ 3.3 M 。 由于信息较少, 小入了一手, 然后发公告了,估计都跌麻木了没人上车,结果今天获利 8 倍。 另一个值得 关注的信息是特朗普最大主题代币 $TRUMP 今日突然下跌 -40% 我们通过链上数据观察, 这部分游资进入了 $DJT 而这个...

Jun 18, 20241 min read355

Ton 会成为真正的独角兽,因为它拥有

TON 才是独角兽。 正确使用TON 工具能给你带来财富。 SOL / ETH 的 memcoins 交易良好,但 TON 正在超越它。 以下 5 个基本工具可帮助你获得 10-100 倍收益 1⃣️ TON 的土壤 显然,TON 上的 memcoin 越来越受欢迎,TON 市值已超过 160 亿美元。 $NOT 上线币安,其涨幅众所周知。其他大量 TON memcoin 会增值 10-100 倍。让我们学习一下用于搜寻早期 TON 山寨币的主要工具…… 2⃣️ Defilla...

Jun 5, 20241 min read109

昨晚用 1 Eth 赚了 10 Eth ! 没想到睡觉起来就这样了。

昨天睡觉前搞了 $DEW 建仓时市值 500K 附近, 现在 6M 左右了, 一晚上 10 倍。 最近确实以太坊链上出金狗,千万市值天天都有。 也主要因为我睡着了, 睡之前和朋友讲了下,建议他上车,他说他在打球🏀 推荐 $DEW 的主要原因是, 我前几天不是推了 $PEW ,推的时候市值 $2 M 附近,结果 3天一路干到了 $94 M , 大概快有 50 倍... 当时跟朋友讲让他上点车, 他在追剧,忘上车了, 我说下次一定。 他推荐我看《庆余年》,说这个好看。 昨晚看到出...

Jun 1, 20241 min read324

专注 Web3 网络赚钱模式, 区块链实践教程与交流分享

127 posts

专注 Web3 网络赚钱模式, 实践教程与交流分享 !

加入社群 | 订阅Youtube