どうもこじりょー(@kojiryoinvestor)です。
前回のチュートリアルでは、OpenZeppelinを利用してシンプルなERC20トークンを実装しました。
今回のチュートリアルでは、前回で作成したトークンに追加発行の機能をつけていきます。
前回のチュートリアルの記事をベースに作成しているので、まだ読んでいない場合はひと通り確認した後に進めることをオススメします!
【Truffle5.0対応】シンプルなERC20トークンを作成しよう!
ERC20トークンの追加発行とは?
前回のチュートリアルで作成したERC20トークンでは、デプロイ時に100ST発行した後は、それ以降追加発行することはできませんでした。
トークンの用途によっては、株式のように株を追加発行して資金を集めるようなことができるようにしておきたいでしょう。
OpenZeppelinを利用することで、ERC20トークンを追加発行するコントラクトを簡単に実装することができます。
ERC20Mintable
トークンの追加発行機能を実装するには「ERC20Mintable.sol」を継承します。
「ERC20Mintablel」は「ERC20.sol」と「MinterRole.sol」を継承したコントラクトです。
特定のイーサリアムアカウントにトークンの追加発行の権限を与えたり、指定したアドレスに追加発行したトークンを送ることができます。
pragma solidity ^0.4.24;
import "./ERC20.sol";
import "../../access/roles/MinterRole.sol";
/**
* @title ERC20Mintable
* @dev ERC20 minting logic
*/
contract ERC20Mintable is ERC20, MinterRole {
/**
* @dev Function to mint tokens
* @param to The address that will receive the minted tokens.
* @param value The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(address to, uint256 value) public onlyMinter returns (bool) {
_mint(to, value);
return true;
}
}
「mint()」は引数「to」でトークンを付与するアドレス受け取り、引数「value」で追加発行するトークンの量を受け取ります。
また「mint()」を実行しようとするイーサリアムアカウントに権限があるかどうかを「onlyMinter()」で検証します。
問題なければ「ERC20.sol」のプライベートメソッド「_mint()」を実行して、トークンを追加発行し、指定したアドレスに付与します。
mint()の実行権が付与されるまでの流れ
「ERC20Mintable」をコンストラクタ宣言すると、「ERC20Mintable」が継承している「MinterRole.sol」の「constructor()」が実行されます。
pragma solidity ^0.4.24;
import "../Roles.sol";
contract MinterRole {
using Roles for Roles.Role;
event MinterAdded(address indexed account);
event MinterRemoved(address indexed account);
Roles.Role private minters;
constructor() internal {
_addMinter(msg.sender);
}
途中省略...
function _addMinter(address account) internal {
minters.add(account);
emit MinterAdded(account);
}
以下省略...
}
「MinterRole」のコンストラクタが呼び出されると、プライベート関数の「_addMinter()」を実行します。
constructor() internal {
_addMinter(msg.sender);
}
「_addMinter()」は引数にコントラクトのデプロイを実行したイーサリアムアカウントを受け取り、「Roles.sol」ライブラリの「add()」を実行します。
function _addMinter(address account) internal {
minters.add(account);
emit MinterAdded(account);
}
「Roles.sol」ライブラリの「add()」では、イーサリアムアカウントとboolean(true or false)をmappingで関連づけます。
Roles.sol:
pragma solidity ^0.4.24;
/**
* @title Roles
* @dev Library for managing addresses assigned to a Role.
*/
library Roles {
struct Role {
mapping (address => bool) bearer;
}
/**
* @dev give an account access to this role
*/
function add(Role storage role, address account) internal {
require(account != address(0));
require(!has(role, account));
role.bearer[account] = true;
}
途中省略...
/**
* @dev check if an account has this role
* @return bool
*/
function has(Role storage role, address account)
internal
view
returns (bool)
{
require(account != address(0));
return role.bearer[account];
}
}
以上で、mapping型「bearer」上にboolean型「true」と関連づけられたイーサリアムアカウントは「ERC20Minter」の「mint()」を実行できるようになります。
実装
それではERC20トークンを追加発行できるように拡張していきましょう。
作成したERC20トークンのコントラクト内に「ERC20Mintable.sol」をインポートして継承します。
デプロイ
それではGanacheを起動して、プライベートネットにコントラクトをデプロイします。
コードの完成版はGitHubで公開しています。
https://github.com/kojimaro/ERC20Tutorial
再度コントラクトをデプロイする際は「–reset」オプションを付けます。
デプロイに成功したらプライベートネットのノードにアクセスしましょう。
$ truffle migrate --reset
$ truffle console

トークンを追加発行する
トークン残高の確認
プライベートネットのノードにアクセスしたら、Ganacheが生成したイーサリアムアカウントを取得します。
下記のコードを実行して、イーサリアムアカウントをaccounts変数に格納します。
web3.eth.getAccounts().then(a=>accounts=a)

今回はトークンの付与がされていないaccounts[1]に50ST付与していきます。
まずはaccounts[1]がトークンを持っていないことを確認していきます。
はじめに以下のコードを実行して、コントラクトのインスタンスを生成してinstance変数に格納します。
SimpleToken.deployed().then(i=>instance=i)
下記のコードを実行してトークン名を表示できていればインスタンスの生成に成功しています。
instance.name()

続いて下記のコードを実行して、accounts[1]のトークン残高を取得します。
instance.balanceOf(accounts[1]).then(b=>balance=b)
Ether(10^18)の単位に変換し、トークンの残高を表示します。
web3.utils.fromWei(balance, "ether")

ついでにtotalSupply(発行しているトークンの合計量)も確認しておきましょう。
instance.totalSupply().then(t=>total=t)
web3.utils.fromWei(total, "ether")

accounts[1]のトークン残高は0であり、現在発行されているトークンの量は、初回にaccounts[0]に対して付与された100STのみということがわかりました。
mint()の実行
それでは次にaccounts[0]にmint()を実行する権限があるかどうかを確認します。
instance.isMinter(accounts[0])
Trueと表示されればmint()を実行する権限が付与されています。

続いて、数が大きいので50ST(10^18)をBN.jsのインスタンスに変換し、value変数に格納します。
value = web3.utils.toBN(50*(10**18))
mint()を実行し、トークンを50ST分追加発行してaccounts[1]に付与します。
instance.mint(accounts[1], value)
再度accounts[1]のトークン残高を取得して、Ehter単位に変換してみます。
instance.balanceOf(accounts[1]).then(b=>balance=b)
web3.utils.fromWei(balance, "ether")

accounts[1]に50ST付与されていることがわかります。
再度totalSupplyも確認してみましょう。
instance.totalSupply().then(t=>total=t)
web3.utils.fromWei(total, "ether")

mint()を実行することでトークンを追加発行できることが確認できました。
おわりに
以上、OpenZeppelinを利用したERC20トークンの追加発行機能を追加するチュートリアルでした。
次回は発行したトークンを処分する「burn()」を解説していきます。
[…] ERC20トークンの追加発行機能を実装しよう!【OpenZeppelin2.0】 […]
[…] 2. ERC20トークンの追加発行機能を実装しよう!【OpenZeppelin2.0】 […]