智能合約開發學習—實踐 ERC20

這裡整理之前上課的筆記,都是入門級別一共 4 章,分別如下:

講那麼多,還是看個實際的例子比較有感覺

所謂得 ERC 是 Ethereum Request for Comments 的縮寫,是被同意後的 EIP 演變而來的

EIP 是 Ethereum Improvement Proposals 的縮寫,顧名思義就是提案,提案給 eth 社群經審查確定實作後就成為 ERC

而 EIP-20 的標題是 Token standard,就是以太幣的標準

interfaces

EIP-20 的文件有明定要實現標準接口如下

  • totalSupply
    • 返回此 contract 的 token 最大值
    • function totalSupply() public view returns (uint256)
  • balanceOf
    • 返回傳入 owner 的 balance
    • function balanceOf(address _owner) public view returns (uint256 balance)
  • transfer
    • 進行移轉,當 caller 的 balance 不夠時要丟出例外
    • function transfer(address _to, uint256 _value) public returns (bool success)
  • transferFrom
    • 用在提款流程,允許合約代表你進行 token 移轉,如果_from沒有授權合約的話要拋出例外
    • function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
  • approve
    • 允許_spender從你的帳戶中多次提款,最大值為_value,每次呼叫都會複寫掉_value
    • function approve(address _spender, uint256 _value) public returns (bool success)
  • allowance
    • 返回_spender剩餘可以從_owner中提出的 token 數量
    • function allowance(address _owner, address _spender) public view returns (uint256 remaining)
  • (event) Transfer
    • 當發生移轉時必須被 trigger,就算是 0 也是
    • event Transfer(address indexed _from, address indexed _to, uint256 _value)
  • (event) Approve
    • 在呼叫approve(address _spender, uint256 _value)必須被 trigger
    • event Approval(address indexed _owner, address indexed _spender, uint256 _value)

implementation

  • 開發環境

這裡是用 Remix IDE 進行開發,不用煩惱本地環境配置的問題,如何使用它串接 metamask 請參考本文底部的 references

  • ERC20.sol
//SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.5.0 <0.9.0;
// ----------------------------------------------------------------------------
// EIP-20: ERC-20 Token Standard
// https://eips.ethereum.org/EIPS/eip-20
// -----------------------------------------

interface ERC20Interface {
    function totalSupply() external view returns (uint);
    function balanceOf(address tokenOwner) external view returns (uint balance);
    function transfer(address to, uint tokens) external returns (bool success);

    function allowance(address tokenOwner, address spender) external view returns (uint remaining);
    function approve(address spender, uint tokens) external returns (bool success);
    function transferFrom(address from, address to, uint tokens) external returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}


contract Cryptos is ERC20Interface{
    string public name = "Cryptos";
    string public symbol = "CRPT";
    uint public decimals = 0; //18 is very common
    uint public override totalSupply;

    address public founder;
    mapping(address => uint) public balances;
    // balances[0x1111...] = 100;

    mapping(address => mapping(address => uint)) allowed;
    // allowed[0x111][0x222] = 100;


    constructor(){
        totalSupply = 1000000;
        founder = msg.sender;
        balances[founder] = totalSupply;
    }


    function balanceOf(address tokenOwner) public view override returns (uint balance){
        return balances[tokenOwner];
    }


    function transfer(address to, uint tokens) public override returns(bool success){
        require(balances[msg.sender] >= tokens);

        balances[to] += tokens;
        balances[msg.sender] -= tokens;
        emit Transfer(msg.sender, to, tokens);

        return true;
    }


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


    function approve(address spender, uint tokens) public override returns (bool success){
        require(balances[msg.sender] >= tokens);
        require(tokens > 0);

        allowed[msg.sender][spender] = tokens;

        emit Approval(msg.sender, spender, tokens);
        return true;
    }


    function transferFrom(address from, address to, uint tokens) public override returns (bool success){
         require(allowed[from][msg.sender] >= tokens);
         require(balances[from] >= tokens);

         balances[from] -= tokens;
         allowed[from][msg.sender] -= tokens;
         balances[to] += tokens;

         emit Transfer(from, to, tokens);

         return true;
     }
}

references

cmd + /