圈钱跑路 ERC20 Token 合约代码分析
ERC20 Token 合約代碼分析
合約接口代碼
// https://github.com/ethereum/EIPs/issues/20// 接口標準contract ERC20 {function totalSupply() constant returns (uint totalSupply); // 總發行量function balanceOf(address _owner) constant returns (uint balance);function transfer(address _to, uint _value) returns (bool success); // 代幣分發(注意, 這個只有合約的Creator 可以調用)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);// Token信息string public constant name = "4FunCoin";string public constant symbol = "4FC";uint8 public constant decimals = 18; // token的精度, 大部分都是18 }上面的代碼是一個標準的ERC20標準的代碼, 他給出了框架, 我們只需要實現相應的函數就好了, 這里給出函數說明:
接口函數
- 函數的形參是局部有效, 所以前面使用下劃線, 與其他的變量區別開來. 如 _owner.
- totalSupply() 函數返回這個Token的總發行量;
- balanceOf() 查詢某個地址的Token數量 , 結合mapping實現
- transfer() owner 使用這個進行發送代幣
- transferFrom () token的所有者用來發送token
- allowance() 控制代幣的交易,如可交易賬號及資產, 控制Token的流通
- approve() 允許用戶可花費的代幣數;
事件函數
這里兩個Event是重點, 之前沒弄懂, 現在倒是明白不少, 就是產生事件, 從而可以被前端代碼捕獲到, 從對事件使用事件服務函數進行處理 , 這里的參數,也將傳遞給服務函數
- event Transfer() Token的轉賬事件
- event Approval() 允許事件
合約實現代碼
理解了上面的函數, 下面的代碼,就實現了Token合約的函數填充
pragma solidity ^0.4.16;interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; } // token的 接受者 這里聲明接口, 將會在我們的ABI里contract TokenERC20 { /*********Token的屬性說明************/string public name = 4FunCoin;string public symbol = 4FC;uint8 public decimals = 18; // 18 是建議的默認值uint256 public totalSupply; // 發行量// 建立映射 地址對應了 uint' 便是他的余額mapping (address => uint256) public balanceOf; // 地址對應余額mapping (address => mapping (address => uint256)) public allowance;// 事件,用來通知客戶端Token交易發生event Transfer(address indexed from, address indexed to, uint256 value);// 事件,用來通知客戶端代幣被消耗(這里就不是轉移, 是token用了就沒了)event Burn(address indexed from, uint256 value);// 這里是構造函數, 實例創建時候執行function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {totalSupply = initialSupply * 10 ** uint256(decimals); // 這里確定了總發行量balanceOf[msg.sender] = totalSupply; // 這里就比較重要, 這里相當于實現了, 把token 全部給合約的Creatorname = tokenName;symbol = tokenSymbol;}// token的發送函數function _transfer(address _from, address _to, uint _value) internal {require(_to != 0x0); // 不是零地址require(balanceOf[_from] >= _value); // 有足夠的余額來發送require(balanceOf[_to] + _value > balanceOf[_to]); // 這里也有意思, 不能發送負數的值(hhhh)uint previousBalances = balanceOf[_from] + balanceOf[_to]; // 這個是為了校驗, 避免過程出錯, 總量不變對吧?balanceOf[_from] -= _value; //發錢 不多說balanceOf[_to] += _value;Transfer(_from, _to, _value); // 這里觸發了轉賬的事件 , 見上eventassert(balanceOf[_from] + balanceOf[_to] == previousBalances); // 判斷總額是否一致, 避免過程出錯}function transfer(address _to, uint256 _value) public {_transfer(msg.sender, _to, _value); // 這里已經儲存了 合約創建者的信息, 這個函數是只能被合約創建者使用}function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {require(_value <= allowance[_from][msg.sender]); // 這句很重要, 地址對應的合約地址(也就是token余額)allowance[_from][msg.sender] -= _value;_transfer(_from, _to, _value);return true;}function approve(address _spender, uint256 _value) publicreturns (bool success) {allowance[msg.sender][_spender] = _value; // 這里是可花費總量return true;}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;}}// 正如其名, 這個是燒幣(SB)的.. ,用于把創建者的 token 燒掉function burn(uint256 _value) public returns (bool success) {require(balanceOf[msg.sender] >= _value); // 必須要有這么多balanceOf[msg.sender] -= _value;totalSupply -= _value;Burn(msg.sender, _value);return true;}// 這個是用戶銷毀token.....function burnFrom(address _from, uint256 _value) public returns (bool success) {require(balanceOf[_from] >= _value); // 一樣要有這么多require(_value <= allowance[_from][msg.sender]); // balanceOf[_from] -= _value;allowance[_from][msg.sender] -= _value;totalSupply -= _value;Burn(_from, _value);return true;} }上面的代碼閱讀難度不大, 也寫了大多處的注釋, 這里吧自己學的幾個Point 提一下
構造函數
// 這里是構造函數, 實例創建時候執行 function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {totalSupply = initialSupply * 10 ** uint256(decimals); // 這里確定了總發行量balanceOf[msg.sender] = totalSupply; // 這里就比較重要, 這里相當于實現了, 把token 全部給合約的Creatorname = tokenName;symbol = tokenSymbol;}在Solidity里面, Contract 我們可以直接理解成一個Class吧. 如C++ 一樣, 這里面也存在一個
構造函數而且他們的功能也是近乎相同, 在合約創建的時候執行一次.(沒錯 , 合約整個生命周期里只能執行這樣一次) , 所以他的作用就是實現合約信息的初始化, 一旦數據寫入區塊數據, 將是無法更改的了(永固性).
構造函數的是不能有返回值的(有也無法接受), 但是可以帶參數, 像是此處代碼, 把發行量, token的名稱和token的 符號作為參數留出. 在合約初始化時候我們便可以自行定義.
函數體中可見, 我們對貨幣總量, 名稱和 符號進行賦值, 這樣,這些值就永遠的記錄在了我們的合約的區塊數據中了
映射(mapping)
// 建立映射 地址對應了 uint' 便是他的余額mapping (address => uint256) public balanceOf; // 地址對應余額mapping (address => mapping (address => uint256)) public allowance;這個形式,乍一眼看上去是沒那么好懂. 其實慢慢的 也是理解了, 這里的映射, 通俗的講,就是相當于我們的字典, 是一個鍵值對. 上述的代碼也是建立了一個 address 到 uint類型的映射關系.
balanceOf[msg.sender] = 10000; //msg.sender 是一個地址這樣簡單的方法, 相當于對賬戶進行余額的賦值;
事件(event)
Solidity 是基于事件驅動(前面有說不是消息驅動), 事件也是連接前端的橋梁, 一個事件的觸發, 可以被前端所捕獲, 從而做出相應的響應.比如MeteMask的支付窗口一樣. 點了支付 ,他就彈出來了對吧.
// 事件,用來通知客戶端Token交易發生 event Transfer(address indexed from, address indexed to, uint256 value);// 下面是事件監聽 var ClientReceipt = web3.eth.contract(abi); //導入接口說明 var clientReceipt = ClientReceipt.at(0x123 /* address */); //合約地址 // 對事件進行監聽, var event = clientReceipt.Deposit(function(error, result) {if (!error)console.log(result); }); // 對事件進行綁定bindEvents: function() {$(document).on('click', '.btn-adopt', App.handleAdopt);},EG:
事件 是合約和區塊鏈通訊的一種機制。你的前端應用“監聽”某些事件,并做出反應。
// in contract // 這里建立事件 event IntegersAdded(uint x, uint y, uint result);function add(uint _x, uint _y) public {uint result = _x + _y;//觸發事件,通知appIntegersAdded(_x, _y, result);return result; }//in js // 你的 app 前端可以監聽這個事件。JavaScript 實現如下:YourContract.IntegersAdded(function(error, result) { // do something }接口(interface)
這個合約的第二行就是, 差點忘了… 這個也是一個相對重要的東西, 合約主要的作用個人理解來說就是實現與其他合約的交互.
如果我們的合約需要和區塊鏈上的其他的合約交互(比如使用其中的公有函數),則需先定義一個 interface (接口)。
先舉一個簡單的例子子。 假設在區塊鏈上有這么一個合約:
contract LuckyNumber {mapping(address => uint) numbers;function setNum(uint _num) public {numbers[msg.sender] = _num;}function getNum(address _myAddress) public view returns (uint) {return numbers[_myAddress];} }這是個很簡單的合約,您可以用它存儲自己的幸運號碼,并將其與地址關聯。 這樣其他人就可以通過地址得到號碼了。
現在假設我們有一個外部合約,使用 getNum 函數可讀取其中的數據。
首先,我們定義 LuckyNumber 合約的 interface :
interface NumberInterface {function getNum(address _myAddress) public view returns (uint); }首先,我們只用聲明進行交互的函數 —— 在本例中為 getNum.(聲明, 函數體是空的)
然后我們在我們自己的合約中直接對接口進行訪問, 就可以getnum 了.
Ethereum 內部有一個散列函數keccak256,它用了SHA3版本。一個散列函數基本上就是把一個字符串轉換為一個256位的16進制數字。字符串的一個微小變化會引起散列數據極大變化。
后面的話
這次的分析也是基于上一篇的 Token發布的文章的進一步, 通過自己讀讀代碼,總結一下里面的point, 借此也是學習力Solidity.
以后的博文將轉戰自己的博客 鏈接在此~ 也希望大家多多交流~
這里也是自己推的公眾號, 也可以關注一波~
總結
以上是生活随笔為你收集整理的圈钱跑路 ERC20 Token 合约代码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 圈钱跑路 发行自己的ERC20 Toke
- 下一篇: SSM项目 Jquery实现From表