一步步教你发行属于自己的以太坊代币
代币Token
如果不那么追求精确的定义,代币就是数字货币,比特币、以太币就是一个代币。 利用以太坊的智能合约可以轻松编写出属于自己的代币,代币可以代表任何可以交易的东西,如:积分、财产、证书等等。 因此不管是出于商业,还是学习很多人想创建一个自己的代币,引用网上的一个图看看创建的代币是什么样子。
你也可以在如下地址查看Tokens:https://etherscan.io/tokens
今天我们就来详细讲一讲怎样创建一个这样的代币。
ERC20 Token
也许你经常看到ERC20和代币一同出现, ERC20是以太坊定义的一个代币标准。 要求我们在实现代币的时候必须要遵守的协议,如指定代币名称、总量、实现代币交易函数等,只有支持了协议才能被以太坊钱包支持。
其接口如下:
contract ERC20Interface {
string public constant name = "Token Name";
string public constant symbol = "SYM";
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);
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
简单说明一下:
name:代币名称
symbol:代币符号
decimals:代币小数点位数,代币的最小单位, 18表示我们可以拥有 .000000000000000001单位个代币。
totalSupply():发行代币总量。
balanceOf():查看对应账号的代币余额。
transfer():实现代币交易,用于给用户发送代币(从我们的账户里)。
transferFrom():实现代币用户之间的交易。
allowance():控制代币的交易,如可交易账号及资产。
approve():允许用户可花费的代币数。
一个最简单的合约代码,详细看注释:
pragma solidity ^0.4.18;
/**
* @title 基础版的代币合约
*/
contract token {
/*记录所有余额的映射*/
mapping (address => uint256) public balanceOf;
/* 初始化合约,并且把初始的所有代币都给这合约的创建者
* @param initialSupply 代币的总数
*/
function token(uint256 initialSupply) {
//给指定帐户初始化代币总量,初始化用于奖励合约创建者
balanceOf[msg.sender] = initialSupply;
}
/**
* 私有方法从一个帐户发送给另一个帐户代币
* @param _from address 发送代币的地址
* @param _to address 接受代币的地址
* @param _value uint256 接受代币的数量
*/
function _transfer(address _from, address _to, uint256 _value) internal {
//避免转帐的地址是0x0
require(_to != 0x0);
//检查发送者是否拥有足够余额
require(balanceOf[_from] >= _value);
//检查是否溢出
require(balanceOf[_to] + _value > balanceOf[_to]);
//保存数据用于后面的判断
uint previousBalances = balanceOf[_from] + balanceOf[_to];
//从发送者减掉发送额
balanceOf[_from] -= _value;
//给接收者加上相同的量
balanceOf[_to] += _value;
//判断买、卖双方的数据是否和转换前一致
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* 从主帐户合约调用者发送给别人代币
* @param _to address 接受代币的地址
* @param _value uint256 接受代币的数量
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
}
改善代币
实际使用过程中,交易的过程,需要通知到客户端,并且记录到区块中,我们可以使用event事件来指定,如下代码进行声明:
//在区块链上创建一个事件,用以通知客户端
event Transfer(address indexed from, address indexed to, uint256 value);
设置一些代币的基本信息
/* 公共变量 */
string public name; //代币名称
string public symbol; //代币符号比如'$'
uint8 public decimals = 18; //代币单位,展示的小数点后面多少个0,和以太币一样后面是是18个0
uint256 public totalSupply; //代币总量
某些特定的场景中,不允许某个帐户花费超过指定的上限,避免大额支出,我们可以添加一个 approve
方法,来设置一个允许支出最大金额的列表。
mapping (address => mapping (address => uint256)) public allowance;
/**
* 设置帐户允许支付的最大金额
*
* 一般在智能合约的时候,避免支付过多,造成风险
*
* @param _spender 帐户地址
* @param _value 金额
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
同样在 solidity 中,合约之间也可以相互调用,我们可以增加一个 approveAndCall 方法,用于在设置帐户最大支出金额后,可以做一些其他操作。
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
/**
* 设置帐户允许支付的最大金额
*
* 一般在智能合约的时候,避免支付过多,造成风险
*
* @param _spender 帐户地址
* @param _value 金额
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* 设置帐户允许支付的最大金额
*
* 一般在智能合约的时候,避免支付过多,造成风险,加入时间参数,可以在 tokenRecipient 中做其他操作
*
* @param _spender 帐户地址
* @param _value 金额
* @param _extraData 操作的时间
*/
function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
if (approve(_spender, _value)) {
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
}
我们可以增加一个 burn 方法,用于管理员减去指定帐户的指定金额。进行该方法操作时,通知客户端记录到区块链中。
//减去用户余额事件
event Burn(address indexed from, uint256 value);
/**
* 减少代币调用者的余额
*
* 操作以后是不可逆的
*
* @param _value 要删除的数量
*/
function burn(uint256 _value) public returns (bool success) {
//检查帐户余额是否大于要减去的值
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
//给指定帐户减去余额
balanceOf[msg.sender] -= _value;
//代币问题做相应扣除
totalSupply -= _value;
Burn(msg.sender, _value);
return true;
}
/**
* 删除帐户的余额(含其他帐户)
*
* 删除以后是不可逆的
*
* @param _from 要操作的帐户地址
* @param _value 要减去的数量
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
//检查帐户余额是否大于要减去的值
require(balanceOf[_from] >= _value);
//检查 其他帐户 的余额是否够使用
require(_value <= allowance[_from][msg.sender]);
//减掉代币
balanceOf[_from] -= _value;
allowance[_from][msg.sender] -= _value;
//更新总量
totalSupply -= _value;
Burn(_from, _value);
return true;
}
完整的代码如下:
pragma solidity ^0.4.18;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
/**
* @title 基础版的代币合约
*/
contract token {
/* 公共变量 */
string public name; //代币名称
string public symbol; //代币符号比如'$'
uint8 public decimals = 18; //代币单位,展示的小数点后面多少个0,和以太币一样后面是是18个0
uint256 public totalSupply; //代币总量
/*记录所有余额的映射*/
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
/* 在区块链上创建一个事件,用以通知客户端*/
event Transfer(address indexed from, address indexed to, uint256 value); //转帐通知事件
event Burn(address indexed from, uint256 value); //减去用户余额事件
/* 初始化合约,并且把初始的所有代币都给这合约的创建者
* @param initialSupply 代币的总数
* @param tokenName 代币名称
* @param tokenSymbol 代币符号
*/
function token(uint256 initialSupply, string tokenName, string tokenSymbol) {
//初始化总量
totalSupply = initialSupply * 10 ** uint256(decimals); //以太币是10^18,后面18个0,所以默认decimals是18
//给指定帐户初始化代币总量,初始化用于奖励合约创建者
balanceOf[msg.sender] = totalSupply;
name = tokenName;
symbol = tokenSymbol;
}
/**
* 私有方法从一个帐户发送给另一个帐户代币
* @param _from address 发送代币的地址
* @param _to address 接受代币的地址
* @param _value uint256 接受代币的数量
*/
function _transfer(address _from, address _to, uint256 _value) internal {
//避免转帐的地址是0x0
require(_to != 0x0);
//检查发送者是否拥有足够余额
require(balanceOf[_from] >= _value);
//检查是否溢出
require(balanceOf[_to] + _value > balanceOf[_to]);
//保存数据用于后面的判断
uint previousBalances = balanceOf[_from] + balanceOf[_to];
//从发送者减掉发送额
balanceOf[_from] -= _value;
//给接收者加上相同的量
balanceOf[_to] += _value;
//通知任何监听该交易的客户端
Transfer(_from, _to, _value);
//判断买、卖双方的数据是否和转换前一致
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* 从主帐户合约调用者发送给别人代币
* @param _to address 接受代币的地址
* @param _value uint256 接受代币的数量
*/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
/**
* 从某个指定的帐户中,向另一个帐户发送代币
*
* 调用过程,会检查设置的允许最大交易额
*
* @param _from address 发送者地址
* @param _to address 接受者地址
* @param _value uint256 要转移的代币数量
* @return success 是否交易成功
*/
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
//检查发送者是否拥有足够余额
require(_value <= allowance[_from][msg.sender]); // Check allowance
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
/**
* 设置帐户允许支付的最大金额
*
* 一般在智能合约的时候,避免支付过多,造成风险
*
* @param _spender 帐户地址
* @param _value 金额
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* 设置帐户允许支付的最大金额
*
* 一般在智能合约的时候,避免支付过多,造成风险,加入时间参数,可以在 tokenRecipient 中做其他操作
*
* @param _spender 帐户地址
* @param _value 金额
* @param _extraData 操作的时间
*/
function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
if (approve(_spender, _value)) {
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
}
/**
* 减少代币调用者的余额
*
* 操作以后是不可逆的
*
* @param _value 要删除的数量
*/
function burn(uint256 _value) public returns (bool success) {
//检查帐户余额是否大于要减去的值
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
//给指定帐户减去余额
balanceOf[msg.sender] -= _value;
//代币问题做相应扣除
totalSupply -= _value;
Burn(msg.sender, _value);
return true;
}
/**
* 删除帐户的余额(含其他帐户)
*
* 删除以后是不可逆的
*
* @param _from 要操作的帐户地址
* @param _value 要减去的数量
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
//检查帐户余额是否大于要减去的值
require(balanceOf[_from] >= _value);
//检查 其他帐户 的余额是否够使用
require(_value <= allowance[_from][msg.sender]);
//减掉代币
balanceOf[_from] -= _value;
allowance[_from][msg.sender] -= _value;
//更新总量
totalSupply -= _value;
Burn(_from, _value);
return true;
}
}
如何部署
下载 Ethereum Wallet 钱包,打开界面,点击CONTRACTS ==> DEPLOY NEW CONTRACT, 选择部署该合约的账户地址,复制源码,粘贴到 SOLIDITY CONTRACT SOURCE CODE. 在右侧选择token
, Initial Supply
输入初始金额,Token name
输入我们的代币名称 ,Token symbol
输入代币符号, 然后点击 部署,输入部署帐户的密码。
等待其他区块认证后,你就拥有自己的代币了。
参考文档:
代币标准
Create your own crypto-currency with ethereum
一步步教你创建自己的数字货币(代币)进行ICO
Go-Ethereum 1.7.2 结合 Mist 0.9.2 实现代币智能合约的实例