如何在以太坊上發(fā)行自己的代幣

簡單代幣開發(fā)

代幣(Token):

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

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

  • 權(quán)益證明:一種數(shù)字形式存在的權(quán)益憑證,代表一種權(quán)利,一種固有的內(nèi)在價(jià)值和使用價(jià)值
  • 加密:為了防止篡改,保護(hù)隱私,不可以復(fù)制等
  • 較高的可流通性(去中心化):可以進(jìn)行交易,兌換通用或法定貨幣等,之所以要去中心化是因?yàn)橹行幕拇鷰哦即嬖谄脚_(tái)限制及信任問題,所以其流通性就會(huì)受到局限

綜上,簡言之代幣的概念大致上就是基于區(qū)塊鏈發(fā)行的加密貨幣或者其他類似的東西。通常大多數(shù)人或團(tuán)隊(duì)在開發(fā)區(qū)塊鏈項(xiàng)目時(shí),都會(huì)考慮發(fā)行自己的代幣。在最初的時(shí)候,我們要發(fā)行自己的加密貨幣得從比特幣的源碼上改造出來。不過現(xiàn)如今通過以太坊平臺(tái),我們能夠很方便的開發(fā)并發(fā)行自己的代幣,所以本文將介紹如何基于以太坊開發(fā)自己的代幣。

本文將使用到兩個(gè)工具,分別是Remix和MetaMask,如果對(duì)這兩個(gè)工具不太了解的話可以參考我另外兩篇文章:

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

pragma solidity ^0.4.20;

contract SimpleToken {
    
    // 保存每個(gè)地址所擁有的代幣數(shù)量
    mapping (address => uint256) public balanceOf; 
    
    // 參數(shù)為代幣的初始供應(yīng)量或者說發(fā)行數(shù)量
    constructor (uint256 initSupply) public {
        // 創(chuàng)建者擁有所有的代幣
        balanceOf[msg.sender] = initSupply;
    }
    
    /**
     * 轉(zhuǎn)移代幣
     * 
     * @param _to     接收方的賬戶地址
     * @param _value  轉(zhuǎn)移的代幣數(shù)量
     */
    function transfer (address _to, uint256 _value) public {
        // 發(fā)送方的賬戶余額需大于等于轉(zhuǎn)移的代幣數(shù)量
        require(balanceOf[msg.sender] >= _value);
        // 檢查有沒有發(fā)生溢出,因?yàn)閿?shù)量有可能超過uint256可存儲(chǔ)的范圍
        require(balanceOf[_to] + _value >= balanceOf[_to]);
        
        // 減少發(fā)送方賬戶的代幣數(shù)量
        balanceOf[msg.sender] -= _value;
        // 增加接收方賬戶的代幣數(shù)量
        balanceOf[_to] += _value;
    }
}

1.代碼編寫完成后,在remix上部署合約,首先初始化代幣供應(yīng)量:


image.png

2.查看指定賬戶所擁有的代幣數(shù)量:


image.png

3.轉(zhuǎn)移代幣到另一個(gè)賬戶地址:


image.png

4.此時(shí)該賬戶地址的代幣數(shù)量就只剩下100了:


image.png

ERC-20標(biāo)準(zhǔn)簡述

在上一小節(jié)中,我們實(shí)現(xiàn)了一個(gè)簡單的代幣,但就因?yàn)楹唵?,所以該代幣是極其不完善的,例如沒有代幣名稱、代幣符號(hào)以及無法控制代幣的交易等。

因此以太坊定義了ERC20,ERC20是以太坊定義的一個(gè)代幣統(tǒng)一標(biāo)準(zhǔn),該標(biāo)準(zhǔn)描述了我們?cè)趯?shí)現(xiàn)代幣時(shí)必須要遵守的協(xié)議,如指定代幣名稱、總量、實(shí)現(xiàn)代幣交易函數(shù)等,只有實(shí)現(xiàn)了該協(xié)議的代幣才能被以太坊錢包支持。

下圖是一個(gè)實(shí)現(xiàn)了ERC20協(xié)議的代幣:


image.png

實(shí)際上ERC20協(xié)議是定義了一組標(biāo)準(zhǔn)接口,該協(xié)議使得在以太坊上開發(fā)的任何代幣都能被其他應(yīng)用程序重用,例如錢包到分散的交易所等,如下:

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

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

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

ERC-20標(biāo)準(zhǔn)代幣的實(shí)現(xiàn)

代碼如下:

pragma solidity ^0.4.20;

// 定義ERC-20標(biāo)準(zhǔn)接口
contract ERC20Interface {
    // 代幣名稱
    string public name;
    // 代幣符號(hào)或者說簡寫
    string public symbol;
    // 代幣小數(shù)點(diǎn)位數(shù),代幣的最小單位
    uint8 public decimals;
    // 代幣的發(fā)行總量
    uint public totalSupply;

    // 實(shí)現(xiàn)代幣交易,用于給某個(gè)地址轉(zhuǎn)移代幣
    function transfer(address to, uint tokens) public returns (bool success);
    // 實(shí)現(xiàn)代幣用戶之間的交易,從一個(gè)地址轉(zhuǎn)移代幣到另一個(gè)地址
    function transferFrom(address from, address to, uint tokens) public returns (bool success);
    // 允許spender多次從你的賬戶取款,并且最多可取tokens個(gè),主要用于某些場(chǎng)景下授權(quán)委托其他用戶從你的賬戶上花費(fèi)代幣
    function approve(address spender, uint tokens) public returns (bool success);
    // 查詢spender允許從tokenOwner上花費(fèi)的代幣數(shù)量
    function allowance(address tokenOwner, address spender) public view returns (uint remaining);

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

// 實(shí)現(xiàn)ERC-20標(biāo)準(zhǔn)接口
contract ERC20Impl is ERC20Interface {
    // 存儲(chǔ)每個(gè)地址的余額(因?yàn)槭莗ublic的所以會(huì)自動(dòng)生成balanceOf方法)
    mapping (address => uint256) public balanceOf;
    // 存儲(chǔ)每個(gè)地址可操作的地址及其可操作的金額
    mapping (address => mapping (address => uint256)) internal allowed;

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

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

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

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

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

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

        // 觸發(fā)相應(yīng)的事件
        emit Transfer(from, to, tokens);   

        success = true;
    }

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

        success = true;
    }

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

將以上代碼復(fù)制到Remix上,編譯合約代碼:

image.png

部署合約:


image.png

部署成功:


image.png

測(cè)試balanceOf方法:


image.png

測(cè)試transfer方法:


image.png

在Remix上測(cè)試完基本的方法后,我們通過MetaMask在以太坊的測(cè)試網(wǎng)絡(luò)上發(fā)行我們的代幣。打開MetaMask選擇Ropsten測(cè)試網(wǎng)絡(luò),如下:

image.png

注:如果你沒有余額請(qǐng)點(diǎn)擊DEPOSIT按鈕,然后再點(diǎn)擊GET ETHER按鈕,會(huì)進(jìn)入一個(gè)網(wǎng)站,在該網(wǎng)站中點(diǎn)擊request 1 ether from faucet,可以送一些測(cè)試網(wǎng)絡(luò)的以太幣給你:


image.png

然后到Remix IDE上刷新一下,此時(shí)Remix會(huì)自動(dòng)選擇相應(yīng)的測(cè)試網(wǎng)絡(luò),注意MetaMask和Remix需要在同一個(gè)瀏覽器上,并且Environment和Account和MetaMask保持一致,如下:


image.png

點(diǎn)擊Deploy后,這時(shí)MetaMask會(huì)彈出一個(gè)交易確認(rèn)框,點(diǎn)CONFIRM:


image.png

待合約部署交易確認(rèn)之后,復(fù)制合約地址:


image.png

打開Metamask界面,打開菜單(Menu),點(diǎn)ADD TOKENS:


image.png

出現(xiàn)如下對(duì)話框,選擇Custom Token,然后將合約地址粘貼進(jìn)去:


image.png

點(diǎn)ADD TOKENS:


image.png

這時(shí)在MetaMask里就可以看到我們創(chuàng)建的代幣了,如下:


image.png

接下來我們測(cè)試一下轉(zhuǎn)移代幣,選擇我們剛剛部署的代幣,點(diǎn)擊SEND:


image.png

填寫轉(zhuǎn)賬的相關(guān)信息:


image.png

確認(rèn)轉(zhuǎn)賬:


image.png

成功后會(huì)生成相應(yīng)的交易記錄:


image.png

如此一來我們就完成了代幣的創(chuàng)建和部署(正式網(wǎng)絡(luò)和測(cè)試網(wǎng)絡(luò)部署方法一樣),現(xiàn)在已經(jīng)可以在Etherscan查詢到我們剛剛發(fā)行的代幣了:

image.png

查看代幣詳情

image.png


關(guān)于發(fā)行總量為0的問題

在上一小節(jié)中,我們已經(jīng)成功在以太坊上發(fā)行了我們自己的代幣,但許多同學(xué)應(yīng)該也已經(jīng)產(chǎn)生了一個(gè)疑問,為啥明明代幣中設(shè)置了代幣的發(fā)行數(shù)量,但是發(fā)行的數(shù)量還是為0呢?其實(shí)這也是新手在開發(fā)代幣時(shí)可能會(huì)遇到的一個(gè)坑,之所以我們看到發(fā)行總量為0是因?yàn)榇鷰诺陌l(fā)行總量的整數(shù)位表示并不是totalSupply的值,而是totalSupply除以10的decimals次方。

例如我們將decimals設(shè)置為8,那么就需要以代幣的發(fā)行總量(totalSupply)除以100000000,才能得到其整數(shù)。上一小節(jié)中合約代碼里設(shè)置的decimals值為18,代表代幣使用的小數(shù)位為18位,而totalSupply的值為1億,自然計(jì)算出來就是小于1的小數(shù),而且還是比較小的小數(shù),MetaMask只顯示3位小數(shù),所以導(dǎo)致在我們看來代幣的發(fā)行總量為0。

既然知道是什么原因?qū)е碌?,那么解決起來就好辦了,修改合約的構(gòu)造器代幣如下:

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

修改完成后,依舊按照我們上一小節(jié)所描述的流程去發(fā)行代幣并將代幣添加到MetaMask錢包中。此時(shí)該代幣的發(fā)行總量就應(yīng)該是我們期望的值了,如下:


image.png

我們也可以嘗試使用代幣進(jìn)行轉(zhuǎn)賬:


image.png

轉(zhuǎn)賬成功,該賬戶的余額也正??蹨p了:


image.png

另一個(gè)賬戶也正常收到了這100個(gè)代幣:


image.png

然后我們?cè)偃ゲ榭匆幌麓鷰?a target="_blank">詳情:

image.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容