本文介绍了如何使用 Remix IDE 和 OpenZeppelin 库创建自己的 ERC-20 代币 Turra's Coffee Token (TCT)。
你好,欢迎回到 Turra’s Coffee Token 系列!在第一部分中,我们对 ERC-20 代币 进行了概念性的理解,探索了它们改变游戏规则的影响,并介绍了我们自己的项目:Turra’s Coffee Token (TCT)。
现在,是时候将愿景转化为有形的数字资产了。你会惊讶于创建自己的 ERC-20 代币 是多么的简单,尤其是在强大的工具的帮助下。在本文结束时,你将编译自己的 TCT 并将其部署到测试网络——所有这些都无需任何复杂的本地设置!
对于这次实践编码冒险,我们将依赖两个旨在简化你旅程的强大工具:
如果你还记得,ERC-20 代币 必须具有六个基本功能(totalSupply、transfer、balanceOf、approve、allowance 和 transferFrom)。手动编写每个函数显然是繁琐的工作;你需要编写并测试它。不仅耗时,而且极易出错和出现安全漏洞。
这就是 OpenZeppelin 的用武之地!OpenZeppelin 提供了一个安全、经过实战测试且经过社区审计的智能合约库,这些智能合约 专门实现了许多标准,包括 ERC-20。有了这个库,你无需担心自己编写那些基本的标准函数。你可以专注于你的特定应用程序。
正如承诺的那样,对于本次编码会话,我们将保持简单和基于浏览器的方式。让我们在 Remix IDE 中准备好我们的工作区:
https://remix.ethereum.org
(自我更正提示:如果 Remix 要求你选择一个工作区,只需选择一个默认工作区或创建一个新的空工作区以保持清洁。你可能还会看到一些已打开的默认文件;如果它们使你的视图混乱,你可以关闭它们。)
2. 创建我们的智能合约文件:
TurrasCoffeeToken.sol
并按 Enter 键。这就是我们代币代码的存放地!3. 无需安装 OpenZeppelin!(Remix 的魔力 )
npm install @openzeppelin/contracts
的本地开发环境不同,Remix 会自动处理标准库(如 OpenZeppelin)的导入。import "@openzeppelin/contracts/...";
,Remix 的编译器就会智能地获取并提供这些合约。无需额外的插件激活或安装步骤即可使用 OpenZeppelin 合约!创建了我们的文件并且 Remix 已准备好处理我们的 OpenZeppelin 导入,我们就可以开始激动人心的部分了:编写 Turra’s Coffee Token 的实际代码!
事不宜迟,让我们深入研究代码。
我们需要编写的第一件事是许可证标识符和 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 版本。^ 符号表示“与此版本和任何较新的次要版本兼容,但不包括重大更改。”
在我们继续之前,我想设置我们代币的一些功能:
因此,要做到这一点,我们首先需要导入 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 用于提供代币燃烧能力。
免费加入 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, ERC20Burnable
:is
是一种“继承”或“从...获得超能力”的方法。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 字节的数据序列,非常适合存储加密哈希。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
函数。这是必要的,因为 ERC20
和 ERC20Capped
合约(我们从中继承)都有它们自己版本的相同函数。为了防止错误,我们必须告诉我们的合约使用哪一个。此代码实际上是说:“我正在覆盖此函数,因此请使用 ERC20Capped
中的版本,然后使用 super
关键字也调用 ERC20
中的原始逻辑。”这确保了核心 ERC-20 逻辑和我们代币的上限都得到正确执行。
如果你不添加 _update() 函数会发生什么
然后,自定义 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 以换取代币!
代码完成后,就可以编译我们的合约了。Remix 实际上会自动编译你的合约,但了解如何手动操作也没有错。单击左侧边栏中的“Solidity 编译器”图标(看起来像一个 Solidity 徽标)。
确保编译器版本与你的 pragma solidity
匹配(例如,0.8.27
)。Remix 通常会自动选择最佳版本。
关于编译器的一个快速说明: 你会注意到,即使我们设置了 pragma solidity ^0.8.27
,Remix 也可能会推荐一个稍微更新的版本,例如 0.8.30
。这是因为我们使用的 ^
符号表示我们的代码与该版本的任何次要更新兼容,因此 Remix 足够聪明,可以为你选择最新的兼容版本。这是一项功能,而不是错误!
单击“编译 TurrasCoffeeToken.sol”按钮。如果一切看起来都很好,你会看到一个绿色对勾——成功!”
轮到你了!
尝试试验编译器版本。如果你尝试使用与你的代码不兼容的版本进行编译会发生什么?你会看到来自 Remix 的明确错误消息,这是开发过程的重要组成部分!
接下来,单击左侧边栏中的“部署和运行事务”图标(看起来像一个带有箭头的 Ethereum 徽标)。”
在“环境”下,选择“Remix VM”之一。使用此选项,你将在 Remix VM 中部署你的合约,它是一个模拟主网而无需任何设置的虚拟环境,对于开发步骤来说是一个不错的选择。或者,如果你想在特定的测试网中部署,你可以选择“注入提供程序 - MetaMask”。这会将 Remix 连接到你的 MetaMask 钱包,该钱包应设置为像 Sepolia 这样的测试网络(确保你从水龙头中获得了一些测试 ETH!)。
选择环境后,接下来确保在“合约”下拉列表中选择了“TurrasCoffeeToken”。
在部署之前,我们需要告诉我们的合约最初要铸造多少代币以及它的最大供应量(上限)应该是多少。在“部署”部分中,“部署”按钮旁边,你会看到 initialSupply
和 cap
的输入字段。
示例: “让我们使用 1000000000000000000000000
(100 万,带有 18 位小数)作为 initialSupply
,使用 2000000000000000000000000
(200 万,带有 18 位小数)作为 cap
。请记住,Solidity 通常使用固定的小数系统(例如 ETH 使用 18 位小数),因此我们添加所有这些零!”(你可以根据自己的喜好调整这些数字)。
单击橙色的“transact”按钮。确认事务后,你会在下面的“已部署合约”下列出你的已部署合约。复制它的地址!”
现在,让我们玩玩我们的 TCT!展开你已部署的合约。你会看到我们在第 1 部分中讨论的所有那些 ERC-20 函数(如 totalSupply
、balanceOf
、transfer
)——这些都是从 OpenZeppelin 自动继承的!”
操作: “尝试使用你自己的地址调用 totalSupply()
和 balanceOf
。你应该会看到你的初始供应量!你甚至可以尝试将 transfer
到另一个地址。”
初始供应和最大供应
恭喜,数字酿酒师!你刚刚迈出了巨大的一步:编写、编译并将你自己的 ERC-20 Turra’s Coffee Token 部署到区块链!你亲眼目睹了 OpenZeppelin 如何极大地简化安全、标准化的代币功能的实现,从而使我们能够专注于我们代币的独特用途。
但是,如果一个代币孤零零地坐在你的钱包里,那又算什么呢?在我们的最后一期 第 3 部分 中,我们将构建一个 代币销售智能合约,该合约允许渴望的咖啡爱好者(以及其他所有人!)使用 Ether 购买 TCT。准备好启动你的数字咖啡经济,并让你的代币可供全世界使用!
- 原文链接: blog.blockmagnates.com/b...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!