酿造我们自己的数字资产:使用 OpenZeppelin 编写 Turra's Coffee Token (TCT) 代码

本文介绍了如何使用 Remix IDE 和 OpenZeppelin 库创建自己的 ERC-20 代币 Turra's Coffee Token (TCT)。

你好,欢迎回到 Turra’s Coffee Token 系列!在第一部分中,我们对 ERC-20 代币 进行了概念性的理解,探索了它们改变游戏规则的影响,并介绍了我们自己的项目:Turra’s Coffee Token (TCT)。

现在,是时候将愿景转化为有形的数字资产了。你会惊讶于创建自己的 ERC-20 代币 是多么的简单,尤其是在强大的工具的帮助下。在本文结束时,你将编译自己的 TCT 并将其部署到测试网络——所有这些都无需任何复杂的本地设置!

对于这次实践编码冒险,我们将依赖两个旨在简化你旅程的强大工具:

  • Remix IDE: 这是我们基于浏览器的 Solidity 智能合约开发环境。它非常用户友好,这意味着你无需在本地下载或安装任何东西即可进行操作——只需一个 Web 浏览器和一个互联网连接。
  • OpenZeppelin Contracts: 这是一个库,其中包含经过实战测试、安全且经过预先审计的代码,用于常见的区块链标准,如 ERC-20(和 ERC-721 等)。它可以帮助我们快速安全地构建强大的代币,而无需从头开始编写每个基本代码。

2. 为什么要使用 OpenZeppelin?

如果你还记得,ERC-20 代币 必须具有六个基本功能(totalSupply、transfer、balanceOf、approve、allowance 和 transferFrom)。手动编写每个函数显然是繁琐的工作;你需要编写并测试它。不仅耗时,而且极易出错和出现安全漏洞。

这就是 OpenZeppelin 的用武之地!OpenZeppelin 提供了一个安全、经过实战测试且经过社区审计的智能合约库,这些智能合约 专门实现了许多标准,包括 ERC-20。有了这个库,你无需担心自己编写那些基本的标准函数。你可以专注于你的特定应用程序。

3. 在 Remix IDE 中设置我们的项目

正如承诺的那样,对于本次编码会话,我们将保持简单和基于浏览器的方式。让我们在 Remix IDE 中准备好我们的工作区:

  1. 打开 Remix IDE:
  • 首先,打开你的 Web 浏览器并导航到 Remix IDE 网站:https://remix.ethereum.org

(自我更正提示:如果 Remix 要求你选择一个工作区,只需选择一个默认工作区或创建一个新的空工作区以保持清洁。你可能还会看到一些已打开的默认文件;如果它们使你的视图混乱,你可以关闭它们。)

2. 创建我们的智能合约文件:

  • 在左侧边栏的“文件资源管理器”面板中(它看起来像一个文件夹图标 📁),单击“创建新文件”图标(它通常是一个加号 + 或一个带有加号的纸张图标)。
  • 将你的新 Solidity 文件命名为 TurrasCoffeeToken.sol 并按 Enter 键。这就是我们代币代码的存放地!

3. 无需安装 OpenZeppelin!(Remix 的魔力 )

  • 这就是 Remix 真正闪光的地方!与通常运行 npm install @openzeppelin/contracts 的本地开发环境不同,Remix 会自动处理标准库(如 OpenZeppelin)的导入。
  • 一旦你在 Solidity 文件中键入 import "@openzeppelin/contracts/...";,Remix 的编译器就会智能地获取并提供这些合约。无需额外的插件激活或安装步骤即可使用 OpenZeppelin 合约

创建了我们的文件并且 Remix 已准备好处理我们的 OpenZeppelin 导入,我们就可以开始激动人心的部分了:编写 Turra’s Coffee Token 的实际代码!

4. 制作我们的 Turra’s Coffee Token (TCT) 智能合约(代码演练):

事不宜迟,让我们深入研究代码。

第一部分:基本结构和导入

我们需要编写的第一件事是许可证标识符和 solidity 版本。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

// SPDX-License-Identifier 是一条注释,用于告知任何查看你的代码的人,如果他们想使用或构建它,需要遵循哪些规则。有一些许可协议,例如 MIT、GPL-3.0-only 和 UNLICENSED。MIT 许可证允许任何人使用、修改和重新分发它,只要它们保留原始许可证,此许可证通常用于开源项目。

如果你的项目仅供内部使用,并且你不打算让其他人使用它,则可以使用 UNLICENSED,它基本上是一个封闭的项目。

然后 pragma solidity ^0.8.27; 行告诉 Remix 编译器使用哪个 Solidity 版本。^ 符号表示“与此版本和任何较新的次要版本兼容,但不包括重大更改。”

在我们继续之前,我想设置我们代币的一些功能:

  • 它将具有名称和符号
  • 某些函数只能由具有特定角色的特定帐户调用,例如 minter、burner 等。
  • 我们的代币将具有最大代币分配量 (cap),这将使代币具有价值并避免滥用的可能性。
  • 我们的代币是可燃烧的,这意味着代币可以被所有者销毁。

因此,要做到这一点,我们首先需要导入 openzeppelin 构建的一些实用程序。我们实际上是在告诉我们的合约“借用”预先编写的、高度安全的代码来实现核心 ERC-20 功能,以及用于访问控制(谁可以做什么)、代币上限(最大供应量)和可燃烧能力(代币所有者可以销毁其代币)的功能。

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {ERC20Capped} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

正如你所看到的,要从 openzeppelin 导入模块,你可以简单地调用 import "<contract_needed", 就像我之前说的那样,我们需要导入四个合约。ERC20.sol 作为核心代币,AccessControl.sol 用于管理角色,ERC20Capped.sol 用于设置最大代币供应量,ERC20Burnable.sol 用于提供代币燃烧能力。

在你的收件箱中获取 Turrah Malan 的故事

免费加入 Medium 以获取这位作家的更新。

Solidity 允许我们使用花括号 {} 从库中导入特定的合约。这是一种最佳实践,因为它有助于保持我们的代码简洁并防止潜在的命名冲突。例如,{ERC20} 告诉编译器仅从 ERC20.sol 文件中获取 ERC20 合约

你还可以使用 OpenZeppelin Wizard 轻松创建基础代码,请在以下位置使用它:https://wizard.openzeppelin.com/

之后,下一步是开始创建合约合约就像 OOP 中的类。在合约中,你可以拥有状态和函数。

contract TurrasCoffeeToken is ERC20, AccessControl, ERC20Capped, ERC20Burnable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    constructor(uint256 initialSupply, uint256 capSupply)
        ERC20("TurrasCoffeeToken", "TCT")
        AccessControl()
        ERC20Capped(capSupply * 10 ** decimals())
    {
        _mint(msg.sender, initialSupply * 10 ** decimals());
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
        _grantRole(BURNER_ROLE, msg.sender);
    }

    function _update(
        address from,
        address to,
        uint256 value
    ) internal virtual override(ERC20, ERC20Capped) {
        super._update(from, to, value);
    }

    function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }

    receive() external payable {}
}

要使用我们导入的库,我们需要在我们的代币合约中继承它。看看第一行 contract TurrasCoffeeToken is ERC20, AccessControl, ERC20Capped, ERC20Burnableis 是一种“继承”或“从...获得超能力”的方法。TCT 现在 一个 ERC-20 代币具有 访问控制,具有 最大上限,并且是可燃烧的。

第二部分:构造函数和初始铸币

在定义新合约之后,我们需要添加一些角色并定义我们代币的一些初始条件,例如名称和符号、最大供应量和角色。

contract TurrasCoffeeToken is ERC20, AccessControl, ERC20Capped, ERC20Burnable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    constructor(uint256 initialSupply, uint256 capSupply)
        ERC20("TurrasCoffeeToken", "TCT")
        AccessControl()
        ERC20Capped(capSupply)
    {
        _mint(msg.sender, initialSupply);
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
        _grantRole(BURNER_ROLE, msg.sender);
    }

以下是该代码的解释:

  • bytes32 public constant MINTER_ROLE = keccak256{“MINTER_ROLE”):是定义角色标识符的代码。bytes32 是 solidity 数据类型,用于存储 32 字节的数据序列,非常适合存储加密哈希。
  • public 是可见性修饰符。solidity 有四个可见性修饰符:public、private、internal 和 external。public 修饰符使变量可以在任何地方(合约内部和外部)被看到和使用。
  • constant 是告诉编译器该值在部署后永远不会更改的关键字。
  • MINTER_ROLE 是变量的名称。
  • keccak256 是 Solidity 原生的加密哈希函数。它接受一串数据(在本例中为 “MINTER_ROLE”)并生成唯一的、固定大小的 bytes32 哈希。keccak256 的关键属性是它是一个单向函数。从字符串生成哈希很容易,但实际上不可能从哈希反向推导出原始字符串。实际上,我们甚至可以使用字符串作为标识符,但它在存储和 gas 效率方面不高。
  • constructor(...) ERC20(...) ERC20Capped(...): 构造函数是一个特殊的函数,仅在我们首次部署智能合约时运行一次。我们在这里设置其初始条件。请注意,代币金额的 uint256 值始终需要考虑小数位数。如果我们想要铸造 1,000 个 Turra’s Coffee Token,我们需要在其后添加 18 个零 (1,000 10¹⁸)。这是一个非常重要的概念!因此,当我们调用此构造函数时,我们将以 `initialSupply 10 * decimals()capSupply 10 decimals()` 的形式输入我们所需的数量,以确保我们获得正确数量的代币**。
  • ERC20("Turra’s Coffee Token", "TCT") ERC20Capped(capSupply * 10 ** decimals()) 此部分通过传递代币的名称、符号和我们想要设置的最大上限来初始化父合约(ERC20 和 ERC20Capped)。
  • _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(MINTER_ROLE, msg.sender); 这些行分配角色。DEFAULT_ADMIN_ROLE 授予完全管理控制权。我们还授予 MINTER_ROLE,这将允许我们在以后铸造新代币msg.sender 指的是部署此合约的地址(可能是你的地址)。
  • _mint(msg.sender, initialSupply); 这是“酿造”我们的第一批 TCT 的关键行!_mint 函数(由 OpenZeppelin 提供)创建初始供应量的代币并将它们发送到 msg.sender(你的地址)。这会立即设置我们的 totalSupply 并更新你的 balanceOf,如第 1 部分中所述!
function _update(
        address from,
        address to,
        uint256 value
    ) internal virtual override(ERC20, ERC20Capped) {
        super._update(from, to, value);
    }

你会注意到这个 _update 函数。这是必要的,因为 ERC20ERC20Capped 合约(我们从中继承)都有它们自己版本的相同函数。为了防止错误,我们必须告诉我们的合约使用哪一个。此代码实际上是说:“我正在覆盖此函数,因此请使用 ERC20Capped 中的版本,然后使用 super 关键字也调用 ERC20 中的原始逻辑。”这确保了核心 ERC-20 逻辑和我们代币的上限都得到正确执行。

如果你不添加 _update() 函数会发生什么

第三部分:自定义 Mint 函数

然后,自定义 mint() 函数用于使 minter 能够创建更多代币(直至达到上限)。

function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }

    receive() external payable {}

该函数需要两个参数:to 和 amount;此函数可以由任何人调用,但只有在调用者具有 MINTER_ROLE 时才会成功,因为我们使用 onlyRole(MINTER_ROLE)。此函数将操作 _mint(),铸造的代币将转移到第一个参数 (to) 的地址,金额为 amount。请记住,这种新的铸币始终会尊重我们为 Turra’s Coffee Token 的总供应量设置的 cap

receive() external payable {} 函数是一个特殊函数,允许我们的智能合约接受发送给它的 Ether (ETH)。虽然我们的 TCT 不是直接构建来处理 ETH 的,但在许多代币合约中包含此函数是一种常见的最佳实践。当我们构建我们的代币销售合约时,它在第 3 部分中将变得更加相关,该合约 需要接受 ETH 以换取代币

5. 在 Remix 中编译我们的合约

代码完成后,就可以编译我们的合约了。Remix 实际上会自动编译你的合约,但了解如何手动操作也没有错。单击左侧边栏中的“Solidity 编译器”图标(看起来像一个 Solidity 徽标)。

确保编译器版本与你的 pragma solidity 匹配(例如,0.8.27)。Remix 通常会自动选择最佳版本。

关于编译器的一个快速说明: 你会注意到,即使我们设置了 pragma solidity ^0.8.27 ,Remix 也可能会推荐一个稍微更新的版本,例如 0.8.30 。这是因为我们使用的 ^ 符号表示我们的代码与该版本的任何次要更新兼容,因此 Remix 足够聪明,可以为你选择最新的兼容版本。这是一项功能,而不是错误!

单击“编译 TurrasCoffeeToken.sol”按钮。如果一切看起来都很好,你会看到一个绿色对勾——成功!”

轮到你了!

尝试试验编译器版本。如果你尝试使用与你的代码不兼容的版本进行编译会发生什么?你会看到来自 Remix 的明确错误消息,这是开发过程的重要组成部分!

6. 在 Remix 中将我们的 Turra’s Coffee Token 部署到测试网络:

接下来,单击左侧边栏中的“部署和运行事务”图标(看起来像一个带有箭头的 Ethereum 徽标)。”

在“环境”下,选择“Remix VM”之一。使用此选项,你将在 Remix VM 中部署你的合约,它是一个模拟主网而无需任何设置的虚拟环境,对于开发步骤来说是一个不错的选择。或者,如果你想在特定的测试网中部署,你可以选择“注入提供程序 - MetaMask”。这会将 Remix 连接到你的 MetaMask 钱包,该钱包应设置为像 Sepolia 这样的测试网络(确保你从水龙头中获得了一些测试 ETH!)。

选择环境后,接下来确保在“合约”下拉列表中选择了“TurrasCoffeeToken”。

在部署之前,我们需要告诉我们的合约最初要铸造多少代币以及它的最大供应量(上限)应该是多少。在“部署”部分中,“部署”按钮旁边,你会看到 initialSupplycap 的输入字段。

示例: “让我们使用 1000000000000000000000000(100 万,带有 18 位小数)作为 initialSupply,使用 2000000000000000000000000(200 万,带有 18 位小数)作为 cap。请记住,Solidity 通常使用固定的小数系统(例如 ETH 使用 18 位小数),因此我们添加所有这些零!”(你可以根据自己的喜好调整这些数字)。

单击橙色的“transact”按钮。确认事务后,你会在下面的“已部署合约”下列出你的已部署合约。复制它的地址!”

现在,让我们玩玩我们的 TCT!展开你已部署的合约。你会看到我们在第 1 部分中讨论的所有那些 ERC-20 函数(如 totalSupplybalanceOftransfer)——这些都是从 OpenZeppelin 自动继承的!”

操作: “尝试使用你自己的地址调用 totalSupply()balanceOf。你应该会看到你的初始供应量!你甚至可以尝试将 transfer 到另一个地址。”

初始供应和最大供应

7. 结论

恭喜,数字酿酒师!你刚刚迈出了巨大的一步:编写、编译并将你自己的 ERC-20 Turra’s Coffee Token 部署到区块链!你亲眼目睹了 OpenZeppelin 如何极大地简化安全、标准化的代币功能的实现,从而使我们能够专注于我们代币的独特用途。

但是,如果一个代币孤零零地坐在你的钱包里,那又算什么呢?在我们的最后一期 第 3 部分 中,我们将构建一个 代币销售智能合约,该合约允许渴望的咖啡爱好者(以及其他所有人!)使用 Ether 购买 TCT。准备好启动你的数字咖啡经济,并让你的代币可供全世界使用!

  • 原文链接: blog.blockmagnates.com/b...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
blockmagnates
blockmagnates
The New Crypto Publication on The Block