はじめに
初めまして。 『DApps開発入門』という本や色々記事を書いているかるでねです。
以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!
今回は『ERC20』についてまとめていきます。
こちらの2022年12月に書いていた記事です。 2025年11月にリライトしました。
ERC20とは
統一されたトークン規格
ERC20とは、「Ethereumブロックチェーンで動作する統一されたトークン規格の1つ」です。
「統一されたトークンの規格」というのは、「共通のルールを持っているトークン」という事です。 共通のルールを持っていないと、トークンごとにウォレットが必要になります。 新しいトークンを触るごとにウォレットを作っていては、だいぶめんどくさいですよね。
そこでERC20というトークン規格に沿って設計することで1つのウォレットで管理できるようになります。
また、ERC20は「代替性トークン」とも呼ばれています。 「代替性トークン」とは、あるトークンと同じ価値を持つ他のトークンと交換できるということです。 反対に交換できないのでNFTを代表する「非代替性トークン」です。
誰でも作成可能
ERC20トークンは誰でも作ることができます。 そのため現在存在するERC20トークンの種類はめちゃくちゃ多いです。
上記から現在のERC20トークンの種類を確認できるのですが、現在(2025年11月17日)では177万9821種類のERC20トークンがあります。
追加機能を実装可能
この章の前半で説明したように、ERC20は統一された規格ですが、あくまで最低限の機能は統一された規格で設計されています。 そのため、トークンごとに追加機能を実装することが可能です。
新規発行するERC20トークンごとに以下のように役割が異なってくるため、役割に応じて追加機能を実装することが可能になります。
ステーブルコイン
ドルや日本円などの法定通貨や市場で取引されるコモディティ(金やプラチナなど)などの価格に連動するように設計された暗号通貨。
DeFiトークン
分散型金融(DeFi)プロトコルを支援するトークン。
スマートコントラクトで管理されているため中央管理者が存在しない。
仮想通貨を預け入れて利息を得たり、流動性を提供して独自トークンを得る、仮想通貨を貸し出して利回りを得るなどして稼ぐことができる。
ユーティリティトークン
特定のコミュニティやサービスなどを利用するにあたり実用性があるトークンのこと。 具体的には以下のような用途が想定される。
- サービス内で物を購入したり利用料を支払う
- 利用者の行動に対してインセンティブを支払う
- 所有することでコミュニティの意思決定に関われる
- 保有している量や期間に応じて報酬が発生する
代表的なERC20トークン
この章では代表的なERC20トークンについて一覧にしていきます。
- Tether(USDT) https://tether.to/en/
- Binance Coin(BNB) https://www.binance.com/en
- USD Coin(USDC) https://www.centre.io/usdc
- DAI(DAI) https://makerdao.com/ja/
- Basic Attention Token(BAT) https://basicattentiontoken.org/
- Polygon(MATIC) https://polygon.technology/
- Shiba Inu(SHIB) https://shibatoken.com/
- Uniswap(UNI) https://uniswap.org/
- ChainLink(LINK) https://chain.link/
EIP20
EIPとは
前章までERC20といってきましたが、EIP20とは何でしょうか?
EIPとは「Ethereum Improvement Proposals」の略で、「Ethereumの新しい機能やプロセスに関する提案を規定する標準規格」のことです。
EIPの細かい説明は以下に書かれています。
簡単に言うと以下になります。
EIPは、Ethereum Improvement Proposalsの略でEthereumをより良いものにするために議論される改善提案のこと。 Ethereumは、特定の誰かによって管理されるものではないため、世界中の誰もがEIPを提出することで、Ethereumの発展に貢献することができる。 20という数字は20番目の提案ということです。
ERCは、EIPの中の1カテゴリです。 そのため、ERC20とEIP20は同じものを指しています。
ERC20の関数
ERC20のオプション関数
ERC20には必須の関数とオプション関数の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)ERC20の必須関数
では次にERC20の必須関数について確認していきます。
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で安全なスマートコントラクトを構築できるオープンフレームワークです。
分散型アプリケーションの構築や自動化、運用のためのセキュリティ製品を主に提供している企業です。
今回は以下のページを使用しながらコードの確認をしていきます。
IERC20
IERC20とは、ERC20を実装する上で必要な関数やイベントが定義されているinterfaceです。
Solidityにおけるイベントとは、簡単にいうと「スマートコントラクトでの動作結果をフロントに伝える」役割を持った機能です。
詳しくは以下の公式ドキュメントを参考にしてください。
interface
interfaceとは、コントラクトに似ていますが実行することはできません。 interfaceには関数名やひきすうなどのみ定義されていて中身は一切定義されていません。 そのため、interfaceを継承したコントラクト内で関数を再度定義して中身を記述する必要があります。
「interfaceいらなくね?」こう思う方もいると思います。
interfaceを使用するメリットは、実装で必要な関数を確認できることにあります。 最初で述べたように、interfaceには実装する上で必要な関数が定義されているので、開発者やコントラクトを実行するユーザーからどんな機能があるのかを簡単に確認できます。 有名なプロジェクトでもinterfaceはよく使用されているので、この機会に理解しておくと後々役に立ちます。
公式ドキュメントは以下になります。
関数
ではまずは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が定義されています。 以下の関数もERC20で解説している関数です。
関数一覧
name()symbol()decimals()
上記の3つの関数はオプション関数のため必須の機能ではありません。
ERC20
ではここから念願のERC20について確認していきます! IERC20でも述べているように、このERC20はIERC20とIERC20Metadataを継承しています。 そのためIERC20の6つの関数とIERC20Metadataの3つの関数が定義されています。
そして、もちろんそれ以外の関数もたくさん定義されているので、簡単な説明も添えていきます。
関数一覧:
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
_name変数の値を返す関数。
function name() public view virtual override returns (string memory) {
return _name;
}symbol
_symbol変数の値を返す関数。
function symbol() public view virtual override returns (string memory) {
return _symbol;
}decimals
decimals に指定した値(デフォルトは18)を返す関数。
function decimals() public view virtual override returns (uint8) {
return 18;
}totalSupply
トークンの総供給量を返す関数。
uint256 private _totalSupply;
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}balanceOf
accountで指定したアドレスが保有するトークンの量を返す関数。
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}transfer
_msgSender関数はmsg.senderの結果を返す関数です。
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}_msgSender関数の定義は以下です。
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}_transfer関数は後述しています。
allowance
_allowancesマッピングにて保管されている、ownerに指定されたアドレスがspenderで指定されたアドレスにどれくらいの量のトークンの送付許可を与えているかを確認できる関数。
mapping(address => mapping(address => uint256)) private _allowances;
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}approve
_spenderに資金引き出し許可を与えるアドレス、_valueに引き出せるトークンを指定して、指定したアドレスに資金の引き出し許可を与える関数。
_approve関数については後述しています。
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}transferFrom
fromで指定したアドレスからtoで指定したアドレスへamount分のトークンを送る関数。
_spendAllowance関数と_transfer関数は後述しています。
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;
}increaseAllowance
spenderに指定したアドレスが送付できるトークン量を増やす関数。
条件
spenderは0アドレスにできない
_approve関数は後述しています。
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}decreaseAllowance
spenderに指定したアドレスが送付できるトークン量を減らす関数。
条件
spenderは0アドレスにすることはできないspenderはsubtractedValueよりも多くのトークン量を送付許可されている必要がある
_approve関数は後述しています。
uncheckedは、オーバフローとアンダーフローのチェックを行わないというマークです。
チェックを行わないことでガス代が節約されるため、チェックが不要な箇所では積極的に使用すると良いです。
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;
}オーバフローとアンダーフローについては以下を参考にしてください。
_transfer
fromに指定されたアドレスから、toに指定されたアドレスに amount 分のトークンを送付する関数。
条件
fromを0アドレスにできないtoを0アドレスにできないfromに指定されたアドレスは、amount以上のトークンを保有している必要がある
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);
}_mint
amount 分のトークンを生成し、account に指定されたアドレスに送ることでトークンの総供給量を増やす関数。
条件
accountを0アドレスにできない
_beforeTokenTransfer 関数と _afterTokenTransfer 関数については後述しています。
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);
}_burn
account で指定したアドレスから amount 分のトークンを破棄してトークンの総供給量を減らす関数。
条件
- 送り先は0アドレスに指定されている
accountを0アドレスにできないaccountで指定したアドレスは、amount分以上のトークンを保有してある必要がある
_beforeTokenTransfer 関数と _afterTokenTransfer 関数については後述しています。
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);
}_approve
owner で指定したアドレスから、spender に指定したアドレスに amount 分のトークンの送付許可を与える関数。
条件
ownerを0アドレスにできないspenderを0アドレスにできない
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);
}_spendAllowance
owner に指定されたアドレスが、spender に指定されたアドレスへ送付許可していたトークンの量を amount に更新する関数。
条件
- 現在
spenderに指定されたアドレスに送付許可されている値が最大値の場合は更新しない ownerに指定されたアドレスが、amountで指定した量のトークンを保有していない場合は処理を戻す
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);
}
}
}_beforeTokenTransfer
トークン送付前に呼び出される関数。 この関数に任意の追加処理を実装することができる。
fromとtoが0アドレスでないときfromで指定されたアドレスからtoに指定したアドレスへamount分のトークンが送付されるfromに0アドレスが指定されると、toで指定されたアドレスにamount分のトークンが生成されたのち送付されるtoに0アドレスが指定されると、fromで指定されたアドレスからamount分のトークンがBurnされるfromとtoの両方が0アドレスになることはない
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}_afterTokenTransfer
トークン送付後に呼び出される関数。 この関数に任意の追加処理を実装することができる。
fromとtoが0アドレスでないときfromで指定されたアドレスからtoに指定したアドレスへamount分のトークンが送付されるfromに0アドレスが指定されると、toで指定されたアドレスにamount分のトークンが生成されたのち送付されるtoに0アドレスが指定されると、fromで指定されたアドレスからamount分のトークンがBurnされるfromとtoの両方が0アドレスになることはない
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}Extension
ここまで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のサイトから簡単にコードを生成できるので、触ってみると理解が深まるかもしれいないです。
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」を膨大な数字になってしまいました。
balanceOf
ではどのユーザーがどれくらいトークンを保持しているか確認できる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 に増えていることが確認できました。
Goerli Testnetにデプロイ
2025年11月現在Goerli Testnetは停止しているので、Sepolia Testnetなどを使用してください。
では次にGoerli Testnetにデプロイしていきます!
Metamask
まだMetamaskを追加していない人は以下から追加してください。
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」と表示されていればデプロイ完了です!
以下から「CARDトークン」が作成されているのを確認できます。
最後に
今回は『ERC20』についてまとめてきました。
他でも色々記事を書いているのでぜひよろしければ読んでいってください!
参考
- https://bitflyer.com/ja-jp/s/glossary/ico
- https://news.mynavi.jp/techplus/article/20210922-1969988/
- 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トークンとはなぜ利用すべきのか
- https://eips.ethereum.org/EIPS/eip-20




