Smart Contract Solidity ブロックチェーン

ERC20について非エンジニアでもわかるように概要から実装まで1から丁寧にわかりやすく解説

かるでね

こんにちは!CryptoGamesというブロックチェーンゲーム企業でエンジニアをしているかるでねです!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

このブログ以外でも情報発信しているので、よければ他の記事も見ていってください。

今回は「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)

Polygon(MATIC)

Shiba Inu(SHIB)

Uniswap(UNI)

ChainLink(LINK)

その他

ここまで紹介してきたトークンはERC20トークンのほんの一部です。

他にもたくさんありますがキリがないため、他にどんな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で安全なスマートコントラクトを構築できるオープンフレームワークです。

分散型アプリケーションの構築や自動化、運用のためのセキュリティ製品を主に提供している企業です。

今回は以下のページを使用しながらコードの確認をしていきます。

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が定義されています。

以下の関数も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;
}

_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アドレスにすることはできない。
  • spendersubtractedValueよりも多くのトークン量を転送許可されている必要がある。

_approve関数は後述しています。

uncheckedは、オーバフローとアンダーフローのチェックを行わないというマークです。

チェックを行わないことでガス代が節約されるため、チェックが不要な箇所では積極的に使用すると良いです。

オーバフローとアンダーフローについては以下を参考にしてください。

_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 {}

トークン転送前に呼び出される関数。

この関数に任意の追加処理を実装することができる。

fromtoが0アドレスでないときfromで指定されたアドレスからtoに指定したアドレスへamount分のトークンが転送される。

fromに0アドレスが指定されると、toで指定されたアドレスにamount分のトークンが生成されたのち転送される。

toに0アドレスが指定されると、fromで指定されたアドレスからamount分のトークンが燃やされる。

fromtoの両方が0アドレスになることはない。

_afterTokenTransfer

function _afterTokenTransfer(
    address from,
    address to,
    uint256 amount
) internal virtual {}

トークン転送後に呼び出される関数。

この関数に任意の追加処理を実装することができる。

fromtoが0アドレスでないときfromで指定されたアドレスからtoに指定したアドレスへamount分のトークンが転送される。

fromに0アドレスが指定されると、toで指定されたアドレスにamount分のトークンが生成されたのち転送される。

toに0アドレスが指定されると、fromで指定されたアドレスからamount分のトークンが燃やされる。

fromtoの両方が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のサイトから簡単にコードを生成できるので、触ってみると理解が深まるかもしれいないです。

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を追加していない人は以下から追加してください。

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トークンを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に追加します。

手順は以下にまとめらています。

次にFaucetからTestnet BNBを取得します。

手順は以下にまとめられています。

BSCテストネットにデプロイ

では早速デプロイしていきましょう!

ちゃんと動作しましたね。

念の為BSC Scanで確認しましょう。

ちゃんとBSCテストネットにもCARDトークンを作成できています!

最後に

今回は「ERC20」について解説してきました!

だいぶ長かったと思いますが、最後まで読んでいただきありがとうございます。

いかがだったでしょうか?

ERC20について理解が深まっていたら嬉しいです!

もし何か質問などがあれば以下のTwitterなどから連絡ください!

普段はSolidityやブロックチェーン、Web3についての情報発信をしています。

Twiiterでは気になった記事などを共有しているので、ぜひフォローしてくれると嬉しいです!

参考

-Smart Contract, Solidity, ブロックチェーン
-,