2019.01.07

2018/12 時点でのOpenZeppelin について

こんにちは。次世代システム研究室のT.M. です。

はじめに

Ethereum でトークンを発行するとなると一般的にはERC20 の仕様のものにするかと思います。さらに、その実装に関しては、OpenZeppelin のものを利用することが多いかと思います。先日、2.0 の新しいバージョンがリリースされたように、OpenZeppelin は活発に開発がなされており、変化しています。私は、2年ほど前にコードを読んだきりであり、久しぶりにコードを読みなおすと大きく変わっており、とても驚きました。当時は、StandardToken.sol を継承して、トークンのコントラクトを作成していたのですが、現在ではStandardToken.sol はなくなっていました。そこで、本稿では現在のERC20 のトークンの実装方法およびトークンに付与することのできる機能について解説します。

標準のERC20トークン

実装

以下のようにERC20.sol を継承して、トークンを実装する。

pragma solidity >=0.4.21 <0.6.0;
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20.sol';

contract MyToken is ERC20 {

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

以前は、コンストラクタでbalances のフィールドの値を直接操作して、初期発行のトークン量を操作していたが、_mint というinternal な関数を利用して初期トークンを発行するように変わっている。

動作確認

> token.totalSupply();
1000
> token.balanceOf('__DEPLOY_ADDRESS__');
1000
> token.transfer('__TO_ADDRESS__', 100);
> token.balanceOf('__DEPLOY_ADDRESS__');
900
> token.balanceOf('__TO_ADDRESS__');
100

totalSupply によりトークンの総量を見ることができる。今回は、最初に1000 トークン発行したため、1000 という値が返ってきた。

balanceOf によりトークンの所持量を見ることができる。初期トークンはコントラクトをデプロイしたアカウントに付与されたので、1000 という値が返ってきた。

transfer をすることで、呼び出しアカウントから引数のアカウントに対して100 トークンが移動した。

Detailed なERC20 トークン

ERC20 トークンに対して、name やsymbol, decimals を追加されたトークンである。

実装

ERC20Detailed.sol を継承して、トークンを実装する。

pragma solidity >=0.4.21 <0.6.0;
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol';

contract MyToken is ERC20Detailed{

  constructor() ERC20Detailed("MYTOKEN", "MTK", 18) public {
    _mint(msg.sender, 1000);
  }
}

コンストラクタにおいて、name やsymbol, decimals を設定する。

動作確認

> token.name();
'MYTOKEN'
> token.symbol();
'MTK'
> token.decimals();
18

設定されたname やsymbol, decimals を見ることができる。

Burnable なERC20 トークン

ERC20 トークンに対して、burn (トークン削除) する機能が付与されたトークンである。

実装

ERC20Burnable.sol を継承して、トークンを実装する。

pragma solidity >=0.4.21 <0.6.0;
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol';

contract MyToken is ERC20Burnable {

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

継承するコントラクトがERC20.sol ではなく、ERC20Burnable.sol であること以外に変更はない。

動作確認

> token.totalSupply();
1000
> token.balanceOf('__DEPLOY_ADDRESS__');
1000
> token.burn(100);
> token.totalSupply();
900
> token.balanceOf('__DEPLOY_ADDRESS__');
900

burn をすることで、burn をしたアカウントから100 トークンが削除された。

Mitable なERC20 トークン

ERC20 トークンに対して、mint (トークン発行) する機能が付与されたトークンである。

実装

ERC20Mintable.sol を継承して、トークンを実装する。

pragma solidity >=0.4.21 <0.6.0;
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol';

contract MyToken is ERC20Mintable {

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

継承するコントラクトがERC20.sol ではなく、ERC20Mintable.sol であること以外に変更はない。

動作確認

> token.totalSupply();
1000
> token.balanceOf('__DEPLOY_ADDRESS__');
1000
> token.mint('__DEPLOY_ADDRESS__', 100);
> token.totalSupply();
1100
> token.balanceOf('__DEPLOY_ADDRESS__');
1100

mint をすることで、100 トークンが新しく発行された。

コントラクトをデプロイしたアカウントがminter (= mint することのできるアカウント)として設定される。minter はminter を追加することができる。

Capped なERC20 トークン

ERC20 トークンに対して、トークンの発行上限量が設定されたトークンである。

実装

ERC20Capped.sol を継承して、トークンを実装する。

pragma solidity >=0.4.21 <0.6.0;
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Capped.sol';

contract MyToken is ERC20Capped {

  constructor() ERC20Capped(2000) public {
    _mint(msg.sender, 1000);
  }
}

コンストラクタにおいて、発行上限量を設定する。

動作確認

> token.totalSupply();
1000
> token.mint('__DEPLOY_ADDRESS__', 1000);
> token.totalSupply();
2000
> token.mint('__DEPLOY_ADDRESS__', 1);
Error: Transaction:....

設定された発行上限までは、トークンを発行することができるが、それを超えてトークンを発行することができない。

Pausable なERC20 トークン

ERC20 トークンに対して、トークンの移動を一時的に禁止にすることができるトークンである。

実装

ERC20Pausable.sol を継承して、トークンを実装する。

pragma solidity >=0.4.21 <0.6.0;
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Pausable.sol';

contract MyToken is ERC20Pausable {

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

継承するコントラクトがERC20.sol ではなく、ERC20Pausable.sol であること以外に変更はない。

動作確認

> token.pause();
> token.transfer('__TO_ADDRESS__', 100);
Error: Transaction:....
> token.unpause();
> token.transfer('__TO_ADDRESS__', 100);

pause をすることで、transfer が実行できなくなり、unpause でtransfer が実行できるように戻ります。

さいごに

OpenZeppelin のコードを久しぶりに読み直すと、大きく実装が変わっていました。今回紹介したのはほんの一部であり、他にもERC721 トークンのコントラクトであったり、crowdsale に関するコントラクトなど様々なコントラクトが提供されている。OpenZeppelin はコントラクトを開発するうえでとても参考になるライブラリですので、今後も参考にしていく必要があります。

次世代システム研究室では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。インフラ設計、構築経験者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。

皆さんのご応募をお待ちしています。