以太坊代幣智能合約示例

學習以太坊智能合約開發最好的方法,莫過於結合具體的應用場景案例,舉例如投票、衆籌、ERC20或ERC721代幣發行等等,經過一個實例的實現,邊實踐邊學習了。在這篇教程中,咱們將使用以太坊solidity來實現一個遵循以太坊ERC20代幣規範的智能合約,並給出最終實現的solidity所有代碼。前端

若是你但願立刻開始學習以太坊DApp開發,能夠訪問匯智網提供的出色的在線互動教程:git

ERC20規範

ERC20約定了一個代幣合約須要實現的接口,規範參見ERC20github

// 接口標準
  contract ERC20 {
      function totalSupply() constant returns (uint totalSupply);   // 總髮行量
      function balanceOf(address _owner) constant returns (uint balance);
      // 代幣分發(注意, 這個只有合約的Creator 能夠調用)
      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);

      // Token信息
      string public constant name = "4FunCoin";
      string public constant symbol = "4FC";
      uint8 public constant decimals = 18;  // token的精度, 大部分都是18
}

上面的代碼是一個標準的ERC20標準的代碼, 規範給出了框架, 咱們只須要實現相應的函數就行了, 這裏給出函數說明。app

接口函數說明

函數的形參是局部有效, 因此前面使用下劃線, 與其餘的變量區別開來. 如 _owner.框架

  • totalSupply() 函數返回這個Token的總髮行量;
  • balanceOf() 查詢某個地址的Token數量 , 結合mapping實現
  • transfer() owner 使用這個進行發送代幣
  • transferFrom () token的全部者用來發送token
  • allowance() 控制代幣的交易,如可交易帳號及資產, 控制Token的流通
  • approve() 容許用戶可花費的代幣數;

事件函數說明

這裏兩個Event是重點, 事件,能夠被前端js代碼捕獲到並進行相應的處理:函數

  • event Transfer() Token的轉帳事件
  • event Approval() 容許事件

ERC20代幣合約實現

理解了上面的函數, 下面的代碼,就實現了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 所有給合約的Creator

        name = 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);   // 這裏觸發了轉帳的事件 , 見上event
        assert(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) public
        returns (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;
    }
}

上面的代碼閱讀難度不大, 也寫了大多處的註釋, 這裏簡單介紹幾個要點:ui

構造函數

// 這裏是構造函數, 實例建立時候執行
function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {
    totalSupply = initialSupply * 10 ** uint256(decimals);  // 這裏肯定了總髮行量
        balanceOf[msg.sender] = totalSupply;    // 這裏就比較重要, 這裏至關於實現了, 把token 所有給合約的Creator

        name = tokenName;
        symbol = tokenSymbol;
    }

在Solidity裏面, Contract 咱們能夠直接理解成一個Class吧, 如C++ 同樣, 這裏面也存在一個 構造函數並且他們的功能也是近乎相同, 在合約建立的時候執行一次.(沒錯 , 合約整個生命週期裏只能執行這樣一次) , 因此他的做用就是實現合約信息的初始化, 一旦數據寫入區塊數據, 將是沒法更改的了(永固性).this

構造函數的是不能有返回值的(有也沒法接受), 可是能夠帶參數, 像是此處代碼, 把發行量, token的名稱和token的 符號做爲參數留出. 在合約初始化時候咱們即可以自行定義..net

函數體中可見, 咱們對貨幣總量, 名稱和 符號進行賦值, 這樣,這些值就永遠的記錄在了咱們的合約的區塊數據中了

映射(mapping)

// 創建映射 地址對應了 uint' 即是他的餘額
    mapping (address => uint256) public balanceOf;   
    // 地址對應餘額
    mapping (address => mapping (address => uint256)) public allowance;

這個形式,乍一眼看上去是沒那麼好懂. 其實慢慢的 也是理解了, 這裏的映射, 通俗的講,就是至關於咱們的字典, 是一個鍵值對. 上述的代碼也是創建了一個 address 到 uint類型的映射關係.

balanceOf[msg.sender] = 10000;  //msg.sender 是一個地址

這樣簡單的方法, 至關於對帳戶進行餘額的賦值;

轉載:http://www.javashuo.com/article/p-frahwtak-bo.html

相關文章
相關標籤/搜索