ERC20 Token
也許你經常看到ERC20和代幣一同出現, ERC20是以太坊定義的一個代幣標準。
要求我們在實現代幣的時候必須要遵守的協議,如指定代幣名稱、總量、實現代幣交易函數等,只有支持了協議才能被以太坊錢包支持。協議的github具體描述位於https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md。
一個標準的協議促使了代幣可以在不同的應用中得到使用,如錢包和去中心化交易所。
接口定義如下:
pragma solidity ^0.4.19;
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);
}
代幣合約基於ERC20編寫的合約代碼如下:
pragma solidity ^0.4.19;
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 可以有的小數點個數,最小的代幣單位。18 是建議的默認值
uint256 public totalSupply;
// 用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;
}
}
部署合約
我採用Remix在線瀏覽器IDE才進行合約的編譯和部署的,打開Remix頁面樣子如下:
Remix: https://remix.ethereum.org
點擊新建按鈕,將上面編輯的合約複製到IDE中:
複製完成後,IDE的左邊會自動校驗你的合約的編寫是否準確,有錯誤會提示,警告可以忽略。
中間插入MetaMask講解
MetaMask使用
由於我們是在以太坊測試鏈上發行合約,還是需要ether的,只不過可以免費獲得,爲了發行合約,我們需要測試鏈賬戶,下面我們將插入使用MetaMaskchrome插件來鏈接創建賬戶:
安裝好後,在瀏覽器右上角會出現圖標,點擊圖標,一直點擊到下圖,填寫你的密碼,進入後就創建好了一個MetaMask錢包,MetaMask會爲用戶創建12個英文助記詞,一定要保存好這些助記詞,一定要保存好這些助記詞,一定要保存好這些助記詞,在其他錢包導入這個新創建的賬戶的時候有可能需要這些助記詞。具體細節可以參考這篇文章。
創建好後,會給你默認一個賬號:
由於我們是要基於以太坊測試鏈發行代幣,所以我們選擇測試鏈:
此時,我們發現我們的賬戶中沒有ether,依次按照如下步驟獲取
這時候會打開網頁,點擊圖標(建議點一兩次就ok,每次會給你的賬戶放1個ether,部署合約1個就已經搓搓有餘。)。
一會你就會發現你們賬戶有金額;
準備工作終於做完了,現在我們開始部署代幣合約,這時候點擊IDE右側橫欄中的run,按照1到3確認信息,並在4中編寫你要發行的代幣的信息,依次是100000000,"GaoTeB","GTB"(發行總量,發行幣全稱,發行幣簡稱),4步確認不誤後點擊create按鈕發佈代幣合約帶測試鏈中
點擊後,會彈出對話框
點擊submit後,如果不報錯,此時,會出現你的合約信息:
點擊合約會打開頁面,你可以看到正在創建中:
那麼你就基本已經發行成功你的代幣了!
怎麼在我們的賬戶下看呢?
下面我們使用MetaMask工具查看,依次點擊
將剛纔打開的頁面中的信息填入MetaMask中
這時候,我們就能看到啦:
哎,終於大功告成,下面我們來轉賬一回試試:
代幣交易
MetaMask插件沒有提供代幣交易功能,同時考慮到很多人並沒有以太坊錢包或是被以太坊錢包網絡同步問題折磨,今天我用網頁錢包來講解代幣交易。
初次進入會有一些列的確認信息,一頓點擊,來到這個頁面,選擇與MetaMask同一個網絡鏈(以太坊測試鏈):
點擊鏈接後,這個頁面就會和你的MetaMask鏈接上,你會發現你的賬戶信息就會出現在頁面上。
此時,你的Token信息並沒有出現在這裏,需要你認爲添加:
點擊添加,將你的Token信息填入這裏:
此時,你會發現:
然後開始代幣交易,我們嘗試轉賬給別的賬戶,填寫好信息後依次點擊: