Solidity+Truffle+GanacheでERC20トークンをローカル&Goerliにデプロイするまでの手順

「Solidity何もわからん」な状態だったので

  • ERC20トークンを自分で書いてローカルなEthereum環境にデプロイ
  • その後、テストネット(Goerli)にデプロイ

するところまでやってみたのでメモ。

自分が思い出す用なので、かなりテキトーなメモです。
なので初心者の人には参考にならないかもしれません。

準備するやつ

Truffle

ローカルでコントラクトを開発するためのフレームワーク的なやつ。

以下でインストールできます。

npm install -g truffle

Ganache

ローカルでEthereumノードを動かすためのツール。

ノードを動かすだけじゃなくて100ETHが入ったアカウント×10個を自動生成してくれるぽいです。

以下でインストールできます。

npm install -g ganache-cli

 

ちなみにGUI版もあるみたいなのですが、自分の場合はWSL2を使っているので、コマンドライン版を使いました。

テストネットにデプロイするだけならGanacheは不要ですし、何だったらTruffle自体にもローカルEthereum環境を構築する機能があるぽいので、試しにデプロイしてみるだけならTruffleだけでいい気がします。

(Truffleでもtruffle developで同じことができます)

やること

プロジェクトに準備

以下のコマンドを実行します。

# ディレクトリを作って移動
mkdir solidity-test-erc20
cd solidity-test-erc20

# 初期化
npm init -y
truffle init

# VScodeを開く
code .

こんな感じのVScodeが開いたら成功です👇

コントラクトを書く

contractsディレクトリ配下に、コントラクトを書きます。

自分の場合は、試しにテストしてみたかっただけなので、ERC20に準拠したコードを1から書きました👇

ERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract ERC20 {
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(
        address indexed _owner,
        address indexed _spender,
        uint256 _value
    );
    string public name;
    string public symbols;
    uint8 public decimals = 18;
    uint256 public totalSupply;

    mapping(address => uint256) private ownerToBalance;
    mapping(address => mapping(address => uint256)) private allowances;

    constructor(
        string memory _name,
        string memory _symbols,
        uint256 _totalSupply
    ) {
        name = _name;
        symbols = _symbols;
        totalSupply = _totalSupply;
        ownerToBalance[msg.sender] = _totalSupply;
    }

    function balanceOf(address _owner) external view returns (uint256 balance) {
        return ownerToBalance[_owner];
    }

    function transfer(address _to, uint256 _value) external returns (bool) {
        _transfer(msg.sender, _to, _value);
        return true;
    }

    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    ) public returns (bool) {
        require(
            allowances[_from][_to] >= _value,
            "transfer amount exceed amount"
        );
        _transfer(_from, _to, _value);
        allowances[_from][_to] -= _value;
        return true;
    }

    function _transfer(
        address _from,
        address _to,
        uint256 _value
    ) private {
        require(_from != _to, "to == from");
        require(ownerToBalance[_from] >= _value, "insufficient balance");
        ownerToBalance[_from] -= _value;
        ownerToBalance[_to] += _value;
        emit Transfer(_from, _to, _value);
    }

    function approve(address _spender, uint256 _value) public returns (bool) {
        allowances[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    function allowance(address _owner, address _spender)
        public
        view
        returns (uint256 remaining)
    {
        return allowances[_owner][_spender];
    }
}

 

本来は普通のERC20トークンを作るだけならこんなコードを書く必要はなくて、openzepplinなどで公開されているERC20.solファイルを継承して、コンストラクタを書くだけでいいはずです。

むしろそうしたほうが

  • コントラクト部分を書くだけでいいので楽ちん(たぶん10行くらい書くだけでいい)
  • セキュリティに強い
  • +αの機能がついてる
    • たとえばincreaseAllowanceみたいな関数はERC20に定義されているわけじゃない

という感じでメリットだらけです。

ですが今回は勉強のために自分で書いた感じです。

コントラクトをコンパイル

以下のコマンドで、contractsディレクトリ配下の.solファイルがコンパイルできます。

truffle compile

 

コンパイルが成功したら、プロジェクトの直下にbuildというディレクトリが生成されて、その中にコントラクトのABIなJSONファイルが作成されます。

デプロイ用のコードを書く

migrationsディレクトリ直下に、2_◯◯◯◯.jsというファイルを作ります。

必ず先頭に2という数字を付ける必要があります。

中身は以下のようにします。

const ERC20 = artifacts.require("ERC20");
const toWei = (number) => web3.utils.toWei(web3.utils.toBN(number), "ether");

module.exports = async function (deployer) {
  await deployer.deploy(ERC20, "penpen", "PEN", toWei(10 ** 10));
};

5行目の部分は

  • penpen:コントラクトの名前
  • PEN:トークンのシンボル
  • toWei(10 ** 10):発行数

です。(コントラクトのコンストラクターにわたす引数です)

Ganacheにデプロイ

ganache-cliでローカル環境を立ち上げます。

別ターミナルを立ち上げて、truffle migrationでデプロイします。

あとはTruffleでコンソールの中に入って、デプロイされたことを確認できます。

Goerliにデプロイ

次は本番環境のテストネットにデプロイします。

 

まず以下をインストールします。

npm install -D @truffle/hdwallet-provider

 

次に、InfuraでサインアップしてAPIキーを取得します。


▲こんな感じのAPIキー入りのURLを取得する

 

次にtruffle-config.jsの中のnetworksプロパティ配下に、以下のプロパティを追加します。

goerli: {
      provider: () => {
        return new HDWalletProvider(
          "ここにニーモニックを入れる",
          "https://goerli.infura.io/v3/ここに自分のAPIキーを入れる
        );
      },
      network_id: "5", // eslint-disable-line camelcase
      gas: 4465030,
      gasPrice: 10000000000,
    },

 

「ここにニーモニックを入れる」の部分は、ウォレットを作ったときに最初に表示される20個くらいのワードの組み合わせのアレです。

そのニーモニックによって生成されるウォレットの1番目が使用されます。

「何個目のウォレットを使用するか」については、new HDWalletProviderの引数で指定できます。(詳しくは公式に載ってます。)

truffle.config.jsにそのままAPIキーなどを打ち込んでいますが、本来は環境変数にするなりした方が良いです。

 

準備ができたら、以下のコマンドでデプロイします。

truffle migrate --network goerli

EtherScanでVerify

デプロイ後は、EtherscanのGoerliページにコントラクトアドレスを入力すると確認できます。

たとえば私がデプロイしたコントラクトは以下のURLから見れます。

https://goerli.etherscan.io/address/0x4cB3208229d6F3Cf5D1017E1c409190Ef5a6B8B5#code

 

ただこのままだと「ソースコードをverifyしてね」な画面が表示されてしまってるのでverifyします。

以下の記事が参考になります。

https://medium.com/quick-programming/verify-a-smart-contract-on-etherscan-using-truffle-cb2656fd9c41

 

基本的には以下の流れを行うだけです。

  1. truffle-plugin-verifyというnpmパッケージを追加する
  2. truffle-config.jsをいじる
  3. truffle run verify ERC20 --network goerliを実行する

これだけでverifyできます。

これで以下のようにコントラクトの中身が表示されるようになります。

 

Read Contractからコントラクトを操作できたりもします👇


▲きちんと発行した分が指定したアドレスが保持されていることを確認できる

 

おわり

Dapp
スポンサーリンク
この記事を書いた人
penpen

1991生まれ。WEBエンジニア。

技術スタック:TypeScript/Next.js/Express/Docker/AWS

フォローする
フォローする
penpenメモ

コメント

タイトルとURLをコピーしました