ERC20是以太坊网络上发行代币(Token)的一个标准协议接口,协议的github具体描述位于https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md。一个标准的协议促使了代币可以在不同的应用中得到使用,如钱包和去中心化交易所。目前有很多实现该标准协议的Token Examples,我们将使用https://github.com/ConsenSys/Tokens提供的例子进行演示操作。我们先来看下这个实现库中的主要合约文件:
ERC20 Token协议实现
- Token.sol ERC 20协议的抽象定义
//Abstract contract for the full ERC 20 Token standard
// https://github.com/ethereum/EIPs/issues/20
pragma solidity ^0.4.8;
contract Token {
/// token总量,默认会为public变量生成一个getter函数接口,名称为totalSupply().
uint256 public totalSupply;
/// 获取账户_owner拥有token的数量
function balanceOf(address _owner) constant returns (uint256 balance);
//从消息发送者账户中往_to账户转数量为_value的token
function transfer(address _to, uint256 _value) returns (bool success);
//从账户_from中往账户_to转数量为_value的token,与approve方法配合使用
function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
//消息发送账户设置账户_spender能从发送账户中转出数量为_value的token
function approve(address _spender, uint256 _value) returns (bool success);
//获取账户_spender可以从账户_owner中转出token的数量
function allowance(address _owner, address _spender) constant returns (uint256 remaining);
//发生转账时必须要触发的事件
event Transfer(address indexed _from, address indexed _to, uint256 _value);
//当函数approve(address _spender, uint256 _value)成功执行时必须触发的事件
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
- StandardToken.sol ERC20协议的标准实现
/*You should inherit from StandardToken or, for a token like you would want to
deploy in something like Mist, see DAToken.sol.
(This implements ONLY the standard functions and NOTHING else.
If you deploy this, you won't have anything useful.)
Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20.*/
pragma solidity ^0.4.8;
import "./Token.sol";
contract StandardToken is Token {
function transfer(address _to, uint256 _value) returns (bool success) {
//默认totalSupply 不会超过最大值 (2^256 - 1).
//如果随着时间的推移将会有新的token生成,则可以用下面这句避免溢出的异常
//require(balances[msg.sender] >= _value && balances[_to] + _value >balances[_to]);
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;//从消息发送者账户中减去token数量_value
balances[_to] += _value;//往接收账户增加token数量_value
Transfer(msg.sender, _to, _value);//触发转币交易事件
return true;
}
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
//require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]);
require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value);
balances[_to] += _value;//接收账户增加token数量_value
balances[_from] -= _value; ;//支出账户_from减去token数量_value
allowed[_from][msg.sender] -= _value;//消息发送者可以从账户_from中转出的数量减少_value
Transfer(_from, _to, _value);//触发转币交易事件
return true;
}
//查询余额
function balanceOf(address _owner) constant returns (uint256 balance) {
return balances[_owner];
}
//授权账户_spender可以从消息发送者账户转出数量为_value的token
function approve(address _spender, uint256 _value) returns (bool success)
{
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
return allowed[_owner][_spender];//允许_spender从_owner中转出的token数
}
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
}
- DAToken.sol 具体的token实现
import "./StandardToken.sol";
pragma solidity ^0.4.8;
contract DAToken is StandardToken {
/* Public variables of the token */
string public name; //名称: eg Davie
uint8 public decimals; //最多的小数位数How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether.
string public symbol; //token简称: eg DAC
string public version = 'H0.1'; //版本
function DAToken(uint256 _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) {
balances[msg.sender] = _initialAmount; // 初始token数量给予消息发送者
totalSupply = _initialAmount; // 设置初始总量
name = _tokenName; // token名称
decimals = _decimalUnits; // 小数位数
symbol = _tokenSymbol; // token简称
}
/* 同意转出并调用接收合约(根据自己需求实现) */
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
//call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this.
//receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
//it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
require(_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData));
return true;
}
}
Token合约发布
我们将使用MetaMask和Remix(https://ethereum.github.io/browser-solidity)并选择在Ropsten测试网络进行Token合约发布。
- 切换MetaMask至Ropsten网络
2.将所有相关代码copy至Remix
或者从这里拷贝https://ropsten.etherscan.io/address/0xaa86b7bc852907e5649c958411465e878c954b67#code
在右侧上方红框标记处Environment选择Injected Web3, 下方的Account列表将会列出MetaMask中的账户信息。然后在右侧下方红框中依次填入初始化DAToken需要的参数,初始token数量(100000000)、token的名称描述(My Test Token)、decimal数(8)、token简称(MTT)。
- Token合约发布
“Create”中填入创建的Token参数,
第一个参数是Token的数量;
第二个参数是Token的全称;
第三个参数是Token的精度,即Token最小为小数点后几位;
第四个参数是Token的符号;
例如:100000000,"zhongxh's test token",8,"ZTT"
上述例子代表创建的Token的全称是"zhongxh's test token",符号是“ZTT”, ZTT的最大精度为小数点后8位,即最小是0.000000001ZTT,总共有1亿份,那么总共有100000000 * 10^(-8)ZTT,即1ZTT
点击合约DAToken处的create按钮,将弹出如下画面:
点击SUBMIT将该交易请求发布至Rosten网络中,交易变成功打包后。可以查询到本次操作的交易id为0x1aabf45bed257dd7521215f21744d0ad64b4275436bc2c8148fb44c2489efb84, 合约地址为0xaa86b7bc852907e5649c958411465e878c954b67,交易截图如下:
4.合约源代码上传
接下来我们看下如何在https://ropsten.etherscan.io上传合约的源代码,首先打开合约0xaa86b7bc852907e5649c958411465e878c954b67的详情页,并切换至Contrant Code标签中,如下图。
在Contract code 标签页中点击“Verify And Publish”链接,然后填入下图红框各项内容。
- Contract address为上面创建的合约地址;
- Contract name为在Remix中选择创建的合约名称DAToken;
- Compiler版本需选择在Remix进行合约创建时选择的编译器版本一致;
- 是否优化Optimization也需要与Remix发布合约时保持一致;
- 在“Enter the Solidity Contract Code below”中填入之前在Remix中的solidity合约代码;
- 在“Constructor Arguments ABI-encoded”中填入构造函数参数(100000000,“My Test Token”,8,“MTT”)的ABI编码,这个会自动填好。
填好上述数据后,点击Verify and Publish,如果验证通过了就会出现如下页面:
最后我们切换到合约的详情页,点击Contract Source标签,就能看到上传的合约源代码了,如下图:
如果遇到错误"Unable to Verify Contract at this point time",请确认在提交合约源代码时,选择的编译器版本与是否优化选项,与你在Remix中的选择是否一致。
- 添加发布的Token到钱包
我们可以在MetaMask中添加上面生成的Token至MetaMask钱包中,如下图。
填入Token地址
点击Add后可以在Tokens标签页中看到上面发布的Token MTT. (因为我们在创建DAToken时,输入的初始量为1000000,而decimal值为8,这个初始量值的单位是token的最低单位,故总量为0.01MTT,大家可以根据自己的情况作出相应调整。)
接下来就可以在Ropsten测试网络上进行Token的转账以及相关的测试操作了,如果测试没问题就按照同样的流程把合约部署至以太坊主网络中发行真实的token了。
因为Metamask不支持toekn的发送,我们切换到MyEtherWallet钱包。
需要注意的是,MyEtherWallet也需要切换到Rostpen网络
接下来切换到“发送以太币/发送代币”
在这里我选择用私钥的方式解锁钱包,私钥可以从Metamask获得
点击Metamask右上角的“...”,然后点击"Export Private Key ",输入密码,即可获得私钥
将私钥拷到MyEtherWallet,就可以解锁你的钱包了。
点击右下角的“Load Token Balance”
右下角显示的正是刚刚创建的Token,也就是ZTT,余额为1,这表明我的ERC20 Token发布成功了!
给另一个账户转token
获取第二个账户地址
切换到account2
复制账户地址
填入转账地址和金额,token
注意这里选择我们刚才发布的LOVE token
点击生成交易,然后发送交易。
稍后回到Metamask查看
可以看到账户 account2收到了 100个LOVE