ERC-20トークンのスマート・コントラクトを理解する
イーサリアム上で最も重要なスマート・コントラクト標準の1つはERC-20として知られており、イーサリアム・ブロックチェーン上のすべてのスマート・コントラクトで代替可能トークンを実装するための技術標準として普及しています。
ERC-20は、すべての代替可能なイーサリアムトークンが準拠すべき共通のルールリストを定義しています。その結果、このトークン標準により、あらゆるタイプの開発者が、新しいトークンがより広範なイーサリアムシステム内でどのように機能するかを正確に予測できるようになります。トークンがルールに従っている限り、新しいトークンがリリースされるたびに新しいプロジェクトを最初からやり直す必要がないことがわかっているため、開発者の作業が簡素化され、容易になります。
以下は、ERC-20が実装しなければならない関数をインターフェースとして提示したものです。インターフェースとは何かわからない場合は、Solidityでのオブジェクト指向プログラミング(OOP) (opens in a new tab)に関する記事を確認してください。
pragma solidity ^0.6.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
以下は、各関数が何のためのものかを1行ずつ説明したものです。この後、ERC-20トークンのシンプルな実装を紹介します。
ゲッター
function totalSupply() external view returns (uint256);
存在するトークンの総量を返します。この関数はゲッターであり、コントラクトの状態を変更しません。Solidityには浮動小数点数がないことに注意してください。そのため、ほとんどのトークンは18桁の小数を採用しており、1トークンに対して 1000000000000000000 のように総供給量やその他の結果を返します。すべてのトークンが18桁の小数を持っているわけではないため、トークンを扱う際にはこの点に十分注意する必要があります。
function balanceOf(address account) external view returns (uint256);
アドレス (account) が所有するトークンの量を返します。この関数はゲッターであり、コントラクトの状態を変更しません。
function allowance(address owner, address spender) external view returns (uint256);
ERC-20標準では、あるアドレスが別のアドレスに対して、自身からトークンを引き出すためのアローワンスを与えることができます。このゲッターは、spender が owner に代わって消費することを許可されているトークンの残数を返します。この関数はゲッターであり、コントラクトの状態を変更せず、デフォルトでは0を返す必要があります。
関数
function transfer(address recipient, uint256 amount) external returns (bool);
関数を呼び出したアドレス (msg.sender) から受信者のアドレスへ、amount 分のトークンを送金します。この関数は、後述する Transfer イベントを発行します。送金が可能であった場合は true を返します。
function approve(address spender, uint256 amount) external returns (bool);
関数呼び出し元 (msg.sender) の残高から、spender が送金できる allowance を設定します。この関数は Approval イベントを発行します。関数は、アローワンスが正常に設定されたかどうかを返します。
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
アローワンスの仕組みを使用して、sender から recipient へ amount 分のトークンを送金します。その後、呼び出し元のアローワンスから amount が差し引かれます。この関数は Transfer イベントを発行します。
イベント
event Transfer(address indexed from, address indexed to, uint256 value);
このイベントは、トークンの量 (value) が from アドレスから to アドレスに送金されたときに発行されます。
新しいトークンをミンティングする場合、通常は 0x00..0000 アドレスを from とした送金になり、トークンをバーンする場合は 0x00..0000 アドレスを to とした送金になります。
event Approval(address indexed owner, address indexed spender, uint256 value);
このイベントは、トークンの量 (value) が owner によって承認され、spender が使用できるようになったときに発行されます。
ERC-20トークンの基本的な実装
以下は、独自のERC-20トークンのベースとなる最もシンプルなコードです。
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract ERC20Basic is IERC20 {
string public constant name = "ERC20Basic";
string public constant symbol = "ERC";
uint8 public constant decimals = 18;
mapping(address => uint256) balances;
mapping(address => mapping (address => uint256)) allowed;
uint256 totalSupply_ = 10 ether;
constructor() {
balances[msg.sender] = totalSupply_;
}
function totalSupply() public override view returns (uint256) {
return totalSupply_;
}
function balanceOf(address tokenOwner) public override view returns (uint256) {
return balances[tokenOwner];
}
function transfer(address receiver, uint256 numTokens) public override returns (bool) {
require(numTokens <= balances[msg.sender]);
balances[msg.sender] = balances[msg.sender]-numTokens;
balances[receiver] = balances[receiver]+numTokens;
emit Transfer(msg.sender, receiver, numTokens);
return true;
}
function approve(address delegate, uint256 numTokens) public override returns (bool) {
allowed[msg.sender][delegate] = numTokens;
emit Approval(msg.sender, delegate, numTokens);
return true;
}
function allowance(address owner, address delegate) public override view returns (uint) {
return allowed[owner][delegate];
}
function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
require(numTokens <= balances[owner]);
require(numTokens <= allowed[owner][msg.sender]);
balances[owner] = balances[owner]-numTokens;
allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;
balances[buyer] = balances[buyer]+numTokens;
emit Transfer(owner, buyer, numTokens);
return true;
}
}
ERC-20トークン標準のもう1つの優れた実装として、オープンツェッペリンのERC-20実装 (opens in a new tab)があります。