如何以简单的方式创建 ERC20 代币

已发表: 2022-03-11

本文的目标是演示如何在尽可能短的时间内创建 ERC20 代币。

让我们从基础开始:什么是 ERC20 代币?

近年来,ERC20 代币规范已成为以太坊代币的事实标准。 换句话说,目前大多数以太坊合约都符合 ERC20。 本文将详细介绍如何创建自己的以太坊代币,但在开始之前,让我们仔细看看 ERC20 标准。

ERC20 代币说明

是什么让 ERC20 代币如此有吸引力和成功? 有几个因素在起作用:

  1. 正如您将在本教程中看到的那样,ERC20 令牌简单且易于部署。
  2. ERC20 标准解决了一个重大问题,因为基于区块链的市场和加密钱包需要一套单一的标准化命令来与它们管理的代币范围进行通信。 这包括不同代币之间的交互规则,以及代币购买规则。
  3. 它是第一个提供以太坊代币标准化的流行规范。 它绝不是第一个,但由于它的流行,它很快成为了行业标准。

就像其他以太坊代币一样,ERC20 代币被实现为智能合约,并以去中心化的方式在以太坊虚拟机 (EVM) 上执行。

Solidity:智能合约编程语言

以太坊智能合约是用 Solidity 编写的。 虽然有替代语言,但几乎没有人将它们用于此目的。 Solidity 类似于 JavaScript,所以如果你对 JavaScript,甚至 Java 和其他类似 C 的语言有一定的了解,你应该可以毫不费力地弄清楚 Solidity 中的一段代码,甚至在你真正掌握 Solidity 足以使用之前它。

这就是乐趣的开始,因为您应该能够立即开始创建一个简单的 ERC20 合约。 这是一项简单的任务,非常简单,本文将演示如何在一小时内编写和部署 ERC20 代币。

我们将在此演示中创建的代币将是一个简单的 ERC20 实现,没有太多的花里胡哨。 但是,我在现实世界中看到了许多类似的简单令牌,而且它们往往做得很好。

ERC20 通证标准概述

什么是 ERC20?

简而言之,ERC20 标准定义了一组由所有 ERC20 代币实现的功能,以便与其他合约、钱包或市场集成。 这组功能相当简短和基本。

 function totalSupply() public view returns (uint256); function balanceOf(address tokenOwner) public view returns (uint); function allowance(address tokenOwner, address spender) public view returns (uint); function transfer(address to, uint tokens) public returns (bool); function approve(address spender, uint tokens) public returns (bool); function transferFrom(address from, address to, uint tokens) public returns (bool);

ERC20 功能允许外部用户(例如加密钱包应用程序)找出用户的余额,并在获得适当授权的情况下将资金从一个用户转移到另一个用户。

智能合约定义了两个特别定义的事件:

 event Approval(address indexed tokenOwner, address indexed spender, uint tokens); event Transfer(address indexed from, address indexed to, uint tokens);

当用户被授予从帐户中提取代币的权利时,以及代币实际转移后,这些事件将被调用或发出

除了标准的 ERC20 功能外,许多 ERC20 代币还具有附加字段,有些已成为 ERC20 标准的事实上的一部分,如果不是书面形式,那么实际上是在实践中。 以下是此类字段的一些示例。

 string public constant name; string public constant symbol; uint8 public constant decimals;

以下是关于 ERC20 和 Solidity 命名法的几点:

  • 可以在合约本身之外访问public功能
  • view基本上意味着不变,即合约的内部状态不会被函数改变
  • event是 Solidity 允许客户端(例如您的应用程序前端)在合同中的特定事件时收到通知的方式

如果您已经具备基本的 Java/JavaScript 技能,那么大多数 Solidity 语言结构都应该是清楚的。

在 Solidity 中编写 ERC20 代币

稳固的 ERC20 代币

现在我们已经概述了基础知识并解释了创建 ERC20 代币需要什么,是时候开始编写一些逻辑了。

首先,我们需要定义两个映射对象。 这是关联或键/值数组的 Solidity 概念:

 mapping(address => uint256) balances; mapping(address => mapping (address => uint256)) allowed;

表达式mapping(address => uint256)定义了一个关联数组,其键是address类型——一个用于表示帐户地址的数字,其值是uint256类型——一个 256 位整数,通常用于存储代币余额。

第一个映射对象balances将保存每个所有者帐户的代币余额。

第二个映射对象allowed将包括所有获准从给定账户提款的账户以及每个账户允许的提款金额。

如您所见,allowed mapping 的 value 字段本身就是一个映射绘图帐户地址到其批准的提款金额。

这些映射连同所有其他合约字段将存储在区块链中,并将被挖掘,从而将更改传播到所有网络用户节点。

区块链存储成本高昂,您的合约用户需要以一种或另一种方式付费。 因此,您应该始终尝试最小化存储大小并写入区块链。

现在我们已经有了所需的数据结构,我们可以开始将 ERC20 逻辑实际写入适当的函数中。

设置 ICO 代币数量

我们如何设置 ICO 代币的数量? 好吧,有很多方法可以设置 ICO 代币的最大数量,这个问题本身可能值得进行长时间的讨论。

为了我们 ECR20 教程的需要,我们将使用最简单的方法:在合约创建时设置代币总量,并最初将它们全部分配给“合约所有者”,即部署智能合约的账户:

 uint256 totalSupply_; constructor(uint256 total) public { totalSupply_ = total; balances[msg.sender] = _totalSupply; }

构造函数是以太坊在部署合约后立即自动调用的特殊函数。 它通常用于使用合约部署帐户传递的参数初始化令牌的状态。

msg是由以太坊本身声明和填充的全局变量。 它包含执行合同的重要数据。 我们在这里使用的字段: msg.sender包含执行当前合约功能的以太坊账户。

只有部署账户才能进入合约的构造函数。 当合约启动时,该函数将可用代币分配给“合约所有者”账户。

获取总代币供应量

function totalSupply() public view returns (uint256) { return totalSupply_; }

此函数将返回此合约分配的所有代币的数量,无论所有者如何。

获取所有者的代币余额

function balanceOf(address tokenOwner) public view returns (uint) { return balances[tokenOwner]; }

balanceOf将返回由其所有者地址标识的帐户的当前代币余额。

将代币转移到另一个账户

function transfer(address receiver, uint numTokens) public returns (bool) { require(numTokens <= balances[msg.sender]); balances[msg.sender] = balances[msg.sender] — numTokens; balances[receiver] = balances[receiver] + numTokens; emit Transfer(msg.sender, receiver, numTokens); return true; }

顾名思义, transfer函数用于将numTokens数量的代币从所有者的余额转移到另一个用户或receiver的余额。 转让所有者是msg.sender即执行该功能的人,这意味着只有令牌的所有者才能将它们转让给其他人。

Solidity 断言谓词的方式是require 。 在这种情况下,转账账户有足够的余额来执行转账。 如果require语句失败,事务会立即回滚,不会将任何更改写入区块链。

就在退出之前,该函数触发 ERC20 事件Transfer允许注册的侦听器对其完成做出反应。

批准代表提取代币

此功能最常用于代币市场场景。

 function approve(address delegate, uint numTokens) public returns (bool) { allowed[msg.sender][delegate] = numTokens; emit Approval(msg.sender, delegate, numTokens); return true; }

approve的作用是允许所有者,即msg.sender批准委托账户——可能是市场本身——从他的账户中提取代币并将它们转移到其他账户。

如您所见,此功能用于所有者在市场上提供代币的场景。 它允许市场在不等待事先批准的情况下完成交易。

在执行结束时,此函数会触发Approval事件。

获取批准提款的代币数量

function allowance(address owner, address delegate) public view returns (uint) { return allowed[owner][delegate]; }

此函数将所有者当前批准的代币数量返回给特定代表,如approve函数中设置的那样。

委托代币转移

transferFrom函数是我们之前讨论过的approve函数的对等点。 它允许获准提款的代表将所有者资金转移到第三方账户。

 function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) { require(numTokens <= balances[owner]); require(numTokens <= allowed[owner][msg.sender]); balances[owner] = balances[owner] — numTokens; allowed[owner][msg.sender] = allowed[from][msg.sender] — numTokens; balances[buyer] = balances[buyer] + numTokens; Transfer(owner, buyer, numTokens); return true; }

函数启动时的两个require语句用于验证交易是否合法,即所有者有足够的代币可以转移,并且委托人已批准(至少) numTokens可以撤回。

除了将numTokens金额从所有者转移到买方之外,此函数还从受托人的津贴中减去numTokens 。 这基本上允许具有给定津贴的代表将其分成几个单独的提款,这是典型的市场行为。

我们可以在这里停下来,并有一个有效的 ERC20 实施。 但是,我们想要更进一步,因为我们想要一个工业实力代币。 这要求我们使我们的代码更安全一些,尽管我们仍然能够保持令牌相对简单,即使不是基本的。

SafeMath Solidity 库

SafeMath是一个 Solidity 库,旨在处理已知的黑客破坏合约的一种方式:整数溢出攻击。 在这种攻击中,黑客通过传递将使相关整数超过其最大值的参数来强制合约使用不正确的数值。

Solidity 中的 Safemath 库:插图

SafeMath通过在执行算术运算之前测试溢出来防止这种情况发生,从而消除了溢出攻击的危险。 该库非常小,因此对合约大小的影响很小,不会产生任何性能和存储成本损失。

让我们将SafeMath添加到我们的代码中:

 library SafeMath { // Only relevant functions function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a — b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } }

SafeMath使用assert语句来验证传递参数的正确性。 如果assert失败,函数执行将立即停止,所有区块链更改都将回滚。

接下来,让我们添加以下语句,将库引入 Solidity 编译器:

using SafeMath for uint256;

然后,我们用 SafeMath 函数替换一开始使用的简单算法:

 balances[msg.sender] = balances[msg.sender].sub(numTokens); balances[receiver] = balances[receiver].add(numTokens); balances[buyer] = balances[buyer].add(numTokens); balances[owner] = balances[owner].sub(numTokens);

打包在一起

在 Solidity 中,智能合约的功能和事件被包装到一个称为合约的实体中,您可以将其默默地转换为“区块链类”。 下面是我们创建的 ERC20 兼容合约,包括我们的代码要点。 名称和符号字段可以随意更改。 大多数令牌将十进制值保持为 18,因此我们将这样做。

以太坊合约部署

是时候将我们的合约部署到区块链上了。 部署后,我们的合约将转移到参与网络的所有节点。 对合约所做的任何和所有更改都将传播到所有参与节点。

以太坊开发人员通常使用 Truffle 等部署工具。 对于本文的有限需求,即使是 Truffle 也太过分了,一个名为 Remix 的简单在线工具就足够了。

要使用它,您需要在浏览器上安装 MetaMask 插件和一个 Rinkeby(以太坊测试网络)帐户,其中至少包含一些 Rinkeby Ether。 这些都是比较简单的步骤,所以我们不会详细介绍。

如果您没有,请前往 MetaMask 和 Rinkeby 获取下载链接并获得明确的安装和使用说明。

现在我们已经准备好所有构建块,我们将前往 Remix 并将上面的代码(包括 pragma 行和 SafeMath 库)粘贴到在线编辑器中。

然后,我们将跳转到右侧名为“ Run ”的第二个选项卡,然后单击“ Deploy ”。 将出现一个 MetaMask 弹出窗口,要求我们确认交易。 当然,我们会批准。

图片替代文字

  • 绿框:确保你在 Rinkeby
  • 蓝色框:设置您的总代币供应量
  • 红框:部署!

要点:https://gist.github.com/giladHaimov/8e81dbde10c9aeff69a1d683ed6870be#file-basicerc20-sol

恭喜! 您刚刚部署了您的第一个 ERC20 代币,就像真正的以太坊专业人士一样。 正如所承诺的那样,该令牌简单轻巧,功能齐全,符合 ERC20 标准,并受 MathSafe 保护。 它已准备好在整个区块链中购买、支付和转移。

这就是智能合约的全部内容吗?

不,甚至没有接近,因为我们的简短演示几乎没有触及表面,只涉及智能合约开发的一个方面。

智能合约可能要复杂得多,具体取决于您的业务逻辑、用户交互的建模、是否允许铸造和销毁代币、您在合约中引入的生命周期更改、对管理员级别功能的需求,这通常伴随着管理员授权的一组功能,等等。 你得到图片。

不过,如果您可以复制我们在这里所做的工作,那将是扩展您的知识并在必要时转向更复杂的合同的坚实基础。