Challenge UNCHAIN vol.4 – dAppのバックエンドはHardhatにお任せ –

さて、LEARNの第1回「ETH-dApp」 のSection 1。

何が待ち受けているのか楽しみ。

No Title

Learn / ETH-dApp このプロジェクトでは、イーサリアムネットワーク上にスマートコントラクトを実装して、スマートコントラクトとやりとりできる独自のWebアプリケーションを構築します。 プロジェクトを進めるには以下の技術が必要です。 いますべてを理解している必要はありません。 …

Lesson 1. 環境構築

  • スマートコントラクト作成
  • スマートコントラクトをブロックチェーン上にデプロイ
  • Webアプリケーション(dApp)作成

ローカル環境でやっていこうという話だが・・・ローカルが汚れるのが嫌いなので、大好きなCloud9でやります。

まずインストールするのはHardhat。

Hardhat | Ethereum development environment for professionals by Nomic Foundation

Easily deploy your contracts, run tests and debug Solidity code without dealing with live environments. Hardhat Network is a local Ethereum network designed for development. Hardhat is the best choice for Solidity debugging. You get Solidity stack traces, console.log and explicit error messages when transactions fail. Change anything you like.

Flexible. Extensible. Fast.
Ethereum development environment for professionals

柔軟性があって、拡張性があって、高速らしい。

プロフェッショナルのためのEtheruem開発環境。

使わずには、やっていけませんよ、という感じ。

ツールとしては3つあって、

  • Runner: the main component
  • Network: a local ethereum network node for development
  • VSCode: a VSCode extension

で、VSCodeは・・・個人的には使わなそう。

なんだけど、さらにその前にCloud9を整備します。

$ sudo yum update
$ npm -v
8.19.3
$ node -v
v16.19.0

あ、Amazon Managed Blockchainはあるけど、、、別で確認しよう。

Amazon Managed Blockchain(ブロックチェーンネットワークを簡単に作成、管理)| AWS

スケーラブルなブロックチェーンネットワークを簡単に作成し管理する Amazon Managed Blockchain はフルマネージドサービスで、一般的なオープンソースフレームワークである Hyperledger Fabric や Ethereum を使用して、パブリックネットワークへの参加や、スケーラブルなプライベートネットワークの作成と管理を簡単に行えます。 ブロックチェーンは、 信頼された中央機関なしで 複数の当事者がトランザクションを実行できるアプリケーションを構築することを可能にします。 現在、既存のテクノロジーを使用してスケーラブルなブロックチェーンネットワークを構築することは、設定が複雑で管理が困難です。ブロックチェーンネットワークを構築するために、各ネットワークメンバーは、手動でハードウェアをプロビジョニングし、ソフトウェアをインストールし、アクセスコントロール用の証明書を作成および管理し、ネットワーキングコンポーネントを設定する必要があります。ブロックチェーンネットワークが稼働したら、インフラストラクチャを継続的に監視し、トランザクション要求の増加、ネットワークへの参加または退会などの変化に対応する必要があります。 Amazon Managed Blockchain は、わずか数クリックでブロックチェーンインフラストラクチャのプロビジョニングが可能なフルマネージドサービスです。Amazon Managed Blockchain は、プライベートブロックチェーンネットワークの構築や、パブリックブロックチェーンネットワークに接続するためのノードの作成に必要なオーバーヘッドを排除します。 プライベートブロックチェーンネットワークの構築を目指すエンタープライズは、Amazon Managed Blockchain 上で Hyperledger Fabric ブロックチェーンをほんの数分で作成し、AWS アカウント ID を介してパートナー組織をネットワークに招待することができます。ネットワークが稼働したら、Managed Blockchain によってブロックチェーンネットワークの管理と維持が容易になり、証明書の管理やピアノードの可用性の維持といったタスクが自動化されます。プライベート Hyperledger Fabric ブロックチェーンは、 サプライチェーンのデータ共有 や、証券取引の決済といった従来の金融ユースケースなど、分散型ネットワーク環境におけるプライバシーやアクセス制御を必要とするユースケースに適しています。さらに、Amazon Managed Blockchain Hyperledger Fabric は GovCloud でサポートされており、政府機関や請負業者は機密データをホストし、最も厳しい米国政府のセキュリティおよびコンプライアンス要件に対応するために特別に設計された環境にデプロイすることができます。 Managed Blockchain は、Web3 分野のビルダー向けに、Ethereum パブリックメインネットとテストネットに接続し、データの読み取り、イベントの購読、ブロックチェーン上のトランザクションのブロードキャストを行う専用の

npmのバージョンもnodeのバージョンもあんまり整備することはなかったので早速Hardhatをインストール。

$ npm install --save-dev hardhat

added 300 packages, and audited 301 packages in 25s

61 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm notice 
npm notice New major version of npm available! 8.19.3 -> 9.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v9.2.0
npm notice Run npm install -g npm@9.2.0 to update!
npm notice 

整備したと思ったけど、、、npmの最新は9.2.0、nodeの方は19.4.0っぽい。。。

Hardhat Get Started

とりあえず、ここからはHardhatのGet Startedを実施。

プロジェクト作成

$ npx hardhat
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

Welcome to Hardhat v2.12.5

✔ What do you want to do? · Create a JavaScript project
✔ Hardhat project root: · /home/ec2-user/environment/ETH-dApp/my-wave-portal
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Help us improve Hardhat with anonymous crash reports & basic usage data? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y

npm install --save-dev @nomicfoundation/hardhat-toolbox@^2.0.0
npm WARN deprecated uuid@3.4.0: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated request-promise-native@1.0.9: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)
npm WARN deprecated uuid@2.0.1: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)

added 403 packages, and audited 704 packages in 41s

121 packages are looking for funding
  run `npm fund` for details

8 vulnerabilities (5 high, 3 critical)

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

Project created

See the README.md file for some example tasks you can run

Give Hardhat a star on Github if you're enjoying it!

     

GitHub - NomicFoundation/hardhat: Hardhat is a development environment to compile, deploy, test, and debug your Ethereum software. Get Solidity stack traces & console.log.

Hardhat is a development environment to compile, deploy, test, and debug your Ethereum software. Get Solidity stack traces & console.log. - GitHub - NomicFoundation/hardhat: Hardhat is a development environment to compile, deploy, test, and debug your Ethereum software. Get Solidity stack traces & console.log.

Please take a moment to complete the 2022 Solidity Survey: https://hardhat.org/solidity-survey-2022

コンパイル

$ npx hardhat compile
Downloading compiler 0.8.17
Compiled 1 Solidity file successfully

テスト

$ npx hardhat test


  Lock
    Deployment
      ✔ Should set the right unlockTime (1784ms)
      ✔ Should set the right owner
      ✔ Should receive and store the funds to lock
      ✔ Should fail if the unlockTime is not in the future (57ms)
    Withdrawals
      Validations
        ✔ Should revert with the right error if called too soon (43ms)
        ✔ Should revert with the right error if called from another account
        ✔ Shouldn't fail if the unlockTime has arrived and the owner calls it
      Events
        ✔ Should emit an event on withdrawals (42ms)
      Transfers
        ✔ Should transfer the funds to the owner (62ms)


  9 passing (2s)

デプロイ

$ npx hardhat run scripts/deploy.js 
Lock with 1 ETH and unlock timestamp 1704785451 deployed to xxxxx

イーサリアムのテストネットワークの立ち上げ

$ npx hardhat node
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

Account #0: xxxxx (10000 ETH)
Private Key: xxxxx

Account #1: xxxxx (10000 ETH)
Private Key: xxxxx

Account #2: xxxxx (10000 ETH)
Private Key: xxxxx

Account #3: xxxxx (10000 ETH)
Private Key: xxxxx

Account #4: xxxxx (10000 ETH)
Private Key: xxxxx

Account #5: xxxxx (10000 ETH)
Private Key: xxxxx

Account #6: xxxxx (10000 ETH)
Private Key: xxxxx

Account #7: xxxxx (10000 ETH)
Private Key: xxxxx

Account #8: xxxxx (10000 ETH)
Private Key: xxxxx

Account #9: xxxxx (10000 ETH)
Private Key: xxxxx

Account #10: xxxxx (10000 ETH)
Private Key: xxxxx

Account #11: xxxxx (10000 ETH)
Private Key: xxxxx

Account #12: xxxxx (10000 ETH)
Private Key: xxxxx

Account #13: xxxxx (10000 ETH)
Private Key: xxxxx

Account #14: xxxxx (10000 ETH)
Private Key: xxxxx

Account #15: xxxxx (10000 ETH)
Private Key: xxxxx

Account #16: xxxxx (10000 ETH)
Private Key: xxxxx

Account #17: xxxxx (10000 ETH)
Private Key: xxxxx

Account #18: xxxxx (10000 ETH)
Private Key: xxxxx

Account #19: xxxxx (10000 ETH)
Private Key: xxxxx

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

ここで、UNCHAINサイトに戻って、hardhat-toolboxも必要ということなのでインストール。

これでLesson 1は完了。

Lesson 2. コントラクト作成

pragma solidity ^0.8.9;

import "hardhat/console.sol";

contract WavePortal {
    constructor() {
        console.log("Here is my first smart contract!");
    }
}

contractはclassのようなもの。

constructorはいわゆるコンストラクター。

・・・あれ、これでおわり!?

Lesson 3. コントラクト実行

scripts/run.jsを記載。

const main = async () => {
  const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
  const waveContract = await waveContractFactory.deploy();
  const wavePortal = await waveContract.deployed();

  console.log("WavePortal address: ", wavePortal.address);
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

runMain();

で、実行。

$ npx hardhat run scripts/run.js
Compiled 2 Solidity files successfully
Here is my first smart contract!
WavePortal address:  xxxxx
  const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
  const waveContract = await waveContractFactory.deploy();
  const wavePortal = await waveContract.deployed();

main関数でやっていることはシンプルで、

  • hre.ethersgetContractFactoryでContractFactory生成
  • ContractFactoryのdeployでContract生成(デプロイ)
  • Contractのdeployedでデプロイするのを待って、、、別のインスタンスを返している???(要確認)

hreとはHardhat Runtime Environment(HRE)のこと。

ここもこれで終了。

Lesson 4. データ保存

保存ができてくると、アプリケーションっぽくなってくる。

import "hardhat/console.sol";

contract WavePortal {
  uint256 totalWaves;
  constructor() {
    console.log("Here is my first smart contract!");
  }
 function wave() public {
   totalWaves += 1;
   console.log("%s has waved!", msg.sender);
  }
  function getTotalWaves() public view returns (uint256) {
    console.log("We have %d total waves!", totalWaves);
    return totalWaves;
  }                              
}
  uint256 totalWaves;

の感じで、contract自体に変数を持たすと、ストレージに永続的に保存されるらしい。

 function wave() public {

publicが最後に来るのが気になるけど、、、

Solidityは4つの修飾子

  • public: 基本どこからでも呼び出しOK。→デフォルト(修飾子なし)はpublicだって。怖くないのか!?
  • private: 定義されたコントラクト内。
  • internal: 定義されたコントラクト内と継承されたコントラクトなので、protected的な。
  • external: 外部からのみ。→これは初めての概念・・・
   console.log("%s has waved!", msg.sender);

msgはどこにも宣言されてないのに使ってOKなものらしい。

msg.senderは当然「送信者」のことで、

  function getTotalWaves() public view returns (uint256) {

viewのように関数にだけつく修飾子があるとのこと。

  • view: 読み取り専用の関数。状態変数が変更されないようにする。
  • pure: 状態変数の読み込みや変更をしないで、「純粋に」パラメータ、ローカル変数だけを使用して値を返す。

これによってガス代が大きく変わる模様。

状態変数を事前に取ってきたりして、余計なメモリを展開しなくていいってことなんだろう。

実行してみる

main関数を更新する。

const main = async () => {
  const [owner, randomPerson] = await hre.ethers.getSigners();
  const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
  const waveContract = await waveContractFactory.deploy();
  const wavePortal = await waveContract.deployed();

  console.log("Contract deployed to:", wavePortal.address);
  console.log("Contract deployed by:", owner.address);

  let waveCount;
  waveCount = await waveContract.getTotalWaves();

  let waveTxn = await waveContract.wave();
  await waveTxn.wait();

  waveCount = await waveContract.getTotalWaves();
};

まずは最初の変更。

  const [owner, randomPerson] = await hre.ethers.getSigners();

hre.ethers.getSigners()はHardhatが提供する任意のアドレスを返す関数・・・ということはテスト用?開発用???

あ、やはりシミュレーション用とのこと。

  const [owner, randomPerson, randomPerson2] = await hre.ethers.getSigners();

と複数のrandomPersonを返すこともできる。

Lesson 5. コントラクトデプロイ(ローカル環境)

$ npx hardhat node

これでイーサリアムネットワークが立ち上がる。

※このネットワークにデプロイするので、Ctrl+Cとかで切らないように置いておく。

(別ターミナルにて)

次に、scripts/deploy.jsの編集。

const main = async () => {
  const [deployer] = await hre.ethers.getSigners();
  const accountBalance = await deployer.getBalance();
  const waveContract = await hre.ethers.getContractFactory("WavePortal");
  const wavePortal = await waveContract.deploy();

  console.log("Deploying contracts with account: ", deployer.address);
  console.log("Account balance: ", accountBalance.toString());
  console.log("Contract deployed to: ", wavePortal.address);
  console.log("Contract deployed by: ", deployer.address);
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

runMain();

scripts/deploy.jsでは余計なことをせずデプロイだけするような記述。

なので、scripts/run.jsがテスト用、scripts/deploy.jsが本番用と思っておけば良さそう。

scripts/run.jsは名前変えた方が良さそう。

で、デプロイ。

$ npx hardhat run scripts/deploy.js --network localhost
Deploying contracts with account:  xxxxx
Account balance:  10000000000000000000000
Contract deployed to:  xxxxx
Contract deployed by:  xxxxx

デプロイも問題なくなくできた。

$ npx haradhat node側のコンソールにも出力あり。

eth_accounts
eth_chainId (2)
eth_getBalance
eth_accounts
eth_blockNumber
eth_chainId (2)
eth_estimateGas
eth_getBlockByNumber
eth_feeHistory
eth_sendTransaction
  Contract deployment: WavePortal
  Contract address:    xxxxx
  Transaction:         xxxxx
  From:                xxxxx
  Value:               0 ETH
  Gas used:            345526 of 345526
  Block #1:            xxxxx

  console.log:
    Here is my first smart contract!

eth_chainId
eth_getTransactionByHash

これでSection 1も完了。

まとめ

とりあえず、簡単にCloud9でバックエンドの開発環境を整備できることがわかった。

あとは、Hardhatに尽きる。

サンプルのスマートコントラクトは、現状シンプルなので特に難しくないし。

とりあえず、Hardhatを使って、

  • プロジェクト作成(npx hardhat
  • コンパイル(npx hardhat compile) ※これは結局使っていない。実行と同時にコンパイルが走るのかな。
  • テスト(npx hardhat test) ※これは結局使っていない。おそらくmochaとかのテストフレームワークを使うとか書いていた。
  • 実行(npx hardhat run scripts/***.js
  • ネットワーク立ち上げ(npx hardhat node)

ができるようになれば、このSection 1は完璧でしょう。