ERC-20日本語訳と解説
ERC-20: Token Standard
はじめに
dApp開発などをしているときに、ちょっと不安になったときの参照用としてご利用ください。原文の抄訳と筆者のコメントを混ぜながら書きました。
間違いは優しくご指摘いただけると幸いです。
推奨知識
- Ethereumの利用経験
- 静的型付け言語の経験
ERCって?
初めての投稿なので一応説明します。
ERCとはざっくりいうと、Ethereum上のアプリケーションに関するお約束事リストのことです。主にスマートコントラクトのインターフェースを定義するものが多いです。
ERC-20についていうと、Ethereum上の"トークン"がこの規格に基づいているために、DEX(分散型取引所)のようなスマートコントラクトを可能にしています。
EIPというEthereumの標準規格の1つのカテゴリーなので、「ERC-20」と「EIP-20」の意味は全く同じです。
以下本題
一言でいうと
「スマートコントラクトでトークンを表現するときはこれらを満たすように書きましょう。」
概要
スマートコントラクト内のトークンの標準的なAPIを実装するための規格。
トークンのTransfer(移動)と第三者にそれを許可する仕組みを提供する。
仕様
メソッド(コントラクトというクラスに属する関数)
name (optional)
function name() public view returns (string)
トークンの名前を返す。(例:”MyToken”)
これが実装されていなくても、この規格に対応するプログラムはエラーを出してはいけない。以下、”optional”はこの意味を持つ。
symbol (optional)
function symbol() public view returns (string)
トークンのシンボルを返す。(例:”HIX”)
decimals (optional)
function decimals() public view returns (uint8)
トークンのゼロの数を返す
8
の場合、そのユーザー表現は実際のトークン量を10^8で割った値になる。
ユーザー表現とは、ウォレットやアプリケーションで表示されるトークンの量のこと。
EIPの仕様の文面では、トークンの量とはユーザー表現ではなく、実際の整数値とする。
totalSupply
function totalSupply() public view returns (uint256)
トークンの総供給量を返す。
balanceOf
function balanceOf(address _owner) public view returns (uint256 balance)
アドレス _owner
の保有量を返す。
transfer
function transfer(address _to, uint256 _value) public returns (bool success)
_value
の分だけアドレス_to
にトークンを移動させる。このとき、Transfer
イベントを発火しなくてはいけない。この関数は呼び出し側の保有量が十分でないときにエラーを投げるべきである。
注)_value
が0でもTransfer
イベントを発火しなくてはいけない。
transferFrom
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
transferにトークンの送り主アドレスである_from
を追加する。
これは自分のアドレスに代わって他のコントラクトがトークンを移動するときに利用される。これは事前に送信者がその代理のコントラクトを認証していない場合はエラーを投げるべきである。その認証方法が次の関数である。
approve
function approve(address _spender, uint256 _value) public returns (bool success)
_spender
に自分のアカウントからのトークンの引き出しを_value
分まで許可する。
同じ_spender
でもう一度この関数が呼ばれた場合、引き出し上限を_value
にアップデートする。(追加するわけではない)
**注意)ここで説明された攻撃を避けるために、引き出し上限をアップデートするときは一度それを0
にするべき。
もしくは、その説明文にあるようにfunction approve(address _spender, uint256 _currentValue, uint256 _value) returns (bool success)
とそれに対応するイベントを追加で実装すべきである
allowance
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
_spender
が現在_owner
から引き出すことができるトークンの最大量を返す。
イベント (主にフロントエンドやテストの際にキャッチされる情報)
Transfer
event Transfer(address indexed _from, address indexed _to, uint256 _value)
トークンが移動したときに発火されなくてはいけない。移動する量がゼロのときでも。
新しくトークンを生成するときは_from
を0x0
としてこのイベントを発火すべきである。
Approval
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
approve(address _spender, uint256 _value)
がtrue
を返すときに必ず発火しなくてはいけない。
OpenZeppelinによる実装がよく使われる
IERC20.sol
以上の仕様に過不足なくインターフェースを定義している。
仕様とは違って、引数名にアンダースコアのプレフィックスがない。
ERC20.sol
以上の仕様を満たす実装で、さらに以下の追加要素を持つ。
- increaseAllowance()やdecreaseAllowance()による第三者トークン最大使用量のアトミックな変更。
- _mint(), _burn()というinternal関数。mintは新しく
totalSupply
を増やすことで、burnは減らすことである。この機能を有効化するにはコントラクト作成ウィザードを利用するといい。 - トークンの移動の前後に呼び出されるフック
- 拡張系
他にもPermit, Votes, Flash Mintingなどの興味深い拡張があるが割愛する。OpenZeppelinによる実装については、開発の際に集中的に調べて継承するものを選ぶというアプローチになるだろう。
小ネタ
Standadized_Contract_APIsで最初期のコントラクト実装の標準化に対するアプローチをみることができる。
Vitalik氏による最初のコミットはEthereumが稼働開始した2015/7/30の1ヶ月半前で、当初から彼の頭には今でいうDeFi, DEX, ENSの構想があったことが伺える。天才。
Discussion