1. 程式人生 > >在以太坊上發行自己的代幣

在以太坊上發行自己的代幣

余額 定義 完成後 pst 菜單 dem dea scan 基於

簡單代幣開發

代幣(Token):

代幣單純從其名字上理解的話,就是一種可以替代通用貨幣起到交換媒介作用的東西,可以是商場積分,可以是遊戲幣,也可以是籌碼。但是在區塊鏈中,就不完全是那麽回事了,區塊鏈中的代幣或者說Token通常指的是具有流通性的加密數字權益證明,例如比特幣、以太幣等數字貨幣都屬於代幣

從以上定義可以得知代幣的三個要素:

  • 權益證明:一種數字形式存在的權益憑證,代表一種權利,一種固有的內在價值和使用價值
  • 加密:為了防止篡改,保護隱私,不可以復制等
  • 較高的可流通性(去中心化):可以進行交易,兌換通用或法定貨幣等,之所以要去中心化是因為中心化的代幣都存在平臺限制及信任問題,所以其流通性就會受到局限

綜上,簡言之代幣的概念大致上就是基於區塊鏈發行的加密貨幣或者其他類似的東西。通常大多數人或團隊在開發區塊鏈項目時,都會考慮發行自己的代幣。在最初的時候,我們要發行自己的加密貨幣得從比特幣的源碼上改造出來。不過現如今通過以太坊平臺,我們能夠很方便的開發並發行自己的代幣,所以本文將介紹如何基於以太坊開發自己的代幣。

首先我們使用solidity開發一個擁有最基本功能的“代幣”demo,以便了解代幣最基礎的樣子,代碼如下:

pragma solidity ^0.4.20;

contract SimpleToken {

    // 保存每個地址所擁有的代幣數量
    mapping (address => uint256) public balanceOf; 

    // 參數為代幣的初始供應量或者說發行數量
    constructor (uint256 initSupply) public {
        // 創建者擁有所有的代幣
        balanceOf[msg.sender] = initSupply;
    }

    /**
     * 轉移代幣
     * 
     * @param _to     接收方的賬戶地址
     * @param _value  轉移的代幣數量
     */
    function transfer (address _to, uint256 _value) public {
        // 發送方的賬戶余額需大於等於轉移的代幣數量
        require(balanceOf[msg.sender] >= _value);
        // 檢查有沒有發生溢出,因為數量有可能超過uint256可存儲的範圍
        require(balanceOf[_to] + _value >= balanceOf[_to]);

        // 減少發送方賬戶的代幣數量
        balanceOf[msg.sender] -= _value;
        // 增加接收方賬戶的代幣數量
        balanceOf[_to] += _value;
    }
}

1.代碼編寫完成後,在remix上部署合約,首先初始化代幣供應量:
技術分享圖片

2.查看指定賬戶所擁有的代幣數量:
技術分享圖片

3.轉移代幣到另一個賬戶地址:
技術分享圖片

4.此時該賬戶地址的代幣數量就只剩下100了:
技術分享圖片


ERC-20標準簡述

在上一小節中,我們實現了一個簡單的代幣,但就因為簡單,所以該代幣是極其不完善的,例如沒有代幣名稱、代幣符號以及無法控制代幣的交易等。

因此以太坊定義了ERC20,ERC20是以太坊定義的一個代幣統一標準,該標準描述了我們在實現代幣時必須要遵守的協議,如指定代幣名稱、總量、實現代幣交易函數等,只有實現了該協議的代幣才能被以太坊錢包支持。

下圖是一個實現了ERC20協議的代幣:
技術分享圖片

實際上ERC20協議是定義了一組標準接口,該協議使得在以太坊上開發的任何代幣都能被其他應用程序重用,例如錢包到分散的交易所等,如下:

contract ERC20Interface {
    // 代幣名稱
    string public constant name = "Token Name";
    // 代幣符號或者說簡寫
    string public constant symbol = "SYM";
    // 代幣小數點位數,代幣的最小單位, 18表示我們可以擁有 0.0000000000000000001單位個代幣
    uint8 public constant decimals = 18;  // 18 is the most common number of decimal places

    // 查詢代幣的發行總量
    function totalSupply() public constant returns (uint);
    // 查詢地址的代幣余額
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    // 查詢spender允許從tokenOwner上花費的代幣數量
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    // 實現代幣交易,用於從本賬戶給某個地址轉移代幣
    function transfer(address to, uint tokens) public returns (bool success);
    // 允許spender多次從你的賬戶取款,並且最多可取tokens個,主要用於某些場景下授權委托其他用戶從你的賬戶上花費代幣
    function approve(address spender, uint tokens) public returns (bool success);
    // 實現代幣用戶之間的交易,從一個地址轉移代幣到另一個地址
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    // 代幣交易時觸發的事件,即調用transfer方法時觸發
    event Transfer(address indexed from, address indexed to, uint tokens);
    // 允許其他用戶從你的賬戶上花費代幣時觸發的事件,即調用approve方法時觸發
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

ERC-20標準代幣的實現

代碼如下:

pragma solidity ^0.4.20;

// 定義ERC-20標準接口
contract ERC20Interface {
    // 代幣名稱
    string public name;
    // 代幣符號或者說簡寫
    string public symbol;
    // 代幣小數點位數,代幣的最小單位
    uint8 public decimals;
    // 代幣的發行總量
    uint public totalSupply;

    // 實現代幣交易,用於給某個地址轉移代幣
    function transfer(address to, uint tokens) public returns (bool success);
    // 實現代幣用戶之間的交易,從一個地址轉移代幣到另一個地址
    function transferFrom(address from, address to, uint tokens) public returns (bool success);
    // 允許spender多次從你的賬戶取款,並且最多可取tokens個,主要用於某些場景下授權委托其他用戶從你的賬戶上花費代幣
    function approve(address spender, uint tokens) public returns (bool success);
    // 查詢spender允許從tokenOwner上花費的代幣數量
    function allowance(address tokenOwner, address spender) public view returns (uint remaining);

    // 代幣交易時觸發的事件,即調用transfer方法時觸發
    event Transfer(address indexed from, address indexed to, uint tokens);
    // 允許其他用戶從你的賬戶上花費代幣時觸發的事件,即調用approve方法時觸發
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

// 實現ERC-20標準接口
contract ERC20Impl is ERC20Interface {
    // 存儲每個地址的余額(因為是public的所以會自動生成balanceOf方法)
    mapping (address => uint256) public balanceOf;
    // 存儲每個地址可操作的地址及其可操作的金額
    mapping (address => mapping (address => uint256)) internal allowed;

    // 初始化屬性
    constructor() public {
        name = "Test Token";
        symbol = "TEST"; 
        decimals = 18;
        totalSupply = 100000000;
        // 初始化該代幣的賬戶會擁有所有的代幣
        balanceOf[msg.sender] = totalSupply;
    }

    function transfer(address to, uint tokens) public returns (bool success) {
        // 檢驗接收者地址是否合法
        require(to != address(0));
        // 檢驗發送者賬戶余額是否足夠
        require(balanceOf[msg.sender] >= tokens);
        // 檢驗是否會發生溢出
        require(balanceOf[to] + tokens >= balanceOf[to]);

        // 扣除發送者賬戶余額
        balanceOf[msg.sender] -= tokens;
        // 增加接收者賬戶余額
        balanceOf[to] += tokens;

        // 觸發相應的事件
        emit Transfer(msg.sender, to, tokens);
    }

    function transferFrom(address from, address to, uint tokens) public returns (bool success) {
        // 檢驗地址是否合法
        require(to != address(0) && from != address(0));
        // 檢驗發送者賬戶余額是否足夠
        require(balanceOf[from] >= tokens);
        // 檢驗操作的金額是否是被允許的
        require(allowed[from][msg.sender] <= tokens);
        // 檢驗是否會發生溢出
        require(balanceOf[to] + tokens >= balanceOf[to]);

        // 扣除發送者賬戶余額
        balanceOf[from] -= tokens;
        // 增加接收者賬戶余額
        balanceOf[to] += tokens;

        // 觸發相應的事件
        emit Transfer(from, to, tokens);   

        success = true;
    }

    function approve(address spender, uint tokens) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        // 觸發相應的事件
        emit Approval(msg.sender, spender, tokens);

        success = true;
    }

    function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
        return allowed[tokenOwner][spender];
    }
}

將以上代碼復制到Remix上,編譯合約代碼:
技術分享圖片

部署合約:
技術分享圖片

部署成功:
技術分享圖片

測試balanceOf方法:
技術分享圖片

測試transfer方法:
技術分享圖片


在Remix上測試完基本的方法後,我們通過MetaMask在以太坊的測試網絡上發行我們的代幣。打開MetaMask選擇Ropsten測試網絡,如下:
技術分享圖片

註:如果你沒有余額請點擊DEPOSIT按鈕,然後再點擊GET ETHER按鈕,會進入一個網站,在該網站中點擊request 1 ether from faucet,可以送一些測試網絡的以太幣給你:
技術分享圖片

然後到Remix IDE上刷新一下,此時Remix會自動選擇相應的測試網絡,註意MetaMask和Remix需要在同一個瀏覽器上,並且Environment和Account和MetaMask保持一致,如下:
技術分享圖片

點擊Deploy後,這時MetaMask會彈出一個交易確認框,點CONFIRM:
技術分享圖片

待合約部署交易確認之後,復制合約地址:
技術分享圖片

打開Metamask界面,打開菜單(Menu),點ADD TOKEN:
技術分享圖片

出現如下對話框,選擇Custom Token,然後將合約地址粘貼進去:
技術分享圖片

點ADD TOKENS:
技術分享圖片

這時在MetaMask裏就可以看到我們創建的代幣了,如下:
技術分享圖片

接下來我們測試一下代幣的轉賬功能,選擇我們剛剛部署的代幣,點擊SEND:
技術分享圖片

填寫轉賬的相關信息:
技術分享圖片

確認轉賬:
技術分享圖片

成功後會生成相應的交易記錄:
技術分享圖片

如此一來我們就完成了代幣的創建和部署(正式網絡和測試網絡部署方法一樣),現在已經可以在Etherscan查詢到我們剛剛發行的代幣了:
技術分享圖片

點擊合約地址可以查看到代幣的詳情:
技術分享圖片

在以太坊上發行自己的代幣