【傳智播客.黑馬程序員訓(xùn)練營成都中心】
一、前言
??
1.1 預(yù)備知識
(1) 以太坊相關(guān)概念 (2) 熟悉智能合約代碼編寫與部署 (3) 以太坊私鏈環(huán)境搭建 (4) Mist錢包的安裝與使用
1.2 環(huán)境介紹
(1) win10 (2) Mist錢包 (3) geth ---(version:1.7.2) ---搭建以太坊私鏈
二域那、以太坊代幣(Token)
??
2.1 介紹
如果不那么追求精確的定義播掷,代幣就是數(shù)字貨幣审编,比特幣、以太幣都可以定義代幣歧匈。而以太坊代幣是基于以太坊智能合約編寫出來并發(fā)行到以太坊虛擬機上的合約數(shù)字貨幣垒酬,代幣可以代表任何可以交易的東西,如:積分、財產(chǎn)勘究、證書等等矮湘。利用以太坊智能合約能夠非常輕松實現(xiàn)自己的代幣,因為以太坊提供了一個開發(fā)代幣的ERC20標準口糕。
2.2 標準代幣ERC20 Token
ERC20和代幣經(jīng)常一同出現(xiàn)缅阳, ERC20是以太坊定義的一個代幣標準。 要求我們在實現(xiàn)代幣的時必須要遵守的協(xié)議景描,如指定代幣名稱十办、總量、實現(xiàn)代幣交易函數(shù)等伏伯,只有支持了協(xié)議才能被以太坊錢包支持橘洞,這樣你的代幣才具有交易和流通的能力。目前ERC20存在一些無法解決的問題说搅,但是新的標準ERC223以及出世炸枣,但市面上大多數(shù)以太坊代幣都采用ERC20標準,所以本文也使用ERC20標準實現(xiàn)自己的代碼弄唧,最后會對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使用的小數(shù)點后幾位麸俘。比如如果設(shè)置為3辩稽,就是支持0.001表示。 totalSupply() 返回token的總供應(yīng)量 balanceOf() 返回某個地址(賬戶)的賬戶余額 transfer() 從代幣合約的調(diào)用者地址上轉(zhuǎn)移value的數(shù)量token到的地址to从媚,并且必須觸發(fā)Transfer事件逞泄。 transferFrom() transferFrom方法用于允許合同代理某人轉(zhuǎn)移token。前提是被代理人調(diào)用approve方法允許代理人設(shè)置操作自己多少token allowance() 被代理人設(shè)置代理人操作自己的多少token approve() 更改被代理人設(shè)置代理人操作自己的多少token拜效,并且必須觸發(fā)Approval事件 Transfer事件 代幣被轉(zhuǎn)移時觸發(fā)該事件喷众,記錄轉(zhuǎn)賬日志 Approval事件 調(diào)用approve方法時觸發(fā),記錄授權(quán)日志
注意:以上ERC20標準代幣接口方法只是標準紧憾,并不是所有方法都需要實現(xiàn)到千,當然我們還可以根據(jù)自己的業(yè)務(wù)增強自己的代幣,比如實現(xiàn)代幣管理稻励、代幣增發(fā)父阻、代幣兌換愈涩、資產(chǎn)凍結(jié)望抽、Gas自動補充等功能的高級代幣
3.2 代幣合約實現(xiàn)
<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 可以有的小數(shù)點個數(shù)加矛,最小的代幣單位。18 是建議的默認值
uint256 public totalSupply;//token的總數(shù)
?
// 用mapping保存每個地址對應(yīng)的余額
mapping (address => uint256) public balanceOf;
// 存儲對賬號的控制
mapping (address => mapping (address => uint256)) public allowance;
?
// 事件煤篙,用來通知客戶端交易發(fā)生
event Transfer(address indexed from, address indexed to, uint256 value);
?
// 事件斟览,用來通知客戶端代幣被消費
event Burn(address indexed from, uint256 value);
?
/**
- 初始化構(gòu)造
/
function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {
totalSupply = initialSupply * 10 ** uint256(decimals); // 供應(yīng)的份額,份額跟最小的代幣單位有關(guān)辑奈,份額 = 幣數(shù) * 10 ** decimals苛茂。
balanceOf[msg.sender] = totalSupply; // 創(chuàng)建者擁有所有的代幣
name = tokenName; // 代幣名稱
symbol = tokenSymbol; // 代幣符號
}
?
/* - 代幣交易轉(zhuǎn)移的內(nèi)部實現(xiàn)
/
function _transfer(address _from, address _to, uint _value) internal {
// 確保目標地址不為0x0,因為0x0地址代表銷毀
require(_to != 0x0);
// 檢查發(fā)送者余額
require(balanceOf[_from] >= _value);
// 確保轉(zhuǎn)移為正數(shù)個
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);
}
?
/* - 代幣交易轉(zhuǎn)移
- 從自己(創(chuàng)建交易者)賬號發(fā)送
_value
個代幣到_to
賬號 - @param _to 接收者地址
- @param _value 轉(zhuǎn)移數(shù)額
/
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
?
/* - 賬號之間代幣交易轉(zhuǎn)移
- @param _from 發(fā)送者地址
- @param _to 接收者地址
- @param _value 轉(zhuǎn)移數(shù)額
/
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;
}
?
/* - 設(shè)置某個地址(合約)可以創(chuàng)建交易者名義花費的代幣數(shù)。
- 允許發(fā)送者
_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;
}
?
/* - 設(shè)置允許一個地址(合約)以我(創(chuàng)建交易者)的名義可最多花費的代幣數(shù)稍计。
- @param _spender 被授權(quán)的地址(合約)
- @param _value 最大可花費代幣數(shù)
- @param _extraData 發(fā)送給合約的附加數(shù)據(jù)
/
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;
}
}
?
/* - 銷毀我(創(chuàng)建交易者)賬戶中指定個代幣
/
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私有環(huán)境部署
以太坊私鏈的搭建和mist錢包的安裝與使用請自行學習
4.1 啟動私鏈
4.2 新建賬號
4.3 挖礦
在控制臺輸入:miner.start() 即可開始挖礦挖礦:挖礦可以獲得私有鏈的以太幣,因為我們需要部署我們上面寫的代幣合約就需要消費gas臣嚣,gas就是相應(yīng)的以太幣净刮。挖礦默認采用第一個賬號進行,如下所示
查看余額
4.4 打開Mist錢包并連接到私鏈
4.5 合約部署
點擊【CONTRACTS】進入合約部署界面五硅则、測試Token
??
點擊【Main account】進入該賬號界面
點擊【Transfer Ether & Tokens】進入轉(zhuǎn)賬界面
在最下面點擊【send】并在彈出框輸入密碼,待挖礦成功之后如下界面
當然你還可以在token合約主頁面進行相關(guān)方法的測試淹父,方法的測試有讀者自行完成!
六、ERC20標準代幣的問題
ERC20有兩種轉(zhuǎn)賬方式怎虫,一種是收件方為一份合同暑认,這種情況下用戶必須使用approve+transferFrom的功能來進行代幣轉(zhuǎn)移;而另一種則是收件方為合同外賬戶(例如大审,錢包地址)的情況蘸际,用戶需將代幣通過transfer功能轉(zhuǎn)出 。如果用戶使用transfer給合同地址轉(zhuǎn)賬將導(dǎo)致代幣丟失饥努,據(jù)了解捡鱼,以太坊生態(tài)中的Golem代幣,至今仍有93644.51美元的代幣因投資者的無意操作而流失到合同地址內(nèi)酷愧,造成這些代幣的永久性丟失驾诈。因此ERC223就此誕生,相關(guān)介紹請讀者自行學習溶浴!
七乍迄、結(jié)尾
本文介紹了基于以太坊私鏈開發(fā)ERC20標準代幣合約,并且使用Mist錢包部署士败,以及簡單的轉(zhuǎn)賬測試闯两。 區(qū)塊鏈技術(shù)是目前非常熱門的褥伴,區(qū)塊鏈技術(shù)暫時還不成熟,但是相信未來區(qū)塊鏈在互聯(lián)網(wǎng)的地位一定不可小覷漾狼,希望本文能夠?qū)ψx者有幫助重慢。相互學習相互探討不斷專研