【传智播客.黑马程序员训练营成都中心】
一、前言
1.1 预备知识
(1) 以太坊相关概念 (2) 熟悉智能合约代码编写与部署 (3) 以太坊私链环境搭建 (4) Mist钱包的安装与使用
1.2 环境介绍
(1) win10 (2) Mist钱包 (3) geth ---(version:1.7.2) ---搭建以太坊私链
二、以太坊代币(Token)
2.1 介绍
如果不那么追求精确的定义,代币就是数字货币,比特币、以太币都可以定义代币。而以太坊代币是基于以太坊智能合约编写出来并发行到以太坊虚拟机上的合约数字货币,代币可以代表任何可以交易的东西,如:积分、财产、证书等等。利用以太坊智能合约能够非常轻松实现自己的代币,因为以太坊提供了一个开发代币的ERC20标准。
2.2 标准代币ERC20 Token
ERC20和代币经常一同出现, ERC20是以太坊定义的一个代币标准。 要求我们在实现代币的时必须要遵守的协议,如指定代币名称、总量、实现代币交易函数等,只有支持了协议才能被以太坊钱包支持,这样你的代币才具有交易和流通的能力。目前ERC20存在一些无法解决的问题,但是新的标准ERC223以及出世,但市面上大多数以太坊代币都采用ERC20标准,所以本文也使用ERC20标准实现自己的代码,最后会对ERC20存在的问题给大家稍作解释,那接下来我们就进入正题。
三、代币合约
3.1 ERC20的标准接口
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" contenteditable="false" cid="n19" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; caret-color: rgb(51, 51, 51); color: rgb(51, 51, 51); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; background-position: inherit inherit; background-repeat: inherit inherit;">contract ERC20 {
function name() constant returns (string name)
function symbol() constant returns (string symbol)
function decimals() constant returns (uint8 decimals)
function totalSupply() constant returns (uint totalSupply);
function balanceOf(address _owner) constant returns (uint balance);
function transfer(address _to, uint _value) returns (bool success);
function transferFrom(address _from, address _to, uint _value) returns (bool success);
function approve(address _spender, uint _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint remaining);
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}</pre>
接口代码解释: name() 返回ERC20代币的名字,例如”My test token”。 symbol() 返回代币的简称,例如:MTT,这个也是我们一般在代币交易所看到的名字。 decimals() 返回token使用的小数点后几位。比如如果设置为3,就是支持0.001表示。 totalSupply() 返回token的总供应量 balanceOf() 返回某个地址(账户)的账户余额 transfer() 从代币合约的调用者地址上转移value的数量token到的地址to,并且必须触发Transfer事件。 transferFrom() transferFrom方法用于允许合同代理某人转移token。前提是被代理人调用approve方法允许代理人设置操作自己多少token allowance() 被代理人设置代理人操作自己的多少token approve() 更改被代理人设置代理人操作自己的多少token,并且必须触发Approval事件 Transfer事件 代币被转移时触发该事件,记录转账日志 Approval事件 调用approve方法时触发,记录授权日志
注意:以上ERC20标准代币接口方法只是标准,并不是所有方法都需要实现,当然我们还可以根据自己的业务增强自己的代币,比如实现代币管理、代币增发、代币兑换、资产冻结、Gas自动补充等功能的高级代币
3.2 代币合约实现
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" contenteditable="false" cid="n24" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; caret-color: rgb(51, 51, 51); color: rgb(51, 51, 51); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; background-position: inherit inherit; background-repeat: inherit inherit;">pragma solidity ^0.4.16;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
contract TokenERC20 {
string public name; //token的名字
string public symbol;//token的简称
uint8 public decimals = 18; // decimals 可以有的小数点个数,最小的代币单位。18 是建议的默认值
uint256 public totalSupply;//token的总数
// 用mapping保存每个地址对应的余额
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);
/**
- 初始化构造
/
function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {
totalSupply = initialSupply * 10 ** uint256(decimals); // 供应的份额,份额跟最小的代币单位有关,份额 = 币数 * 10 ** decimals。
balanceOf[msg.sender] = totalSupply; // 创建者拥有所有的代币
name = tokenName; // 代币名称
symbol = tokenSymbol; // 代币符号
}
/* - 代币交易转移的内部实现
/
function _transfer(address _from, address _to, uint _value) internal {
// 确保目标地址不为0x0,因为0x0地址代表销毁
require(_to != 0x0);
// 检查发送者余额
require(balanceOf[_from] >= _value);
// 确保转移为正数个
require(balanceOf[_to] + _value > balanceOf[_to]);
// 以下用来检查交易,
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// Subtract from the sender
balanceOf[_from] -= _value;
// Add the same to the recipient
balanceOf[_to] += _value;
Transfer(_from, _to, _value);
// 用assert来检查代码逻辑。
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/* - 代币交易转移
- 从自己(创建交易者)账号发送
_value
个代币到_to
账号 - @param _to 接收者地址
- @param _value 转移数额
/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
/* - 账号之间代币交易转移
- @param _from 发送者地址
- @param _to 接收者地址
- @param _value 转移数额
/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]); // Check allowance
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
/* - 设置某个地址(合约)可以创建交易者名义花费的代币数。
- 允许发送者
_spender
花费不多于_value
个代币 - @param _spender The address authorized to spend
- @param _value the max amount they can spend
/
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/* - 设置允许一个地址(合约)以我(创建交易者)的名义可最多花费的代币数。
- @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;
}
}
/* - 销毁我(创建交易者)账户中指定个代币
/
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
balanceOf[msg.sender] -= _value; // Subtract from the sender
totalSupply -= _value; // Updates totalSupply
Burn(msg.sender, _value);
return true;
}
/* - 销毁用户账户中指定个代币
- Remove
_value
tokens from the system irreversibly on behalf of_from
. - @param _from the address of the sender
- @param _value the amount of money to burn
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value); // Check if the targeted balance is enough
require(_value <= allowance[_from][msg.sender]); // Check allowance
balanceOf[_from] -= _value; // Subtract from the targeted balance
allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance
totalSupply -= _value; // Update totalSupply
Burn(_from, _value);
return true;
}
}</pre>
四、部署
采用以太坊钱包mist+geth私有环境部署
以太坊私链的搭建和mist钱包的安装与使用请自行学习
4.1 启动私链
image4.2 新建账号
image4.3 挖矿
在控制台输入:miner.start() 即可开始挖矿 image挖矿:挖矿可以获得私有链的以太币,因为我们需要部署我们上面写的代币合约就需要消费gas,gas就是相应的以太币。挖矿默认采用第一个账号进行,如下所示
查看余额
image4.4 打开Mist钱包并连接到私链
image 界面介绍 image4.5 合约部署
点击【CONTRACTS】进入合约部署界面 image 点击【DEPLOY NEW CONTRACT】开始部署新的合约 image 点击最下面的【DEPLOY】弹出如下窗口(一定要在geth控制台执行miner.start(1)开启挖矿) image 点击【WALLETS】进入回到主界面 image五、测试Token
点击【Main account】进入该账号界面
image点击【Transfer Ether & Tokens】进入转账界面
image在最下面点击【send】并在弹出框输入密码,待挖矿成功之后如下界面
image image当然你还可以在token合约主页面进行相关方法的测试,方法的测试有读者自行完成!
六、ERC20标准代币的问题
ERC20有两种转账方式,一种是收件方为一份合同,这种情况下用户必须使用approve+transferFrom的功能来进行代币转移;而另一种则是收件方为合同外账户(例如,钱包地址)的情况,用户需将代币通过transfer功能转出 。如果用户使用transfer给合同地址转账将导致代币丢失,据了解,以太坊生态中的Golem代币,至今仍有93644.51美元的代币因投资者的无意操作而流失到合同地址内,造成这些代币的永久性丢失。因此ERC223就此诞生,相关介绍请读者自行学习!
七、结尾
本文介绍了基于以太坊私链开发ERC20标准代币合约,并且使用Mist钱包部署,以及简单的转账测试。 区块链技术是目前非常热门的,区块链技术暂时还不成熟,但是相信未来区块链在互联网的地位一定不可小觑,希望本文能够对读者有帮助。相互学习相互探讨不断专研
网友评论