こんにちは!CryptoGamesというブロックチェーンゲーム企業でエンジニアをしているかるでねです!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。
このブログ以外でも情報発信しているので、よければ他の記事も見ていってください。
https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58
今回は「ERC20」について1からわかりやすく解説していきます!
「ERC20」がわからなかったり、なんとなく理解している非エンジニアやこれから「ERC20」を理解しようとしているエンジニアの方向けの記事になります。
丁寧に解説している分だいぶ長くなってしまっているので時間があるときに一気に読んでみるのをお勧めします。
それでは早速中身を見ていきましょう!
ERC20とは
統一されたトークン規格
ERC20とは、「Ethereumブロックチェーンで動作する統一されたトークン規格の1つ」です。
「統一されたトークンの規格」というのは、「共通のルールを持っているトークン」という事です。
共通のルールを持っていないと、トークンごとにウォレットが必要になります。
新しいトークンを触るごとにウォレットを作っていては、だいぶめんどくさいですよね。
そこでERC20というトークン規格に沿って設計することで1つのウォレットで管理できるようになります。
また、ERC20は「代替性トークン」とも呼ばれています。
「代替性トークン」とは、あるトークンと同じ価値を持つ他のトークンと交換できるということです。
反対に交換できないのでNFTを代表する「非代替性トークン」です。
ICOへ参加しやすくなる
ICOとは、「イニシャル・コイン・オファリング(Initial Coin offering )」の略称で、仮想通貨を新規発行して資金調達する方法の1つです。
手順としては以下になります。
手順
- あるプロジェクトが新規仮想通貨の開発目的や方針を公開。
- 賛同する投資家や支援者が、既存の仮想通貨で新たに発行された仮想通貨を購入する。
- プロジェクト側は集めた資金でプロジェクトやサービスの開発を行う。
- 投資家や支援者はプロジェクトが提供するサービスを利用したり、仮想通貨の価格上昇により資産を増やすことができる。
これがICOです。
では、これがERC20にどう関係してくるのでしょうか?
先ほども述べたようにERC20は「共通のルールを持っているトークン」という特徴を持っています。
それによりウォレットを新たに作る仮想通貨ごとに用意する必要がありません。
そのため、既存のウォレットを使用できるため、ICOに参加するハードルが下がります。
ちなみにERC20を扱えるウォレットとして代表的なものとして、「Metamask」や「Trust Wallet」、「MyEtherWallet」などがあります。
誰でも作成可能
ERC20トークンは誰でも作ることができます。
そのため現在存在するERC20トークンの種類はめちゃくちゃ多いです。
上記から現在のERC20トークンの種類を確認できるのですが、現在(2022年12月31日)では73万5000種類のERC20トークンがあります(年末も年末にこの記事を書いています)。
追加機能を実装可能
この章の前半で説明したように、ERC20には統一された規格があります。
これはあくまで最低限の機能は統一された規格で設計されています。
そのため、トークンごとに追加機能を実装することが可能です。
新規発行するERC20トークンごとに以下のように役割が異なってくるため、役割に応じて追加機能を実装することが可能になります。
ステーブルコイン
ドルや日本円などの法定通貨や市場で取引されるコモディティ(金やプラチナなど)などの価格に連動するように設計された暗号通貨。
DeFiトークン
分散型金融(DeFi)プロトコルを支援するトークン。
スマートコントラクトで管理されているため中央管理者が存在しない。
仮想通貨を預け入れて利息を得たり、流動性を提供して独自トークンを得る、仮想通貨を貸し出して利回りを得るなどして稼ぐことができる。
ユーティリティトークン
特定のコミュニティやサービスなどを利用するにあたり実用性があるトークンのこと。
具体的には以下のような用途が想定される。
用途
- サービス内で物を購入したり利用料を支払う。
- 利用者の行動に対してインセンティブを支払う。
- 所有することでコミュニティの意思決定に関われる。
- 保有している量や期間に応じて報酬が発生する。
代表的なERC20トークン
この章では代表的なERC20トークンについて一覧にしていきます。
Tether(USDT)
Binance Coin(BNB)
USD Coin(USDC)
DAI(DAI)
Basic Attention Token(BAT)
https://basicattentiontoken.org/
Polygon(MATIC)
Shiba Inu(SHIB)
Uniswap(UNI)
ChainLink(LINK)
その他
ここまで紹介してきたトークンはERC20トークンのほんの一部です。
他にもたくさんありますがキリがないため、他にどんなERC20トークンがあるのか以下のサイトで確認してください。
https://www.owl-coin.com/tags/erc20
EIP20
EIPとは
前章までERC20といってきましたが、EIP20とは何でしょうか?
EIPとは 「Ethereum Improvement Proposals」の略で、「Ethereumの新しい機能やプロセスに関する提案を規定する標準規格」のことです。
EIPの細かい説明は以下に書かれています。
簡単に言うと以下になります。
EIPは、Ethereum Improvement Proposalsの略でイーサリアムをより良いものにするために議論される改善提案のこと。イーサリアムは、特定の誰かによって管理されるものではないため、世界中の誰もがEIPを提出することで、イーサリアムの発展に貢献することができる。
20
という数字は20
番目の提案ということです。
EIP20のオプション関数
EIP20には必須の関数とオプション関数の2つがあります。
まずは実装してもしなくても良い、オプション機能について紹介していきます。
name
トークンの名前を返す関数です。
例)Tether
function name() public view returns (string)
symbol
トークンのシンボルを返す関数。
例)USDT
function symbol() public view returns (string)
decimals
トークンが使用する小数点以下の桁数を返す関数。
例えば8を指定すると、トークンの金額を100000000
で割って表示し、ユーザーに対してより読みやすい方法でトークン数を表示することができる。
function decimals() public view returns (uint8)
EIP20の必須関数
では次にEIP20の必須関数について確認していきます。
totalSupply
トークンの総供給量を返す関数。
function totalSupply() public view returns (uint256)
balanceOf
特定アドレスが保持しているトークン残高を返す関数。
function balanceOf(address _owner) public view returns (uint256 balance)
transfer
_to
に送り先のアドレス、_value
に送りたいトークンの量を指定して転送し、転送結果が成功か失敗か返す関数。
条件
_to
に0アドレスを指定できない。transfer
関数の実行アドレスは_value
以上のトークンを保有している必要がある。
function transfer(address _to, uint256 _value) public returns (bool success)
transferFrom
_from
に送り元アドレス、_to
に送り先のアドレス、_value
に送りたいトークンの量を指定して転送し、転送結果が成功か失敗か返す関数。
transferFrom
関数の用途としては、コントラクトがトークン保有者に代わってトークンを転送する。
コントラクトを実行するアドレスは_value
以上のトークンの転送権限が必要。
条件
_from
と_to
に0アドレスを指定できない。_from
で指定したアドレスは_value
以上のトークンを保有している必要がある。- コントラクトを実行するアドレスは
_value
以上のトークンの転送権限が必要。
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
approve
_spender
に資金引き出し許可を与えるアドレス、_value
に引き出せるトークンを指定して、指定したアドレスに資金の引き出し許可を与える関数。
_spender
に指定されたアドレスは、_value
に指定されたトークン量の範囲内であれば何度でも転送できる。
条件
- _spenderには0アドレスを指定できない。
function approve(address _spender, uint256 _value) public returns (bool success)
allowance
_owner
に転送許可を与えたアドレス、_spender
に転送許可を与えられたアドレスを格納し、transferFrom
関数を通じて、_spender
が_owner
から引き出すことができるトークン量を返す関数。
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
ERC20のコード確認
ではこの章から実際にERC20の実装コードを見ていきましょう!
今回はERC20を実装する上でほとんどの人が使用している「OpenZeppelin」を使用していきます。
「OpenZeppelin」とは、Solidityで安全なスマートコントラクトを構築できるオープンフレームワークです。
分散型アプリケーションの構築や自動化、運用のためのセキュリティ製品を主に提供している企業です。
今回は以下のページを使用しながらコードの確認をしていきます。
https://docs.openzeppelin.com/contracts/4.x/api/token/erc20
IERC20
IERC20とは、ERC20を実装する上で必要な関数やイベントが定義されているinterface
です。
Solidityにおけるイベントとは、簡単にいうと「スマートコントラクトでの動作結果をフロントに伝える」役割を持った機能です。
詳しくは以下の公式ドキュメントを参考にしてください。
https://solidity-jp.readthedocs.io/ja/latest/contracts.html#events
interface
interface
とは、コントラクトに似ていますが実行することはできません。
interface
には関数名やひきすうなどのみ定義されていて中身は一切定義されていません。
そのため、interface
を継承したコントラクト内で関数を再度定義して中身を記述する必要があります。
「interface
いらなくね?」
こう思う方もいると思います。
interface
を使用するメリットは、実装で必要な関数を確認できることにあります。
最初で述べたように、interface
には実装する上で必要な関数が定義されているので、開発者やコントラクトを実行するユーザーからどんな機能があるのかを簡単に確認できます。
有名なプロジェクトでもinterface
はよく使用されているので、この機会に理解しておくと後々役に立ちます。
公式ドキュメントは以下になります。
https://solidity-jp.readthedocs.io/ja/latest/contracts.html#interfaces
関数
ではまずはIERC20に定義されている関数から確認していきましょう。
関数一覧
totalSupply()
balanceOf(account)
transfer(to, amount)
allowance(owner, spender)
approve(spender, amount)
transferFrom(from, to, amount)
上記の6つ関数が定義されています。
EIP20の解説部分で取り上げた関数と同じなため詳細はそちらを参照してください!
イベント
IERC20には2つのイベントが定義されています。
イベント一覧
Transfer(from, to, value)
value
分のトークンを保有している、from
に指定されたアドレスからto
に指定されたアドレスへトークンが転送されたときに発行される。- 値は
0
であっても良い。
Approval(owner, spender, value)
approve
関数が呼び出され、owner
に指定されたアドレスからspender
に指定されたアドレスへvalue
分のトークンの転送許可が設定されたときに発行される。
IERC20Metadata
次にIERC20メタデータについて確認していきます。
こちらもIERC20同様interface
が定義されています。
以下の関数もEIP20で解説している関数です。
関数一覧
name()
symbol()
decimals()
上記の3つの関数はオプション関数のため必須の機能ではありません。
ERC20
ではここから念願のERC20について確認していきます!
IERC20でも述べているように、このERC20はIERC20とIERC20Metadataを継承しています。
そのためIERC20の6つの関数とIERC20Metadataの2つの関数が定義されています。
そして、もちろんそれ以外の関数もたくさん定義されているので、簡単な説明も添えていきます。
関数一覧
constructor(name_, symbol_)
name()
symbol()
decimals()
totalSupply()
balanceOf(account)
transfer(to, amount)
allowance(owner, spender)
approve(spender, amount)
transferFrom(from, to, amount)
increaseAllowance(spender, addedValue)
decreaseAllowance(spender, subtractedValue)
_transfer(from, to, amount)
_mint(account, amount)
_burn(account, amount)
_approve(owner, spender, amount)
_spendAllowance(owner, spender, amount)
_beforeTokenTransfer(from, to, amount)
_afterTokenTransfer(from, to, amount)
説明も合わせて書くとだいぶ長くなってしまうので分割して説明していきます。
constructor
トークンの名前(name
)とシンボル(symbol
)をデプロイ時に渡し、デプロイ後は変更できない。
小数値(decimals
)のデフォルト値は18になっているため、別の値に設定したいときは上書きする必要がある。
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
name
function name() public view virtual override returns (string memory) {
return _name;
}
_symbol
変数の値を返す変数。
decimals
function decimals() public view virtual override returns (uint8) {
return 18;
}
decimals
に指定した値(デフォルトは18
)を返す関数。
totalSupply
uint256 private _totalSupply;
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
トークンの総供給量を返す関数。
balanceOf
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
account
で指定したアドレスが保有するトークンの量を返す関数。
transfer
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
_msgSender
関数はmsg.sender
の結果を返す関数です。
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/utils/Context.sol
_transfer
関数は後述しています。
allowance
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
_allowances
マッピングにて保管されている、owner
に指定されたアドレスがspender
で指定されたアドレスにどれくらいの量のトークンの転送許可を与えているかを確認できる関数。
mapping(address => mapping(address => uint256)) private _allowances;
approve
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
_spender
に資金引き出し許可を与えるアドレス、_value
に引き出せるトークンを指定して、指定したアドレスに資金の引き出し許可を与える関数。
_approve
関数については後述しています。
transferFrom
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
from
で指定したアドレスからto
で指定したアドレスへamount
分のトークンを送る関数。
_spendAllowance
関数と_transfer
関数は後述しています。
increaseAllowance
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
spender
に指定したアドレスが転送できるトークン量を増やす関数。
条件
- spenderは0アドレスにできない。
_approve
関数は後述しています。
decreaseAllowance
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
spender
に指定したアドレスが転送できるトークン量を減らす関数。
条件
spender
は0アドレスにすることはできない。spender
はsubtractedValue
よりも多くのトークン量を転送許可されている必要がある。
_approve
関数は後述しています。
uncheckedは、オーバフローとアンダーフローのチェックを行わないというマークです。
チェックを行わないことでガス代が節約されるため、チェックが不要な箇所では積極的に使用すると良いです。
https://ethereum.stackexchange.com/questions/113221/what-is-the-purpose-of-unchecked-in-solidity
オーバフローとアンダーフローについては以下を参考にしてください。
_transfer
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
from
に指定されたアドレスから、to
に指定されたアドレスにamount
分のトークンを転送する関数。
条件
from
を0アドレスにできない。to
を0アドレスにできない。from
に指定されたアドレスは、amount
以上のトークンを保有している必要がある。
_mint
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
amount
分のトークンを生成し、account
に指定されたアドレスに送ることでトークンの総供給量を増やす関数。
条件
account
を0アドレスにできない。
_beforeTokenTransfer
関数と_afterTokenTransfer
関数については後述しています。
_burn
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
account
で指定したアドレスからamount
分のトークンを破棄してトークンの総供給量を減らす関数。
条件
- 送り先は0アドレスに指定されている。
account
を0アドレスにできない。account
で指定したアドレスは、amount
分以上のトークンを保有してある必要がある。
_beforeTokenTransfer
関数と_afterTokenTransfer
関数については後述しています。
_approve
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
owner
で指定したアドレスから、spender
に指定したアドレスにamount
分のトークンの転送許可を与える関数。
条件
owner
を0アドレスにできない。soender
を0アドレスにできない。
_spendAllowance
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
owner
に指定されたアドレスが、spender
に指定されたアドレスへ転送許可していたトークンの量をamount
に更新する関数。
条件
- 現在
spender
に指定されたアドレスに転送許可されている値が最大値の場合は更新しない。 owner
に指定されたアドレスが、amount
で指定した量のトークンを保有していない場合は処理を戻す。
_beforeTokenTransfer
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
トークン転送前に呼び出される関数。
この関数に任意の追加処理を実装することができる。
from
とto
が0アドレスでないときfrom
で指定されたアドレスからto
に指定したアドレスへamount
分のトークンが転送される。
from
に0アドレスが指定されると、to
で指定されたアドレスにamount
分のトークンが生成されたのち転送される。
to
に0アドレスが指定されると、from
で指定されたアドレスからamount
分のトークンが燃やされる。
from
とto
の両方が0アドレスになることはない。
_afterTokenTransfer
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
トークン転送後に呼び出される関数。
この関数に任意の追加処理を実装することができる。
from
とto
が0アドレスでないときfrom
で指定されたアドレスからto
に指定したアドレスへamount
分のトークンが転送される。
from
に0アドレスが指定されると、to
で指定されたアドレスにamount
分のトークンが生成されたのち転送される。
to
に0アドレスが指定されると、from
で指定されたアドレスからamount
分のトークンが燃やされる。
from
とto
の両方が0アドレスになることはない。
Extention
ここまでERC20について確認できました。
ERC20には拡張機能がたくさんあるのですが、ここで紹介するにはあまりにも多すぎるため、今後時間あるときに別記事でまとめていきます。
とりあえずは基本的なERC20を知っておけば問題ありません。
ERC20の実装
前章まででERC20の概要とコードの理解ができました。
この章では実際にERC20の実装をしていきたいと思います。
前章が相当長かったと思うので覚悟して取り掛かってください!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Cardene is ERC20 {
constructor() ERC20("Cardene", "CARD") {
_mint(msg.sender, 100000 * 10 ** decimals());
}
}
はい…。嘘つきました。
このたった7行のコードでERC20の実装は完了です。
「え?さっきの大量の関数はどこに?」と思う方もいると思いますが、先ほどの長い関数たちを定義したコントラクトを継承しています。
4行目でopenzeppelinが提供しているERC20のコントラクトを読み込んでいます。
6行目でCardene is ERC20
と4行目で読み込んだコントラクトを継承しています。
継承すると継承元のコントラクトで定義されている関数を全て使用することができるため、先ほど解説した関数を全て使用することができます。
納得していただけたでしょうか?
では次の章で実行していきましょう!
ちなみに以下のOpenzeppelinのサイトから簡単にコードを生成できるので、触ってみると理解が深まるかもしれいないです。
https://docs.openzeppelin.com/contracts/4.x/wizard
ERC20の実行
この章では実際にERC20を実行していきます!
remixというブラウザで使用できるエディタを使用していきます!
実装手順を紹介していきます。
ファイルの作成
以下の画面の左側の「contracts」というディレクトリの上で右クリックすると、メニューが出てくるので「New File」をクリックして、「CardeneERC20.sol」と入力してください。
そうするとファイルが作られるので、「CardeneERC20.sol」をクリックすると右の画面にファイルの中身が表示されます。
まだ何も書かれていないので以下のコードを貼り付けてください。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Cardene is ERC20 {
constructor() ERC20("Cardene", "CARD") {
_mint(msg.sender, 100000 * 10 ** decimals());
}
}
コンパイル
では次にコンパイルしていきます。
以下のように一番左の5つのアイコンの真ん中をクリックして、「Compile CardeneERC20.sol」をクリックしてください。
アイコンに緑のチェックが入ればコンパイル完了です。
デプロイ
一番左の5つのアイコンの下から2つ目のアイコンをクリックして、「Deploy」というボタンをクリックしてください。
そうするとコントラクトがデプロイされて、左下の方にある「Deployed Contracts」の下に「CARDENEERC20 AT 0XD91...39138」と表示されればデプロイできています!
関数の実行
ではここから早速関数を実行していきましょう!
確認系
まずは確認系の関数から実行していきます。
先ほどデプロイしたコントラクト内の「decimals」、「name」、「symbol」、「totalSupply」を実行していきます。
それぞれ結果が出力されていますね。
トークンの名前は「Cardene
」で、トークンのシンボルは「CARD
」、「decimals」はデフォルトの「18
」、総供給量は「100000000000000000000000
」を膨大な数字になってしまいました。
balancesOf
ではどのユーザーがどれくらいトークンを保持しているか確認できるbalanceOf
関数を実行していきましょう。
コントラクトをデプロイした以下のアドレスの右にあるコピーボタンをクリックしてください。
アドレスをコピーしたら、balanceOf
と書かれている右上の矢印をクリックして、account
の部分に先ほどコピーしたアドレスを貼り付けてください。
貼り付けたらcallと書かれているボタンを押すことで、「100000000000000000000000
」と表示されます。
デプロイしたアドレスが全てのトークンを所有していることが確認できました。
transfer
次にトークンを他のアドレスに転送してみます。
ここからは手順が複雑になってくるので動画で紹介していきます。
他のアドレスを指定して1000
トークンを送れました!
approve
他のアドレスに自分が所有しているトークンをtransfer
する許可を与えたいと思います。
他のアドレスとトークン量を指定して、approve
関数を実行してtransfer
する許可を与えることができました。
allowance
では本当にtransferする許可を与えられているのか確認していきます。
allowance
関数を実行することで、transfer
できるトークンの量を確認できます。
トークン所有者のアドレスと先ほどtransfer
許可を与えたアドレスを与えた結果、approve
関数を実行したときに指定した10000
という数値が表示されました。
しっかりtransfer
許可を与えることができていますね。
transferFrom
ではtransfer
の許可を与えられたアドレスから、別のアドレスにトークンを送りたいと思います。
トークン所有者のアドレスと3つ目の別のアドレスを指定して、transfer
の許可を与えられたアドレスからtransferFrom
関数を実行して1000
トークンを送ることができました。
transfer
許可を与えられていないと、トークン所有者以外がtransferFrom
関数を実行しても失敗してしまうのでぜひ試してみてください!
decreaseAllowance
次にtransfer許可を与えているトークンの量を減らしていきます。
transfer
許可を与えているトークン量の調整は、再度approve
関数を実行すれば良いと思うかもしれないです。
しかし、approve
関数はトークン量を更新してしまうため、現在transfer
許可を与えているトークン量に対して増減させることはできません。
そこでdecreaseAllowance
関数を実行することで、10000
という値から5000
という値に減らすことができました。
(ちょうど半分の値なので更新したように見えますが、ちゃんと減算されています。)
increaseAllowance
では最後にdecreaseAllowance
関数の逆の処理を行うincreaseAllowance
関数を実行して、transfer
許可を与えているトークン量を増やしていきましょう!
10000
トークンを追加したところ、15000
に増えていることが確認できました。
まとめ
この章ではERC20を実行してきました。
いかがだったでしょうか?
みているだけでは理解できない部分もあると思うので、ぜひ自分でも手を動かしてみてください。
Goerli Testnetにデプロイ
では次にGoerli Testnetにデプロイしていきます!
Metamask
まだMetamaskを追加していない人は以下から追加してください。
https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn
Goerli Testnet ETH
次にGoerli TestnetのETHを取得します。
Faucetと呼ばれところから取得することができます。
いくつか取得する方法があるのですが、以下のAlchemyのFaucetから取得するのがおすすめです。
24時間ごとに0.5Goerli ETHを受け取ることができます。
デプロイ準備
ではここからデプロイの準備をしていきます。
まずは以下のようにMetamaskをクリックして、Goerli test networkに切り替えてください。
その後Remixに戻って、もし前回デプロイしたデータが残っている場合は、以下の白枠あたりにゴミ箱マークがあるのでクリックしてください。
次に赤枠の部分を「Injected Provider - Metamask」にしてください。
こうすることで、デプロイ時にMetamaskが起動するようになります。
Goerli Testnetが選択されていると、「Injected Provider - Metamask」の下に「Goerli (5) network」と表示されて、Goerli Testnetと接続されていることが確認できます。
これで準備完了です。
Goerli Testnetにデプロイ
ではGoerli Testnetにデプロイしていきましょう!
上記の手順でEtherscanのサイトが確認できれて以下のように「Success」と表示されていればデプロイ完了です!
https://goerli.etherscan.io/tx/0x4998868509277a35c7a2bc1484177a877ed436c67ac89eb7f55a6e8e820f6ab6
以下から「CARDトークン」が作成されているのを確認できます。
https://goerli.etherscan.io/address/0xdb8452c7df969c6248b3e87d89d1f0d39a1e6836
トークンの追加
ではせっかくなので作成したERC20トークンをMetamaskに追加していきましょう。
Goerli Testnetを選択した状態で、以下のように「Assets」の「Import tokens」をクリックしてください。
そうすると以下のような画面になるので、「Token contract address」と「Token symbol」、「Token decimal」を入力して、「Add custom token」のボタンをクリックしてください。
「Token contract address」については、以下の赤枠部分に記載されています。
「Token contract address」を入力するとおそらく「Token symbol」、「Token decimal」は自動で入力されます。
そうすると確認画面が表示されるので、「Import tokens」をクリックしてください。
以下のように作成したERC20トークンが追加されていれば成功です!
別アドレスにCARDトークンを送付
ではせっかくテストネットにデプロイしたので、トークンを送ってみましょう。
別のアカウントをMetamask上で作成し、先ほどと同じ手順でCARDトークンを追加します。
別のアカウントの作成方法は以下の記事が参考になります。
まだCARDトークンを所有していないことが確認できます。
では別アドレスにCARDトークンを送ってみましょう!
少し時間かかりましたが、CARDトークンを別アドレスに送ることができました。
別アドレスのCARDトークンの量を確認すると、先ほど指定した量のCARDトークンを受け取れていますね。
ちなみに80000
ではなく、80000000000000000000000
と指定していたかというと、eth
ではなくwei
で計算されるためです。
1 ether = 10^18 wei
なので、以下のようなサイトで簡単に計算できます。
[おまけ] BSC テストネットにデプロイ
最後におまけとして、BSCテストネットにデプロイしていきたいと思います!
BSCとは「Binance Smart Chain」の略で、仮想通貨取引所「バイナンス(BINANCE)」が運用していて、「BNB(バイナンスコイン)」がネイティブトークンです。
BNBはERC20に準拠しているため、手順は前章のGoerliとほとんど変わりません。
デプロイ準備
まずはデプロイするための準備をしていきましょう!
まずはBSCテストネットをMetamaskに追加します。
手順は以下にまとめらています。
https://academy.binance.com/en/articles/connecting-metamask-to-binance-smart-chain
次にFaucetからTestnet BNBを取得します。
手順は以下にまとめられています。
https://binance-wallet.gitbook.io/binance-chain-wallet/dev/untitled
BSCテストネットにデプロイ
では早速デプロイしていきましょう!
ちゃんと動作しましたね。
念の為BSC Scanで確認しましょう。
https://testnet.bscscan.com/address/0xf9f01010ddad7cf8ddeb526c89a0c3134fa019c4
ちゃんとBSCテストネットにもCARDトークンを作成できています!
最後に
今回は「ERC20」について解説してきました!
だいぶ長かったと思いますが、最後まで読んでいただきありがとうございます。
いかがだったでしょうか?
ERC20について理解が深まっていたら嬉しいです!
もし何か質問などがあれば以下のTwitterなどから連絡ください!
普段はSolidityやブロックチェーン、Web3についての情報発信をしています。
Twiiterでは気になった記事などを共有しているので、ぜひフォローしてくれると嬉しいです!
参考
https://bitflyer.com/ja-jp/s/glossary/ico
https://fisco.jp/media/erc-20/
https://www.coindeskjapan.com/keywords/utility-token
https://phemex.com/ja/academy/イーサリアムのerc-20トークン-知っておくべきすべてのこと
https://www.ledger.com/ja/academy/blockchain/ercトークンとはなぜ利用すべきなのか