@mizumotokのブログ

テクノロジー、投資、書評、映画、筋トレなどについて

意外と簡単!ERC20トークンの作り方

ICOに使われるトークンはほとんどがイーサリアム上のERC20トークンと呼ばれるものです。統一規格になっているためERC20トークン対応のウォレットであれば、誰が発行したトークンでもウォレットで使用することができます。
ERC20トークンを作るのは非常に簡単です。試してみましょう。

f:id:mizumotok:20181128163655p:plain

ERC20トークンについて

ERC20トークンとは

イーサリアムのスマートコントラクトを使えば、オリジナルのトークン/仮想通貨を発行することができます。発行者が自由に実装してもいいのですが、トークンの使い方(API)がそれぞれ異なってしまい、ウォレットの対応やユーザの使用ハードルが高くなってしまいます。
トークンに求められる機能は送金や残高確認等、共通しているものが多いです。そこでイーサリアムの統一規格としてERC20トークンというものができました。ERC20に準拠することで送金や残高確認のやり方は決まってきますので、ERC20対応ウォレットであれば何もせずに新しいトークンを管理することができるようになります。
ICOに使われるトークンはほとんどがERC20トークンです。

ブロックチェーンビジネスとICOのフィジビリティスタディ

ブロックチェーンビジネスとICOのフィジビリティスタディ

ERC20で定められたインターフェース

EIP20(EIPとはイーサリアムを良くするための提案)によると、ERC20トークンは以下のインターフェースを持つスマートコントラクトを作成すればよいです。

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 transfer(address to, uint tokens) public returns (bool success); // トークンの送金
function approve(address spender, uint tokens) public returns (bool success); // トークンの代理送金を許可
function transferFrom(address from, address to, uint tokens) public returns (bool success); // 第3者によるトークンの代理送金

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

また必須ではないですが、以下のインターフェースを含めることが推奨されています。ほとんどのウォレットで使用されます。

function name() public view returns (string); // トークン名
function symbol() public view returns (string); // トークンのシンボル名
function decimals() public view returns (uint8); // 小数点以下の桁数

ERC20トークンの実装

OpenZeppelin

f:id:mizumotok:20181128163942p:plain

OpenZeppelinとは、イーサリアム上で使用できるスマートコントラクトのSolidityライブラリです。オープンソースで開発されており、誰でも再利用することができます。
ERC20トークンについても役に立つライブラリが提供されています。
OpenZeppelinを使用するにはnpmでインストールするだけです。

npm install openzeppelin-solidity

github.com

最小限なERC20トーク

OpenZeppelinではERC20のインターフェースをすべて実装したライブラリが用意されています。コンストラクタだけ追加すれば、すぐにERC20トークンを作成することができます。

pragma solidity ^0.4.24;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20.sol';


contract MyToken is ERC20 {
  uint256 constant private INITIAL_SUPPLY = 1000;

  constructor() public {
    _mint(msg.sender, INITIAL_SUPPLY);
  }
}

コンストラクタでトークンのオーナアカウントに1000トークンを発行しています。
_mintはinternalで実装されている関数で、マイニングでトークンを獲得することを想定しています。すなわちトータルトークン数を増やし、その分は指定したアカウントに配布します。
これをデプロイすれば、総量1000のトークンが使用できるようになります。
OpenZeppelinのERC20ソースコードも簡単なので読めば理解が深まります。

標準的なERC20トーク

先ほど作ったトークンにはトークン名やシンボルが設定されていません。EIP20で推奨されていますし、ほとんどのウォレットで使用されますでの、実装しておきましょう。
OpenZeppelinのERC20Detailedというものが使用できます。

pragma solidity ^0.4.24;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol';


contract MyToken is ERC20, ERC20Detailed {
  string public _name = "MyToken";
  string public _symbol = "MT";
  uint8 public _decimals = 18;

  uint256 constant private INITIAL_SUPPLY = 1000e18;

  constructor()
    public
    ERC20Detailed( _name, _symbol, _decimals)
  {
    _mint(msg.sender, INITIAL_SUPPLY);
  }
}

トークン名とシンボルを指定しています。また、小数点以下を18としたので、初期配布トークン数を1000*1018としています。

便利な追加機能

OpenZeppelinでは基本機能以外にもERC20トークンに付加すると便利な機能をライブラリとして提供してくれています。

Mintable(マイニング)

先ほど出てきた_mintはinternalな関数でしたが、MinterRoleを持っているアカウントがトークンの総量を増やして、その分を任意のアカウントに配布することができます。

pragma solidity ^0.4.24;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol';


contract MyToken is ERC20Mintable, ERC20Detailed {
  string public _name = "MyToken";
  string public _symbol = "MT";
  uint8 public _decimals = 18;

  uint256 constant private INITIAL_SUPPLY = 1000e18;

  constructor()
    public
    ERC20Detailed( _name, _symbol, _decimals)
    ERC20Mintable()
  {
    _mint(msg.sender, INITIAL_SUPPLY);
  }
}

ERC20Mintableを継承するだけでmint関数が使えるようになります。

function mint(address to, uint256 value) public onlyMinter returns (bool) 

MinterRoleはデフォルトではトークンのオーナーアカウントですが、addMint関数を使って他のアカウントに権限を与えることができます。

Burnable(バーン)

バーンとはトークンを消失させることです。その分トークンの総量も減ります。

pragma solidity ^0.4.24;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol';


contract MyToken is ERC20Burnable, ERC20Detailed {
  string public _name = "MyToken";
  string public _symbol = "MT";
  uint8 public _decimals = 18;

  uint256 constant private INITIAL_SUPPLY = 1000e18;

  constructor()
    public
    ERC20Detailed( _name, _symbol, _decimals)
    ERC20Burnable()
  {
    _mint(msg.sender, INITIAL_SUPPLY);
  }
}

ERC20Burnableを継承するだけで、以下の関数が使用できるようになります。

function burn(uint256 value) public;
function burnFrom(address from, uint256 value) public;

burnは自分のアカウントのトークンをバーンします。burnFromは事前にapproveされたアカウントがapproveしたアカウントのトークンをapproveで指定したトークン量だけバーンすることができます。

Capped(上限固定)

Mintableにすると上限なくトークンを増やすことができますが、最大トークン量を制限したいという要求もあります。その場合はCappedが使用できます。

pragma solidity ^0.4.24;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Capped.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol';


contract MyToken is ERC20Capped, ERC20Detailed {
  string public _name = "MyToken";
  string public _symbol = "MT";
  uint8 public _decimals = 18;

  uint256 constant private INITIAL_SUPPLY = 1000e18;
  uint256 constant private SUPPLY_LIMIT = 100000e18;

  constructor()
    public
    ERC20Detailed( _name, _symbol, _decimals)
    ERC20Capped(SUPPLY_LIMIT)
  {
    _mint(msg.sender, INITIAL_SUPPLY);
  }
}

ERC20Cappedを継承するだけで、上限値を返す以下の関数が使用できるようになります。

function cap() public view returns (uint256)

ERC20CappedはERC20Mintableから継承されているため、CappedにはMintableの機能を含みます。

Pausable(一時停止)

トークンの送金や第3者への送金許可を一時的に使用できないようにします。

contract MyToken is ERC20Pausable, ERC20Detailed {
  string public _name = "MyToken";
  string public _symbol = "MT";
  uint8 public _decimals = 18;

  uint256 constant private INITIAL_SUPPLY = 1000e18;

  constructor()
    public
    ERC20Detailed( _name, _symbol, _decimals)
    ERC20Pausable()
  {
    _mint(msg.sender, INITIAL_SUPPLY);
  }
}

ERC20Pausableを使用することでPauserRoleを持っているアカウントがトークンの一時停止、再開をすることができるようになります。

function pause() public onlyPauser whenNotPaused;
function unpause() public onlyPauser whenPaused;

PauserRoleはデフォルトではトークンのオーナーアカウントですが、addPauser関数を使って他のアカウントに権限を与えることができます。

ユーティリティ

OpenZeppelinではトークンの機能ではないのですが、他のスマートコントラクトからERC20トークンを操作さするためのユーティリティが用意されています。

SafeERC20

送金時や第3者への送金許可時に送金可能かどうかチェックします。送金できない場合はthrowされます。
SafeERC20では以下のinternalな関数が実装されています。

function safeTransfer(IERC20 token, address to, uint256 value) internal;
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal;
function safeApprove(IERC20 token, address spender, uint256 value) internal;
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal;
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal;
TokenTimelock

一定期間経過後にトークンを配布したいという要求があります。その場合はTokenTimelockを使用することができます。

pragma solidity ^0.4.24;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20.sol';
import './MyToken.sol';


contract MyTokenTimelock is TokenTimelock {
  constructor(MyToken token, address beneficiaryAddress, uint256 releaseTime)
    public
    TokenTimelock(token, beneficiaryAddress, releaseTime)
  {}
}

release関数を使用したときに、releaseTimeで指定した時刻以降であればtokenのアドレスにあるトークン残高がbeneficiaryAddressのアドレスに配布されます。

function release() public;

ERC721

ERC721トークンとは

イーサリアム上のトークンにはさまざまな規格が提案されていますが、ERC20に並んで有名なのがERC721です。
ERC20はお金と同じようにトークン1つ1つは同じ価値を持ちます。一方でERC721はNFT(Non-Fungible Token)という特徴があり、トークン1つ1つが違うものとみなされます。不動産や宝石のようなものです。
CryptoKittiesというアプリケーションで使われて有名になりました。CryptoKittiesでは仮想猫1匹がトークン1つに該当し、各猫は異なる存在であり、価値も異なります。

f:id:mizumotok:20181128163738p:plain

ERC721トークンの実装

ERC721トークンを作るのも簡単です。

pragma solidity ^0.4.24;

import 'openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol';
import 'openzeppelin-solidity/contracts/token/ERC721/ERC721Mintable.sol';


contract MyNFT is ERC721Full, ERC721Mintable {
  constructor() ERC721Full("MyNFT", "MNFT") public {
  }
}

まとめ

ERC20トークンもERC721トークンも簡単に作れることが分かりました。これらのトークンを使用した魅力的なサービスを構築することができればICOで資金調達することも可能になります。

ブロックチェーンアプリケーション開発の教科書

ブロックチェーンアプリケーション開発の教科書

はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)

はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)

ブロックチェーンビジネスとICOのフィジビリティスタディ

ブロックチェーンビジネスとICOのフィジビリティスタディ