1. 文章摘要
【本文目標】
通過逐步的指導(dǎo)和截圖舉證妻坝,一步步帶領(lǐng)一個技術(shù)小白完成一個數(shù)字貨幣(通證伸眶,代幣,TOKEN)的發(fā)布演示和上線交易刽宪。
【環(huán)境前置條件】
參考《第六課 技術(shù)小白如何開發(fā)一個DAPP區(qū)塊鏈應(yīng)用(以寵物商店為例)》厘贼,已在本地WIDOWS環(huán)境完成MetaMask輕錢包客戶端的安裝和配置;作者建議最好遵循從頭開始的課程學(xué)習(xí)順序纠屋。不過如果你想半途插入實操學(xué)習(xí)涂臣,問題也不大,遇到障礙時反向找對應(yīng)文章的指導(dǎo)內(nèi)容即可完成售担。
【技術(shù)收獲】
從本實踐中赁遗,你可以學(xué)習(xí)到:
ERC20 Token的定義和實踐
使用Remix Solidity IDE編寫智能合約和編譯調(diào)試
使用MetaMask完成錢包賬戶查看
使用網(wǎng)頁錢包完成代幣交易演示
【實操課程列表】
第一課 如何在WINDOWS環(huán)境下搭建以太坊開發(fā)環(huán)境
第二課 如何實現(xiàn)以太坊最簡智能合約“Hello World”的運行
第四課 以太坊開發(fā)框架Truffle從入門到實戰(zhàn)
第六課 技術(shù)小白如何開發(fā)一個DAPP區(qū)塊鏈應(yīng)用(以寵物商店為例)
第七課 技術(shù)小白如何在45分鐘內(nèi)發(fā)行通證(TOKEN)并上線交易
第八課 如何調(diào)試以太坊官網(wǎng)的智能合約眾籌案例
第九課 如何在Remix環(huán)境下進行Solidity代碼單步調(diào)試
第十課 Solidity語言編輯器REMIX指導(dǎo)大全
【說明】未列出的課程為知識普及的非實操類課程,所有區(qū)塊鏈文章參考“區(qū)塊鏈入口”專欄族铆。
2. ERC20 Token定義和接口說明
定義
ERC20合約是在2015年11月在EIP上提出的一個合約標準岩四,代幣定義的一個標準。
Token代表數(shù)字資產(chǎn)哥攘,具有價值剖煌,但是并不是都符合特定的規(guī)范。
基于ERC20的貨幣更容易互換逝淹,并且能夠在Dapps上相同的工作耕姊。
新的標準可以讓token更兼容,允許其他功能栅葡,包括投票標記化茉兰。操作更像一個投票操作,Token的持有人可以完全控制資產(chǎn)欣簇,遵守ERC20的token可以跟蹤任何人在任何時間擁有多少token规脸∨髟迹基于eth合約的子貨幣,所以容易實施莫鸭。
ERC20 Token接口說明
方法
注意:調(diào)用者必須處理返回false
的returns (bool success)
.調(diào)用者絕對不能假設(shè)返回false
的情況不存在闹丐。
name
返回這個令牌的名字,比如"MyToken"
.
可選 - 這種方法可以用來提高可用性被因,但接口和其他契約不能指望這些值存在卿拴。
function name() constant returns (string name)
symbol
返回令牌的符號,比如HIX
.
可選 - 這種方法可以用來提高可用性氏身,但接口和其他契約不能指望這些值存在巍棱。
function symbol() constant returns (string symbol)
decimals
返回token使用的小數(shù)點后幾位, 比如 8
,表示分配token數(shù)量為100000000
可選 - 這種方法可以用來提高可用性蛋欣,但接口和其他契約不能指望這些值存在航徙。
function decimals() constant returns (uint8 decimals)
totalSupply
返回token的總供應(yīng)量。
function totalSupply() constant returns (uint256 totalSupply)
balanceOf
返回地址是_owner
的賬戶的賬戶余額陷虎。
function balanceOf(address _owner) constant returns (uint256 balance)
transfer
轉(zhuǎn)移_value
的token數(shù)量到的地址_to
到踏,并且必須觸發(fā)Transfer
事件。 如果_from
帳戶余額沒有足夠的令牌來支出尚猿,該函數(shù)應(yīng)該被throw
窝稿。
創(chuàng)建新令牌的令牌合同應(yīng)該在創(chuàng)建令牌時將_from
地址設(shè)置為0x0
觸發(fā)傳輸事件。
注意 0值的傳輸必須被視為正常傳輸并觸發(fā)傳輸事件凿掂。
function transfer(address _to, uint256 _value) returns (bool success)
transferFrom
從地址_from
發(fā)送數(shù)量為_value
的token到地址_to
,必須觸發(fā)Transfer
事件伴榔。
transferFrom方法用于提取工作流,允許合同代您轉(zhuǎn)移token庄萎。這可以用于例如允許合約代您轉(zhuǎn)讓代幣和/或以子貨幣收取費用踪少。除了_from帳戶已經(jīng)通過某種機制故意地授權(quán)消息的發(fā)送者之外,該函數(shù)應(yīng)該throw糠涛。
注意 0值的傳輸必須被視為正常傳輸并觸發(fā)傳輸事件援奢。
function transferFrom(address _from, address _to, uint256 _value) returns (bool success)
approve
允許_spender
多次取回您的帳戶,最高達_value
金額忍捡。 如果再次調(diào)用此函數(shù)集漾,它將以_value
覆蓋當前的余量。
注意:為了阻止向量攻擊砸脊,客戶端需要確認以這樣的方式創(chuàng)建用戶接口具篇,即將它們設(shè)置為0,然后將其設(shè)置為同一個花費者的另一個值凌埂。雖然合同本身不應(yīng)該強制執(zhí)行栽连,允許向后兼容以前部署的合同兼容性
function approve(address _spender, uint256 _value) returns (bool success)
allowance
返回_spender
仍然被允許從_owner
提取的金額。
function allowance(address _owner, address _spender) constant returns (uint256 remaining)
Events
Transfer
當token被轉(zhuǎn)移(包括0值),必須被觸發(fā)秒紧。
event Transfer(address indexed _from, address indexed _to, uint256 _value)
Approval
當任何成功調(diào)用approve(address _spender, uint256 _value)
后,必須被觸發(fā)挨下。
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
[官網(wǎng)接口說明點擊查看],(https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md)
接口文件ERC20Interface.sol
如下:
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
// 0.0000000000000000001 個代幣
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 approve(address spender, uint tokens) public returns (bool success);
function transfer(address to, 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);
}
3. TOKEN合約代碼
合約文件TokenERC20.sol
如下:
pragma solidity ^0.4.16;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
contract TokenERC20 {
string public name;
string public symbol;
uint8 public decimals = 18; // decimals 可以有的小數(shù)點個數(shù)熔恢,最小的代幣單位。18 是建議的默認值
uint256 public totalSupply;
// 用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è)置某個地址(合約)可以交易者名義花費的代幣數(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è)置允許一個地址(合約)以交易者名義可最多花費的代幣數(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;
}
}
函數(shù)的功能參考函數(shù)的說明描述和代碼自解釋督勺。
4. 合約編譯部署和發(fā)布
MetaMask錢包聯(lián)網(wǎng)
【前置條件】作者假設(shè)學(xué)習(xí)者已完成MetaMask的安裝和配置。還沒有完成的斤贰,參考《第六課 技術(shù)小白如何開發(fā)一個DAPP區(qū)塊鏈應(yīng)用(以寵物商店為例)》的章節(jié)“5. 安裝 MetaMask和配置區(qū)塊鏈網(wǎng)絡(luò)”智哀,在本地WIDOWS環(huán)境完成MetaMask輕錢包客戶端的安裝和配置。
打開MetaMask錢包荧恍,點擊左上角的“Ropsten Test Network” 連接成功瓷叫。查看存量的賬號Account 1,其中ETH余額顯示為0送巡。
點擊“Copy Address to Clipboard”,記錄Account 1錢包地址為
點擊MetaMask右上角環(huán)形頭像的菜單“Create Account”, 創(chuàng)建一個新錢包賬號Account 8摹菠,0xD1F7922e8b78cBEB182250753ade8379d1E09949
記錄Account 8的錢包地址為
0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363
點擊Account 8的"BUY"按鈕,可以從“Ropsten Test Network” 免費獲取一些測試ETH辨嗽。
點擊按鈕“ROPSTEN TEST FAUCET”可以看到贈送頁面
點擊綠色按鈕淮腾,等待10秒以上圆凰,成功的話每次可以獲取1個測試ETH。
【故障排查】
作者操作時挑童,出現(xiàn)錯誤累铅。從提示看,是由于用戶交易拒絕站叼。等10秒后再點擊該綠色按鈕則未有錯誤提示了娃兽。原因不明,可能是操作頻繁導(dǎo)致尽楔。
多次點擊投储,偶爾出錯,小編一共從這個測試網(wǎng)站獲取了5個測試ETH用于作為發(fā)幣的GAS燃料阔馋。
Remix Solidity IDE調(diào)試環(huán)境介紹
1玛荞,代碼編寫和編譯
我們以第二課的“Hello World”智能合約為例,參考下圖可完成編譯和語法錯誤發(fā)現(xiàn)呕寝。
2勋眯,合約創(chuàng)建
參考附圖描述,在配置號MetaMask賬號和網(wǎng)絡(luò)連接的情況下壁涎,確保賬戶有虛擬ETH用于合約創(chuàng)建花銷凡恍。最后點擊“Create”按鈕可以完成合約創(chuàng)建。點擊輸出區(qū)的網(wǎng)頁鏈接可以查看合約的詳細情況怔球。
3嚼酝, 合約執(zhí)行
參考附件路徑圖,點擊RUN按鈕可以執(zhí)行一次合約竟坛。點擊下方的“Say”按鈕闽巩,可以看到合約輸出。在調(diào)試輸出窗口可以看到“call to hello.say”的運行提示担汤。
【總結(jié)】所以說涎跨,沒有Ubuntu+Ganache等,直接在WINDOWS環(huán)境崭歧,也可以使用Remix+MetaMask+Ropsten Test Network組合完成一套完整的以太坊測試環(huán)境隅很。
更詳細的REMIX幫助文檔參考第十課 Solidity語言編輯器REMIX指導(dǎo)大全
編譯ERC20智能合約
CHROME瀏覽器打開Remix Solidity IDE環(huán)境,打開之前編寫的“TokenERC20.sol”智能合約率碾,然后點擊右側(cè)的“start to compile”按鈕叔营。可以看到所宰,除了一些Warning提示外绒尊,智能合約編譯成功。
運行ERC20智能合約
切換到"RUN"頁面仔粥,Environment選擇“Injected Web3”, Account自動更新為MetaMask的Account 8賬號婴谱⌒返“Create”按鈕前按照發(fā)幣數(shù)量(本次發(fā)1個億),代幣名稱谭羔,代幣符號填寫為100000000,"ColorBay","CB"华糖。
點擊"Create"按鈕,會彈出一個交易確認框口糕,設(shè)置合理的Gas Price缅阳,但要確保Max Transaction Fee不會超過Account 8的ETH總數(shù),點“SUBMIT”按鈕景描。
部署成功的話,Account8 會產(chǎn)生一條交易記錄秀撇,顯示狀態(tài)為“Contract Published”,表示部署成功了超棺。
【說明】有時點擊賬號,會出現(xiàn)“Retry with a higher gas price here”的提示呵燕,一般情況下再等待10秒看看能否交易成功棠绘。萬一還是不成的話,可考慮該Gas Price大點再扭。
MetaMask加載TOKEN
點擊Account 8的交易記錄氧苍,可以跳轉(zhuǎn)智能合約部署信息顯示頁面:
獲取智能合約地址為0x5eeec41dc08d7caece17c4a349635934637036f1,點擊可查看該交易詳情泛范。
點擊MetaMask的Account 8賬號的TOKENS頁面的“ADD TOKEN”按鈕让虐,把代幣智能合約地址Token Contract Address為0x5eeec41dc08d7caece17c4a349635934637036f1,代幣標識符Token Symbol為CB罢荡,則可以創(chuàng)建你的代幣了赡突。
代幣創(chuàng)建成功,一共有1億個CB幣了区赵。此時惭缰,點擊100000000的代幣位置,跳轉(zhuǎn)到代幣查詢頁面, 可以在etherscan網(wǎng)站看到賬戶的持幣數(shù)量了笼才。
截止作者發(fā)稿時漱受,以太坊的價格為2559元/個,而你有1億個CB幣骡送,是不是快要走上人生巔峰了呢昂羡?哈哈,做個夢而已各谚。真正的區(qū)塊鏈應(yīng)用紧憾,要能有生態(tài),能為人類社會創(chuàng)造價值昌渤,而不是講故事赴穗,割韭菜!
5. 代幣交易
由于MetaMask插件沒有提供代幣交易功能,同時考慮到很多人并沒有以太坊錢包或是被以太坊錢包網(wǎng)絡(luò)同步問題折磨般眉,今天我用網(wǎng)頁錢包來講解代幣交易了赵。
1. 進入網(wǎng)頁錢包地址
第一次進入有一些安全提示需要用戶確認。
2. 進入之后甸赃,按照下圖進行設(shè)置
3柿汛,增加自定義代幣
填寫地址Token Contract 為0x5eeec41dc08d7caece17c4a349635934637036f1和其他信息,點擊保存按鈕埠对。
配置成功的界面如下:
【說明】如果不成功络断,則提示為“Not a valid ERC-20 token CB”,則可能是代幣信息填寫不正確或者右上角的網(wǎng)絡(luò)選擇錯誤,沒有選擇“Network Ropston(infura.com)”選項引起项玛。
4.轉(zhuǎn)賬給Account 1
填寫Account 1的地址“0xD1F7922e8b78cBEB182250753ade8379d1E09949”到“發(fā)送至地址”輸入框貌笨,
查看賬戶余額襟沮,Account 8減少800萬個CB幣锥惋,而Account 1則增加了800萬個CB幣。
轉(zhuǎn)賬交易成功了开伏!
作為一個古典投資人膀跌,用45分鐘完成了TOKEN上線和交易,用4個小時整理了這篇文章固灵。學(xué)習(xí)就是這么簡單捅伤,只要你對新技術(shù)保持饑餓感,高齡開發(fā)轉(zhuǎn)型區(qū)塊鏈也不是難事怎虫!
6. 商用實施
做一個ERC20通證玩玩是比較簡單的暑认。但是如果要做一個需要發(fā)布并廣泛使用的通證,防止出現(xiàn)美鏈那樣60多億的價值一夜置零的安全問題大审,那還是要注意安全性問題蘸际。我們商用發(fā)布的ERC20通證跟上面DEMO的比起來,增加了10來位CTO的同行評審粮彤,取得了鏈安科技的專業(yè)安全審計報告,然后才發(fā)布出來姜骡。如果各位要交流商用技術(shù)导坟,請加群交流。
輝哥和歐陽哥哥在知識星球開通了區(qū)塊鏈入門專欄圈澈,用于存放簡書區(qū)塊鏈入門專欄文章的工程源碼等內(nèi)容惫周,并建立專項微信群用于技術(shù)交流,歡迎加入康栈。