1. 程式人生 > >建立自己的ERC20加密貨幣(可管理、增發、兌換、凍結等高階功能的代幣)

建立自己的ERC20加密貨幣(可管理、增發、兌換、凍結等高階功能的代幣)

(注:本文是在原文的基礎上,根據個人的理解,修改部分內容並添加了一些註釋)
pragma solidity ^0.4.16;

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }

/**
 * owned是合約的管理者
 */
contract owned {
    address public owner;

    /**
     * 初臺化建構函式
     */
    function owned () public {
        owner = msg.sender;
    }

    /**
     * 判斷當前合約呼叫者是否是合約的所有者
     */
    modifier onlyOwner {
        require (msg.sender == owner);
        _;
    }

    /**
     * 合約的所有者指派一個新的管理員
     * @param  newOwner address 新的管理員帳戶地址
     */
    function transferOwnership(address newOwner) onlyOwner public {
        if (newOwner != address(0)) {
        owner = newOwner;
      }
    }
}

/**
 * 基礎代幣合約
 */
contract TokenERC20 {
    string public name; //發行的代幣名稱
    string public symbol; //發行的代幣符號
    uint8 public decimals = 18;  //代幣單位,展示的小數點後面多少個0。
    uint256 public totalSupply; //發行的代幣總量

    /*記錄所有餘額的對映*/
    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);  //減去使用者餘額事件

    /* 初始化合約,並且把初始的所有代幣都給這合約的建立者
     * @param initialSupply 代幣的總數
     * @param tokenName 代幣名稱
     * @param tokenSymbol 代幣符號
     */
    function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {
        //初始化總量
        totalSupply = initialSupply * 10 ** uint256(decimals);   
        //給指定帳戶初始化代幣總量,初始化用於獎勵合約建立者
        balanceOf[msg.sender] = totalSupply;
        name = tokenName;
        symbol = tokenSymbol;
    }


    /**
     * 私有方法從一個帳戶傳送給另一個帳戶代幣
     * @param  _from address 傳送代幣的地址
     * @param  _to address 接受代幣的地址
     * @param  _value uint256 接受代幣的數量
     */
    function _transfer(address _from, address _to, uint256 _value) internal {

      //避免轉帳的地址是0x0
      require(_to != 0x0);

      //檢查傳送者是否擁有足夠餘額
      require(balanceOf[_from] >= _value);

      //檢查是否溢位
      require(balanceOf[_to] + _value > balanceOf[_to]);

      //儲存資料用於後面的判斷
      uint previousBalances = balanceOf[_from] + balanceOf[_to];

      //從傳送者減掉髮送額
      balanceOf[_from] -= _value;

      //給接收者加上相同的量
      balanceOf[_to] += _value;

      //通知任何監聽該交易的客戶端
      Transfer(_from, _to, _value);

      //判斷買、賣雙方的資料是否和轉換前一致
      assert(balanceOf[_from] + balanceOf[_to] == previousBalances);

    }

    /**
     * 從主帳戶合約呼叫者傳送給別人代幣
     * @param  _to address 接受代幣的地址
     * @param  _value uint256 接受代幣的數量
     */
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    /**
     * 從某個指定的帳戶中,向另一個帳戶傳送代幣
     * 呼叫過程,會檢查設定的允許最大交易額
     * @param  _from address 傳送者地址
     * @param  _to address 接受者地址
     * @param  _value uint256 要轉移的代幣數量
     * @return success        是否交易成功
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        //檢查傳送者是否擁有足夠餘額
        require(_value <= allowance[_from][msg.sender]);
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    /**
     * 設定帳戶允許支付的最大金額
     * 一般在智慧合約的時候,避免支付過多,造成風險
     * @param _spender 帳戶地址
     * @param _value 金額
     */
    function approve(address _spender, uint256 _value) public returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    /**
     * 設定帳戶允許支付的最大金額
     * 一般在智慧合約的時候,避免支付過多,造成風險,加入時間引數,可以在 tokenRecipient 中做其他操作
     * @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;
        }
    }

    /**
     * 減少代幣呼叫者的餘額
     * 操作以後是不可逆的
     * @param _value 要刪除的數量
     */
    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;
    }

    /**
     * 刪除帳戶的餘額(含其他帳戶)
     * 刪除以後是不可逆的
     * @param _from 要操作的帳戶地址
     * @param _value 要減去的數量
     */
    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;
    }
}

/**
 * 代幣增發、
 * 代幣凍結、
 * 代幣自動銷售和購買、
 * 高階代幣功能
 */
contract MyAdvancedToken is owned, TokenERC20 {

    //賣出的匯率,一個代幣,可以賣出多少個以太幣,單位是wei
    uint256 public sellPrice;

    //買入的匯率,1個以太幣,可以買幾個代幣
    uint256 public buyPrice;

    //是否凍結帳戶的列表
    mapping (address => bool) public frozenAccount;

    //定義一個事件,當有資產被凍結的時候,通知正在監聽事件的客戶端
    event FrozenFunds(address target, bool frozen);


    /*初始化合約,並且把初始的所有的令牌都給這合約的建立者
     * @param initialSupply 所有幣的總數
     * @param tokenName 代幣名稱
     * @param tokenSymbol 代幣符號
     */
        function MyAdvancedToken(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) TokenERC20(initialSupply, tokenName, tokenSymbol) public {}


    /**
     * 私有方法,從指定帳戶轉出餘額
     * @param  _from address 傳送代幣的地址
     * @param  _to address 接受代幣的地址
     * @param  _value uint256 接受代幣的數量
     */
    function _transfer(address _from, address _to, uint _value) internal {

        //避免轉帳的地址是0x0
        require (_to != 0x0);

        //檢查傳送者是否擁有足夠餘額
        require (balanceOf[_from] > _value);

        //檢查是否溢位
        require (balanceOf[_to] + _value > balanceOf[_to]);

        //檢查 凍結帳戶
        require(!frozenAccount[_from]);
        require(!frozenAccount[_to]);

        //從傳送者減掉髮送額
        balanceOf[_from] -= _value;

        //給接收者加上相同的量
        balanceOf[_to] += _value;

        //通知任何監聽該交易的客戶端
        Transfer(_from, _to, _value);

    }

    /**
     * 合約擁有者,可以為指定帳戶創造一些代幣
     * @param  target address 帳戶地址
     * @param  mintedAmount uint256 增加的金額(單位是wei)
     */
    function mintToken(address target, uint256 mintedAmount) onlyOwner public {

        //給指定地址增加代幣,同時總量也相加
        balanceOf[target] += mintedAmount;
        totalSupply += mintedAmount;


        Transfer(0, this, mintedAmount);
        Transfer(this, target, mintedAmount);
    }

    /**
     * 增加凍結帳戶名稱
     *
     * 你可能需要監管功能以便你能控制誰可以/誰不可以使用你建立的代幣合約
     *
     * @param  target address 帳戶地址
     * @param  freeze bool    是否凍結
     */
    function freezeAccount(address target, bool freeze) onlyOwner public {
        frozenAccount[target] = freeze;
        FrozenFunds(target, freeze);
    }

    /**
     * 設定買賣價格
     *
     * 如果你想讓ether(或其他代幣)為你的代幣進行背書,以便可以市場價自動化買賣代幣,我們可以這麼做。如果要使用浮動的價格,也可以在這裡設定
     *
     * @param newSellPrice 新的賣出價格
     * @param newBuyPrice 新的買入價格
     */
    function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
        sellPrice = newSellPrice;
        buyPrice = newBuyPrice;
    }

    /**
     * 使用以太幣購買代幣
     */
    function buy() payable public {
      uint amount = msg.value / buyPrice;

      _transfer(this, msg.sender, amount);
    }

    /**
     * @dev 賣出代幣
     * @return 要賣出的數量(單位是wei)
     */
    function sell(uint256 amount) public {

        //檢查合約的餘額是否充足
        require(this.balance >= amount * sellPrice);

        _transfer(msg.sender, this, amount);

        msg.sender.transfer(amount * sellPrice);
    }
}