LoginSignup
4
3

More than 5 years have passed since last update.

ERC20トークンの実装

Posted at

概要

 ERC20トークンはethereum上で利用できるトークンの標準規格。これに準拠しているトークンは Ethereumウォレット、および同じ基準を使用する他のクライアントまたはコントラクトと互換性がある。今回は標準的なトークンの機能を実装したTokenERC20コントラクトとトークンの中央管理者を設定するowendコントラクトを作成し、それらを継承したより多くの機能を持つトークンを実装する。
 

owenedコントラクト

 トークンの中央管理者を設定する。modifier onlyOwnerによって関数の実行可能者をownerに限定することができる。

ERC20Token.sol
contract owned {
    address public owner;
    //コンストラクタ、  ownerを設定
    function owned() public {
        owner = msg.sender;
    }
    //ownerのみ実行できる
    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }
    //  ownerの交代
    function transferOwnership(address newOwner) onlyOwner public {
        owner = newOwner;
    }
}

TokenERC20コントラクト

ERC20Token.sol
contract TokenERC20 {
    // トークンの名前、シンボル、供給量
    string public name;
    string public symbol;
    //小数点以下何桁までか、18から変えないほうがいい
    uint8 public decimals = 18;
    uint256 public totalSupply;

    //publicはブロックチェーン上の誰でもアクセスできることを示す
    //アカウントのアドレスと残高
    mapping (address => uint256) public balanceOf;
    //アカウント毎にそれぞれのアカウントが自由に使用可能なトークン量を定める
    mapping (address => mapping (address => uint256)) public allowance;

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

    //コンストラクタ
    function TokenERC20(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
        name = tokenName;
        symbol = tokenSymbol;
    }

    //トークンの転送を行う、internalは内部関数を示しコントラクト内からしかアクセスできなくする
    function _transfer(address _from, address _to, uint _value) internal {
        //inputの検証
       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が行われたことを知らせる
        Transfer(_from, _to, _value);
        // 転送が成功したかチェック
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    //トランザクションを送ったアカウントからトークンを転送
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    //トランザクションを送ったアカウントがほかのアカウントのトークンを転送
    function transferFrom(address _from,adress _to,uint256 _value) public returns (bool success) {
      require(_value <= allowance[_from][msg.sender]);
      allowance[_from][msg.sender] -= _value;
      _transfer(_from,_to,_value);
    }

    //他のアカウントに許可するトークンの使用量を設定
    function approve(address _spender,uint256 _value) public returns(bool success) {
      allowance[msg.sender][_spender] = _value;
      return true;
    }
}

トークン例

//継承、owened,TokenERC20にアクセス可能になる
contract MyAdvancedToken is owned, TokenERC20 {
    //売値買値
    uint256 public sellPrice;
    uint256 public buyPrice;
    //アカウントがトランザクションを送るために最低限維持すべきether
    uint minBalanceForAccounts;
    //アカウントの凍結の有無
    mapping (address => bool) public frozenAccount;

    event FrozenFunds(address target, bool frozen);


    //ownerのみ実行可能、1finney=0.001ether
    function setMinBalance(uint minimumBalanceInFinney) onlyOwner {
        minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
    }

    //コンストラクタ
    function MyAdvancedToken(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) TokenERC20(initialSupply, tokenName, tokenSymbol) public {}

    //トークンの転送を行う内部関数
    function _transfer(address _from, address _to, uint _value) internal {
        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);
    }


    function transfer(address _to, uint256 _value) {
      if(msg.sender.balance < minBalanceForAccounts){
        //minBalanceForAccountsに足りない分トークンを売る
        sell((minBalanceForAccounts - msg.sender.balance) / sellPrice);
      }
      _transfer(msg.sender, _to, _value);
      }

    //ownerのみ実行可能、トークンの供給量の変更
    function mintToken(address target, uint256 mintedAmount) onlyOwner public {
        balanceOf[target] += mintedAmount;
        totalSupply += mintedAmount;
        Transfer(0, this, mintedAmount);
        Transfer(this, target, mintedAmount);
    }

    //ownerのみ実行可能、アカウントの凍結
    function freezeAccount(address target, bool freeze) onlyOwner public {
        frozenAccount[target] = freeze;
        FrozenFunds(target, freeze);
    }

    //ownerのみ実行可能、売値と買値を設定、Gasを消費するため余り頻繁には変えたくない
    function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
        sellPrice = newSellPrice;
        buyPrice = newBuyPrice;
    }

    //トークンをetherで買う、payableを指定してetherを受け入れ可能にする
    function buy() payable public {
        uint amount = msg.value / buyPrice;
        _transfer(this, msg.sender, amount);
    }

    //トークンを売る
    function sell(uint256 amount) public {
        //コントラクトが十分なetherを持っているか
        require(this.balance >= amount * sellPrice);
        _transfer(msg.sender, this, amount);
        msg.sender.transfer(amount * sellPrice);
    }

    //マイニングの報酬に1トークンを与える
    function giveBlockReward() {
      balanceOf[block.coinbase] += 1;
    }

    uint currentChallenge = 1;
    //立方根を正しく求められたアカウントに1トークン与える
    function rewardMathGeniuses(uint answerToCurrentReward, uint nextChallenge) {
      require(answerToCurrentReward**3 == currentChallenge);
      balanceOf[msg.sender] += 1;
      //次の問題を設定
      currentChallenge = nextChallenge;
    }
}

参考

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3