主页 > 如何下载imtoken钱包 > 首页> imtoken 多签钱包> 如何在以太坊上发行自己的代币
首页> imtoken 多签钱包> 如何在以太坊上发行自己的代币
简单代币开发
令牌:
代币,如果单纯从名字上理解,是一种可以代替一般货币,充当交换媒介的东西。 可以是商城积分,也可以是游戏币,也可以是筹码。 但在区块链中,并非如此。 区块链中的代币或通证,通常是指加密数字权益的流通,例如比特币、以太坊等数字货币。
从上面的定义,我们可以知道token的三个要素:
综上所述,简而言之,令牌的概念大致基于基于区块链的加密货币或类似的东西。 通常大多数人或团队在开发区块链项目时都会考虑发行自己的代币。 一开始,如果我们要发行自己的加密货币,我们将不得不改编比特币的源代码。 但是现在通过以太坊平台,我们可以很方便的开发和发行自己的代币,所以本文将介绍如何基于以太坊开发自己的代币。
本文将使用到两个工具,分别是 Remix 和 MetaMask。 如果你对这两个工具不是很了解,可以参考我另外两篇文章:
首先,我们使用solidity开发一个最基本功能的“token”demo,了解token最基本的外观。 代码如下:
pragma solidity ^0.4.20;
contract SimpleToken {
// 保存每个地址所拥有的代币数量
mapping (address => uint256) public balanceOf;
// 参数为代币的初始供应量或者说发行数量
constructor (uint256 initSupply) public {
// 创建者拥有所有的代币
balanceOf[msg.sender] = initSupply;
}
/**
* 转移代币
*
* @param _to 接收方的账户地址
* @param _value 转移的代币数量
*/
function transfer (address _to, uint256 _value) public {
// 发送方的账户余额需大于等于转移的代币数量
require(balanceOf[msg.sender] >= _value);
// 检查有没有发生溢出,因为数量有可能超过uint256可存储的范围
require(balanceOf[_to] + _value >= balanceOf[_to]);
// 减少发送方账户的代币数量
balanceOf[msg.sender] -= _value;
// 增加接收方账户的代币数量
balanceOf[_to] += _value;
}
}
1. 代码写好后,在remix上部署合约,首先初始化token supply:
2.查看指定账户拥有的代币数量:
3. 转币到另一个账户地址:
4、此时账户地址中的代币数量只有100个:
ERC-20标准介绍
在上一节中,我们实现了一个简单的通证,但由于其简单性,该通证极其不完整,例如没有通证名称、通证符号、无法控制通证交易等。
因此以太坊总共发行多少枚,以太坊定义了 ERC20。 ERC20是以太坊定义的统一代币标准。 该标准描述了我们在实现代币时必须遵守的协议,如指定代币名称、总量、实现代币交易功能等,只有实现该协议的代币才能被以太坊钱包支持。
下图是实现ERC20协议的代币:
实际上,ERC20协议定义了一套标准接口,使得任何在以太坊上开发的代币都可以被其他应用复用,比如钱包到去中心化交易所等,如下:
contract ERC20Interface {
// 代币名称
string public constant name = "Token Name";
// 代币符号或者说简写
string public constant symbol = "SYM";
// 代币小数点位数,代币的最小单位, 18表示我们可以拥有 0.0000000000000000001单位个代币
uint8 public constant decimals = 18; // 18 is the most common number of decimal places
// 查询代币的发行总量
function totalSupply() public constant returns (uint);
// 查询地址的代币余额
function balanceOf(address tokenOwner) public constant returns (uint balance);
// 查询spender允许从tokenOwner上花费的代币数量
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
// 实现代币交易,用于从本账户给某个地址转移代币
function transfer(address to, uint tokens) public returns (bool success);
// 允许spender多次从你的账户取款,并且最多可取tokens个,主要用于某些场景下授权委托其他用户从你的账户上花费代币
function approve(address spender, uint tokens) public returns (bool success);
// 实现代币用户之间的交易,从一个地址转移代币到另一个地址
function transferFrom(address from, address to, uint tokens) public returns (bool success);
// 代币交易时触发的事件,即调用transfer方法时触发
event Transfer(address indexed from, address indexed to, uint tokens);
// 允许其他用户从你的账户上花费代币时触发的事件,即调用approve方法时触发
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
ERC-20标准代币的实施
代码如下:
pragma solidity ^0.4.20;
// 定义ERC-20标准接口
contract ERC20Interface {
// 代币名称
string public name;
// 代币符号或者说简写
string public symbol;
// 代币小数点位数,代币的最小单位
uint8 public decimals;
// 代币的发行总量
uint public totalSupply;
// 实现代币交易,用于给某个地址转移代币
function transfer(address to, uint tokens) public returns (bool success);
// 实现代币用户之间的交易,从一个地址转移代币到另一个地址
function transferFrom(address from, address to, uint tokens) public returns (bool success);
// 允许spender多次从你的账户取款,并且最多可取tokens个,主要用于某些场景下授权委托其他用户从你的账户上花费代币
function approve(address spender, uint tokens) public returns (bool success);
// 查询spender允许从tokenOwner上花费的代币数量
function allowance(address tokenOwner, address spender) public view returns (uint remaining);
// 代币交易时触发的事件,即调用transfer方法时触发
event Transfer(address indexed from, address indexed to, uint tokens);
// 允许其他用户从你的账户上花费代币时触发的事件,即调用approve方法时触发
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
// 实现ERC-20标准接口
contract ERC20Impl is ERC20Interface {
// 存储每个地址的余额(因为是public的所以会自动生成balanceOf方法)
mapping (address => uint256) public balanceOf;
// 存储每个地址可操作的地址及其可操作的金额
mapping (address => mapping (address => uint256)) internal allowed;
// 初始化属性
constructor() public {
name = "Test Token";
symbol = "TEST";
decimals = 18;
totalSupply = 100000000;
// 初始化该代币的账户会拥有所有的代币
balanceOf[msg.sender] = totalSupply;
}
function transfer(address to, uint tokens) public returns (bool success) {
// 检验接收者地址是否合法
require(to != address(0));
// 检验发送者账户余额是否足够
require(balanceOf[msg.sender] >= tokens);
// 检验是否会发生溢出
require(balanceOf[to] + tokens >= balanceOf[to]);
// 扣除发送者账户余额
balanceOf[msg.sender] -= tokens;
// 增加接收者账户余额
balanceOf[to] += tokens;
// 触发相应的事件
emit Transfer(msg.sender, to, tokens);
success = true;
}
function transferFrom(address from, address to, uint tokens) public returns (bool success) {
// 检验地址是否合法
require(to != address(0) && from != address(0));
// 检验发送者账户余额是否足够
require(balanceOf[from] >= tokens);
// 检验操作的金额是否是被允许的
require(allowed[from][msg.sender] <= tokens);
// 检验是否会发生溢出
require(balanceOf[to] + tokens >= balanceOf[to]);
// 扣除发送者账户余额
balanceOf[from] -= tokens;
// 增加接收者账户余额
balanceOf[to] += tokens;
// 触发相应的事件
emit Transfer(from, to, tokens);
success = true;
}
function approve(address spender, uint tokens) public returns (bool success) {
allowed[msg.sender][spender] = tokens;
// 触发相应的事件
emit Approval(msg.sender, spender, tokens);
success = true;
}
function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
return allowed[tokenOwner][spender];
}
}
将上述代码复制到Remix并编译合约代码:
部署合同:
部署成功:
测试 balanceOf 方法:
测试传输方法:
在 Remix 上测试基本方法后,我们通过 MetaMask 在以太坊的测试网上发行了我们的代币。 打开MetaMask以太坊总共发行多少枚,选择Ropsten测试网络,如下:
注意:如果您没有余额,请点击DEPOSIT按钮,然后点击GET ETHER按钮,您将进入一个网站,点击网站中的request 1 ether from faucet,您可以发送一些测试网络的以太币给您:
然后转到 Remix IDE 刷新它。 这时,Remix 会自动选择对应的测试网络。 注意MetaMask和Remix需要在同一个浏览器上,Environment和Account要和MetaMask保持一致,如下:
点击Deploy后,MetaMask会弹出交易确认框,点击CONFIRM:
合约部署交易确认后,复制合约地址:
打开Metamask界面,打开菜单(Menu),点击ADD TOKEN:
出现如下对话框,选择Custom Token,然后将合约地址粘贴进去:
点击添加令牌:
这时我们可以在MetaMask中看到我们创建的token,如下:
接下来我们来测试代币转账功能,选择我们刚刚部署的代币,点击SEND:
填写转账相关信息:
确认转移:
成功后会生成相应的交易记录:
这样我们就完成了token的创建和部署(正式网和测试网的部署方法是一样的),现在我们可以在Etherscan中查询刚刚发行的token:
点击合约地址查看代币详情:
关于发行总量为0的问题
上一节中,以太坊上一共发行了多少代币? 我们在以太坊上成功发行了自己的代币,但是很多同学应该已经有疑虑了。 为什么token明明设置了发行数量,但发行数量还是0? 其实这也是新手在开发代币时可能遇到的一个坑。 之所以发行总量为0,是因为代币发行总量的整数位不是totalSupply的值,而是totalSupply除以10的小数次方。
例如,如果我们将 decimals 设置为 8,那么我们需要将令牌的总供应量(totalSupply)除以 100000000 得到一个整数。 上一节合约代码中设置的decimals的值为18,即token使用的小数位数为18位,totalSupply的值为1亿。 自然是按小于1的小数来计算,而且还是比较小的小数。 MetaMask 只显示 3 位小数,因此我们认为发行的代币总数为 0。
既然您知道是什么原因造成的,那么修复它就很容易了。 修改合约的constructor token如下:
// 初始化属性
constructor() public {
decimals = 18;
totalSupply = 100000000 * 10 ** uint256(decimals);
name = "Test Token";
symbol = "TEST";
// 初始化该代币的账户会拥有所有的代币
balanceOf[msg.sender] = totalSupply;
}
修改完成后,仍然按照上一节描述的流程发行代币,并将代币添加到MetaMask钱包中。 这时候代币发行总量应该就是我们期望的值,如下:
我们也可以尝试使用代币进行转账:
转账成功,账户余额也正常扣除:
另一个账户也正常收到了这100个代币:
然后让我们检查令牌详细信息: