第八課 技術小白如何在45分鐘內發行通證(TOKEN)並上線交易

1. 文章摘要

【本文目標】

經過逐步的指導和截圖舉證,一步步帶領一個技術小白完成一個數字貨幣(通證,代幣,TOKEN)的發佈演示和上線交易。php

【環境前置條件】

參考《第六課 技術小白如何開發一個DAPP區塊鏈應用(以寵物商店爲例)》,已在本地WIDOWS環境完成MetaMask輕錢包客戶端的安裝和配置;做者建議最好遵循從頭開始的課程學習順序。不過若是你想半途插入實操學習,問題也不大,遇到障礙時反向找對應文章的指導內容便可完成。git

【技術收穫】

從本實踐中,你能夠學習到: ERC20 Token的定義和實踐 使用Remix Solidity IDE編寫智能合約和編譯調試 使用MetaMask完成錢包帳戶查看 使用網頁錢包完成代幣交易演示github

【實操課程列表】

第一課 如何在WINDOWS環境下搭建以太坊開發環境 第二課 如何實現以太坊最簡智能合約「Hello World」的運行 第四課 以太坊開發框架Truffle從入門到實戰 第六課 技術小白如何開發一個DAPP區塊鏈應用(以寵物商店爲例) 第七課 技術小白如何在45分鐘內發行通證(TOKEN)並上線交易 第八課 如何調試以太坊官網的智能合約衆籌案例 第九課 如何在Remix環境下進行Solidity代碼單步調試 第十課 Solidity語言編輯器REMIX指導大全json

【說明】未列出的課程爲知識普及的非實操類課程,全部區塊鏈文章參考「區塊鏈入口」專欄。瀏覽器

2. ERC20 Token定義和接口說明

定義

ERC20合約是在2015年11月在EIP上提出的一個合約標準,代幣定義的一個標準。 Token表明數字資產,具備價值,可是並非都符合特定的規範。 基於ERC20的貨幣更容易互換,而且可以在Dapps上相同的工做。 新的標準可讓token更兼容,容許其餘功能,包括投票標記化。操做更像一個投票操做,Token的持有人能夠徹底控制資產,遵照ERC20的token能夠跟蹤任何人在任什麼時候間擁有多少token。基於eth合約的子貨幣,因此容易實施。安全

ERC20 Token接口說明

方法

注意:調用者必須處理返回falsereturns (bool success).調用者絕對不能假設返回false的狀況不存在。bash

name

返回這個令牌的名字,好比"MyToken". 可選 - 這種方法能夠用來提升可用性,但接口和其餘契約不能期望這些值存在。微信

function name() constant returns (string name)
複製代碼

symbol

返回令牌的符號,好比HIX. 可選 - 這種方法能夠用來提升可用性,但接口和其餘契約不能期望這些值存在。網絡

function symbol() constant returns (string symbol)
複製代碼

decimals

返回token使用的小數點後幾位, 好比 8,表示分配token數量爲100000000app

可選 - 這種方法能夠用來提升可用性,但接口和其餘契約不能期望這些值存在。

function decimals() constant returns (uint8 decimals)
複製代碼

totalSupply

返回token的總供應量。

function totalSupply() constant returns (uint256 totalSupply)
複製代碼

balanceOf

返回地址是_owner的帳戶的帳戶餘額。

function balanceOf(address _owner) constant returns (uint256 balance)
複製代碼

transfer

轉移_value的token數量到的地址_to,而且必須觸發Transfer事件。 若是_from賬戶餘額沒有足夠的令牌來支出,該函數應該被throw

建立新令牌的令牌合同應該在建立令牌時將_from地址設置爲0x0觸發傳輸事件。

注意 0值的傳輸必須被視爲正常傳輸並觸發傳輸事件。

function transfer(address _to, uint256 _value) returns (bool success)
複製代碼

transferFrom

從地址_from發送數量爲_value的token到地址_to,必須觸發Transfer事件。

transferFrom方法用於提取工做流,容許合同代您轉移token。這能夠用於例如容許合約代您轉讓代幣和/或以子貨幣收取費用。除了_from賬戶已經經過某種機制故意地受權消息的發送者以外,該函數應該throw。

注意 0值的傳輸必須被視爲正常傳輸並觸發傳輸事件。

function transferFrom(address _from, address _to, uint256 _value) returns (bool success)
複製代碼

approve

容許_spender屢次取回您的賬戶,最高達_value金額。 若是再次調用此函數,它將以_value覆蓋當前的餘量。

注意:爲了阻止向量攻擊,客戶端須要確認以這樣的方式建立用戶接口,即將它們設置爲0,而後將其設置爲同一個花費者的另外一個值。雖然合同自己不該該強制執行,容許向後兼容之前部署的合同兼容性

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被轉移(包括0值),必須被觸發。

event Transfer(address indexed _from, address indexed _to, uint256 _value)
複製代碼

Approval

當任何成功調用approve(address _spender, uint256 _value)後,必須被觸發。

event Approval(address indexed _owner, address indexed _spender, uint256 _value)
複製代碼

[官網接口說明點擊查看],(github.com/ethereum/EI…) 接口文件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 能夠有的小數點個數,最小的代幣單位。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; } } 複製代碼

函數的功能參考函數的說明描述和代碼自解釋。

4. 合約編譯部署和發佈

MetaMask錢包聯網

**【前置條件】**做者假設學習者已完成MetaMask的安裝和配置。尚未完成的,參考《第六課 技術小白如何開發一個DAPP區塊鏈應用(以寵物商店爲例)》的章節「5. 安裝 MetaMask和配置區塊鏈網絡」,在本地WIDOWS環境完成MetaMask輕錢包客戶端的安裝和配置。

打開MetaMask錢包,點擊左上角的「Ropsten Test Network」 鏈接成功。

鏈接成功
查看存量的帳號Account 1,其中ETH餘額顯示爲0。
存量帳號Account 1餘額爲0
點擊「Copy Address to Clipboard」,記錄Account 1錢包地址爲

0xD1F7922e8b78cBEB182250753ade8379d1E09949

點擊MetaMask右上角環形頭像的菜單「Create Account」, 建立一個新錢包帳號Account 8,

新增帳號成功

記錄Account 8的錢包地址爲

0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363

點擊Account 8的"BUY"按鈕,能夠從「Ropsten Test Network」 免費獲取一些測試ETH。

跳轉到BUY ETH頁面
點擊按鈕「ROPSTEN TEST FAUCET」能夠看到贈送頁面
點擊綠色按鈕,等待10秒以上,成功的話每次能夠獲取1個測試ETH。 【故障排查】
贈送失敗
做者操做時,出現錯誤。從提示看,是因爲用戶交易拒絕。等10秒後再點擊該綠色按鈕則未有錯誤提示了。緣由不明,多是操做頻繁致使。 屢次點擊,偶爾出錯,小編一共從這個測試網站獲取了5個測試ETH用於做爲發幣的GAS燃料。
獲取測試ETH

###Remix Solidity IDE調試環境介紹 1,代碼編寫和編譯 咱們以第二課的「Hello World」智能合約爲例,參考下圖可完成編譯和語法錯誤發現。

編譯環境使用方法
2,合約建立 參考附圖描述,在配置號MetaMask帳號和網絡鏈接的狀況下,確保帳戶有虛擬ETH用於合約建立花銷。最後點擊「Create」按鈕能夠完成合約建立。點擊輸出區的 網頁連接能夠查看合約的詳細狀況。
支付合約花銷
image.png
3, 合約執行 參考附件路徑圖,點擊RUN按鈕能夠執行一次合約。點擊下方的「Say」按鈕,能夠看到合約輸出。在調試輸出窗口能夠看到「call to hello.say」的運行提示。
合約執行

**【總結】**因此說,沒有Ubuntu+Ganache等,直接在WINDOWS環境,也可使用Remix+MetaMask+Ropsten Test Network組合完成一套完整的以太坊測試環境。 更詳細的REMIX幫助文檔參考第十課 Solidity語言編輯器REMIX指導大全

##編譯ERC20智能合約 CHROME瀏覽器打開Remix Solidity IDE環境,打開以前編寫的「TokenERC20.sol」智能合約,而後點擊右側的「start to compile」按鈕。能夠看到,除了一些Warning提示外,智能合約編譯成功。

編譯操做

##運行ERC20智能合約

運行頁面
切換到"RUN"頁面,Environment選擇「Injected Web3」, Account自動更新爲MetaMask的Account 8帳號。「Create」按鈕前按照發幣數量(本次發1個億),代幣名稱,代幣符號填寫爲100000000,"ColorBay","CB"。 點擊"Create"按鈕,會彈出一個交易確認框,設置合理的Gas Price,但要確保Max Transaction Fee不會超過Account 8的ETH總數,點「SUBMIT」按鈕。
智能合約部署
部署成功的話,Account8 會產生一條交易記錄,顯示狀態爲「Contract Published」,表示部署成功了。
部署成功
**【說明】**有時點擊帳號,會出現「Retry with a higher gas price here」的提示,通常狀況下再等待10秒看看可否交易成功。萬一仍是不成的話,可考慮該Gas Price大點。
部署確認中

##MetaMask加載TOKEN 點擊Account 8的交易記錄,能夠跳轉智能合約部署信息顯示頁面:

部署成功
獲取智能合約地址爲 0x5eeec41dc08d7caece17c4a349635934637036f1,點擊可查看該交易詳情。

點擊MetaMask的Account 8帳號的TOKENS頁面的「ADD TOKEN」按鈕,把代幣智能合約地址Token Contract Address爲0x5eeec41dc08d7caece17c4a349635934637036f1,代幣標識符Token Symbol爲CB,則能夠建立你的代幣了。

代幣建立成功,一共有1億個CB幣了。此時,點擊100000000的代幣位置,跳轉到 代幣查詢頁面, 能夠在etherscan網站看到帳戶的持幣數量了。
1億
TOKEN信息

截止做者發稿時,以太坊的價格爲2559元/個,而你有1億個CB幣,是否是快要走上人生巔峯了呢?哈哈,作個夢而已。真正的區塊鏈應用,要能有生態,能爲人類社會創造價值,而不是講故事,割韭菜!

5. 代幣交易

因爲MetaMask插件沒有提供代幣交易功能,同時考慮到不少人並無以太坊錢包或是被以太坊錢包網絡同步問題折磨,今天我用網頁錢包來說解代幣交易。 1. 進入網頁錢包地址 第一次進入有一些安全提示須要用戶確認。 2. 進入以後,按照下圖進行設置

網絡配置

3,增長自定義代幣 填寫地址Token Contract 爲0x5eeec41dc08d7caece17c4a349635934637036f1和其餘信息,點擊保存按鈕。

增長自定義TOKEN

配置TOKEN合約地址和代幣符號
配置成功的界面以下:
**【說明】**若是不成功,則提示爲「Not a valid ERC-20 token CB」,則多是代幣信息填寫不正確或者右上角的網絡選擇錯誤,沒有選擇「Network Ropston( infura.com)」選項引發。

4.轉帳給Account 1 填寫Account 1的地址「0xD1F7922e8b78cBEB182250753ade8379d1E09949」到「發送至地址」輸入框,

選擇CB,不是ETH
轉帳確認
交易確認
交易提示
查看帳戶餘額,Account 8減小800萬個CB幣,而Account 1則增長了800萬個CB幣。
Account 8
Account 1
轉帳交易成功了! 做爲一個古典投資人,用45分鐘完成了TOKEN上線和交易,用4個小時整理了這篇文章。學習就是這麼簡單,只要你對新技術保持飢餓感,高齡開發轉型區塊鏈也不是難事!

6. 總結參考文檔

本文是站在巨人的肩膀上的操做實踐,感謝如下文章做者的幫助:

知識對接服務: 輝哥和歐陽哥哥在知識星球開通了區塊鏈入門專欄,用於存放簡書區塊鏈入門專欄文章的工程源碼等內容,並創建專項微信羣用於技術交流,歡迎加入。

相關文章
相關標籤/搜索