メインコンテンツへスキップ

初心者向けのHello Worldスマートコントラクト - フルスタック

Solidity
Hardhat
Alchemy
スマートコントラクト
デプロイ
ブロックエクスプローラー
フロントエンド
トランザクション
初級
nstrike2
2021年10月25日
72 分の読書

このガイドは、ブロックチェーン開発の初心者で、どこから始めればよいか、スマートコントラクトのデプロイやインタラクトの方法がわからない方を対象としています。 MetaMask (opens in a new tab)Solidity (opens in a new tab)Hardhat (opens in a new tab)Alchemy (opens in a new tab) を使って、Goerliテストネットワークでシンプルなスマートコントラクトを作成し、デプロイする手順を説明します。

このチュートリアルを完了するには、Alchemyのアカウントが必要です。 無料アカウントにサインアップ (opens in a new tab)

ご不明な点がありましたら、Alchemy Discord (opens in a new tab) までお気軽にお問い合わせください。

パート1 - Hardhatを使用してスマートコントラクトを作成・デプロイする

Ethereumネットワークに接続する

イーサリアムチェーンにリクエストを行う方法はたくさんあります。 簡潔にするため、ブロックチェーン開発者プラットフォームであり、APIでもあるAlchemyの無料アカウントを使用します。これにより、自分でノードを実行することなく、Ethereumチェーンと通信できます。 Alchemyには、モニタリングと分析のための開発者ツールもあります。このチュートリアルでは、これらのツールを利用して、スマートコントラクトのデプロイで内部的に何が起こっているかを理解します。

アプリとAPIキーを作成する

Alchemyのアカウントを作成したら、アプリを作成してAPIキーを生成できます。 これにより、Goerliテストネットにリクエストを送信できるようになります。 テストネットに馴染みがない場合は、Alchemyのネットワーク選択ガイド (opens in a new tab) をお読みください。

Alchemyのダッシュボードで、ナビゲーションバーにある Apps ドロップダウンを見つけ、Create App をクリックします。

Hello worldアプリ作成

アプリに「Hello World」という名前を付け、簡単な説明を記述します。 環境として Staging を、ネットワークとして Goerli を選択します。

アプリ作成ビュー hello world

注: 必ず Goerli を選択してください。選択しないと、このチュートリアルは機能しません。

Create app をクリックします。 作成したアプリが下の表に表示されます。

Ethereumアカウントを作成する

トランザクションを送受信するには、Ethereumアカウントが必要です。 ここでは、ユーザーがEthereumアカウントアドレスを管理できる、ブラウザ上の仮想ウォレットであるMetaMaskを使用します。

MetaMaskアカウントは、こちら (opens in a new tab)から無料でダウンロードして作成できます。 アカウントを作成するとき、またはすでにアカウントをお持ちの場合は、右上の「Goerli Test Network」に必ず切り替えてください (実在の通貨を扱わないようにするため)。

ステップ4: フォーセットからイーサを追加する

スマートコントラクトをテストネットワークにデプロイするには、偽のETHが必要です。 GoerliネットワークでETHを取得するには、Goerliフォーセットにアクセスし、Goerliアカウントアドレスを入力します。 Goerliフォーセットは最近、少し信頼性が低い場合があります。試せるオプションの一覧については、テストネットワークのページ を参照してください:

注: ネットワークの混雑により、しばらく時間がかかる場合があります。 ``

ステップ5: 残高を確認する

ウォレットにETHが入っていることを再確認するために、Alchemyのcomposerツール (opens in a new tab) を使って eth_getBalance (opens in a new tab) リクエストを実行してみましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 詳細については、composerツールの使用方法に関するAlchemyの短いチュートリアル (opens in a new tab) をご覧ください。

MetaMaskアカウントのアドレスを入力し、Send Request をクリックします。 以下のようなコードスニペットのレスポンスが表示されます。

1{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" }

注: この結果はETHではなく、wei単位です。 Weiはetherの最小単位として使用されます。

ふう! 私たちの偽物のお金はすべてそこにあります。

ステップ6: プロジェクトを初期化する

まず、プロジェクト用のフォルダを作成する必要があります。 コマンドラインに移動し、次のように入力します。

1mkdir hello-world
2cd hello-world

プロジェクトフォルダに入ったので、npm initを使用してプロジェクトを初期化します。

まだnpmがインストールされていない場合は、Node.jsとnpmをインストールするための指示 (opens in a new tab)に従ってください。

このチュートリアルでは、初期化における質問にどのように答えるかには重点を置いていません。 参考までに、私たちは次のように行いました。

1package name: (hello-world)
2version: (1.0.0)
3description: hello world smart contract
4entry point: (index.js)
5test command:
6git repository:
7keywords:
8author:
9license: (ISC)
10
11About to write to /Users/.../.../.../hello-world/package.json:
12
13{
14 "name": "hello-world",
15 "version": "1.0.0",
16 "description": "hello world smart contract",
17 "main": "index.js",
18 "scripts": {
19 "test": "echo \"Error: no test specified\" && exit 1"
20 },
21 "author": "",
22 "license": "ISC"
23}
すべて表示

package.jsonを承認すれば完了です!

ステップ7: Hardhatをダウンロードする

Hardhatは、イーサリアムのソフトウェアをコンパイル、デプロイ、テスト、デバッグするための開発環境です。 デベロッパーがライブチェーンにデプロイする前に、スマートコントラクトや分散型アプリケーション(Dapp)をローカルに構築する際に役立ちます。

hello-worldプロジェクト内で次を実行します。

1npm install --save-dev hardhat

インストール手順 (opens in a new tab)の詳細については、このページをご覧ください。

ステップ8: Hardhatプロジェクトを作成する

hello-worldプロジェクトフォルダ内で、以下を実行します:

1npx hardhat

ウェルカムメッセージと、次に何をするのかを選択できるオプションが表示されます。 「create an empty hardhat.config.js」を選択してください。

1888 888 888 888 888
2888 888 888 888 888
3888 888 888 888 888
48888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
5888 888 "88b 888P" d88" 888 888 "88b "88b 888
6888 888 .d888888 888 888 888 888 888 .d888888 888
7888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
9
10👷 Welcome to Hardhat v2.0.11 👷‍
11
12What do you want to do? …
13Create a sample project
14❯ Create an empty hardhat.config.js
15Quit
すべて表示

これにより、プロジェクトに hardhat.config.js ファイルが生成されます。 チュートリアルの後半で、これを使用してプロジェクトのセットアップを指定します。

ステップ9: プロジェクトフォルダを追加する

プロジェクトを整理するために、2つの新しいフォルダを作成しましょう。 コマンドラインで、hello-world プロジェクトのルートディレクトリに移動し、次のように入力します:

1mkdir contracts
2mkdir scripts
  • contracts/ には、hello worldスマートコントラクトのコードファイルを保存します
  • scripts/ には、コントラクトをデプロイして対話するためのスクリプトを保存します

ステップ10: コントラクトを作成する

「いつになったらコードを書くのだろう?」と思っているかもしれませんね。 その時が来ました!

お好きなエディタでhello-worldプロジェクトを開いてください。 スマートコントラクトは、最も一般的にはSolidityで記述されており、今回もSolidityを使ってスマートコントラクトを作成します。

  1. contracts フォルダに移動し、HelloWorld.sol という名前の新しいファイルを作成します。
  2. 以下は、このチュートリアルで使用するHello Worldスマートコントラクトのサンプルです。 以下の内容を HelloWorld.sol ファイルにコピーしてください。

注: このコントラクトが何をするのかを理解するために、必ずコメントをお読みください。

1// Solidityのバージョンをセマンティックバージョニングで指定します。
2// 詳細: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity >=0.7.3;
4
5// `HelloWorld` という名前のコントラクトを定義します。
6// コントラクトは関数とデータ (その状態) の集合です。一度デプロイされると、コントラクトはEthereumブロックチェーン上の特定のアドレスに存在します。詳細: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
7contract HelloWorld {
8
9 // update関数が呼び出されたときに発行されます
10 //スマートコントラクトのイベントは、ブロックチェーン上で何かが起こったことをコントラクトがアプリのフロントエンドに伝える方法です。フロントエンドは特定のイベントを「リッスン」し、それが起こったときに行動を起こすことができます。
11 event UpdatedMessages(string oldStr, string newStr);
12
13 // `string` 型の状態変数 `message` を宣言します。
14 // 状態変数は、その値がコントラクトのストレージに永続的に保存される変数です。`public` キーワードにより、変数はコントラクトの外部からアクセス可能になり、他のコントラクトやクライアントが値をアクセスするために呼び出せる関数が作成されます。
15 string public message;
16
17 // 多くのクラスベースのオブジェクト指向言語と同様に、コンストラクタはコントラクト作成時に一度だけ実行される特別な関数です。
18 // コンストラクタはコントラクトのデータを初期化するために使用されます。詳細:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
19 constructor(string memory initMessage) {
20
21 // 文字列引数 `initMessage` を受け取り、その値をコントラクトの `message` ストレージ変数に設定します)。
22 message = initMessage;
23 }
24
25 // 文字列引数を受け取り、 `message` ストレージ変数を更新する公開関数です。
26 function update(string memory newMessage) public {
27 string memory oldMsg = message;
28 message = newMessage;
29 emit UpdatedMessages(oldMsg, newMessage);
30 }
31}
すべて表示

これは、作成時にメッセージを保存する基本的なスマートコントラクトです。 update 関数を呼び出すことで更新できます。

ステップ11: MetaMaskとAlchemyをプロジェクトに接続する

MetaMaskウォレットとAlchemyアカウントを作成し、スマートコントラクトも作成しました。次はこの3つを接続しましょう。

ウォレットから送信されるすべてのトランザクションには、一意の秘密鍵を使用した署名が必要です。 プログラムにこの許可を与えるために、秘密鍵を環境ファイルに安全に保存することができます。 AlchemyのAPIキーもここに保存します。

トランザクションの送信についてさらに詳しく知るには、web3を使用したトランザクションの送信に関するこのチュートリアル (opens in a new tab) を確認してください。

まず、プロジェクトディレクトリにdotenvパッケージをインストールします。

1npm install dotenv --save

次に、プロジェクトのルートディレクトリに .env ファイルを作成します。 そのファイルに、MetaMaskの秘密鍵とHTTP Alchemy APIのURLを追加します。

環境ファイルは .env という名前にする必要があります。そうしないと、環境ファイルとして認識されません。

process.env.env-custom など、他の名前にしないでください。

Alchemy API キーを取得するためのアニメーション ウォークスルー

.envは次のようになります:

1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
2PRIVATE_KEY = "your-metamask-private-key"

これらをコードに実際に接続するために、ステップ13でhardhat.config.jsファイル内のこれらの変数を参照します。

ステップ12: Ethers.jsをインストールする

Ethers.jsは、標準的なJSON-RPCメソッド (opens in a new tab)をよりユーザーフレンドリーなメソッドでラップすることにより、Ethereumとのインタラクションやリクエストを容易にするライブラリです。

Hardhatでは、追加のツールや拡張機能のためにプラグイン (opens in a new tab)を統合することができます。 コントラクトのデプロイにはEthersプラグイン (opens in a new tab)を利用します。

プロジェクトのホームディレクトリで以下を実行します。

npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"

ステップ13: hardhat.config.jsを更新する

ここまでで、いくつかの依存関係とプラグインを追加しました。次に、プロジェクトがそれらすべてを認識できるように、hardhat.config.jsを更新する必要があります。

hardhat.config.jsを次のように更新します:

1/**
2 * @type import('hardhat/config').HardhatUserConfig
3 */
4
5require("dotenv").config()
6require("@nomiclabs/hardhat-ethers")
7
8const { API_URL, PRIVATE_KEY } = process.env
9
10module.exports = {
11 solidity: "0.7.3",
12 defaultNetwork: "goerli",
13 networks: {
14 hardhat: {},
15 goerli: {
16 url: API_URL,
17 accounts: [`0x${PRIVATE_KEY}`],
18 },
19 },
20}
すべて表示

ステップ14: コントラクトをコンパイルする

ここまでの作業がうまくいっていることを確認するために、コントラクトをコンパイルしてみましょう。 compileタスクは、組み込みのHardhatタスクの1つです。

コマンドラインで以下を実行します。

npx hardhat compile

SPDX license identifier not provided in source file という警告が表示されるかもしれませんが、心配する必要はありません。それ以外はすべて問題ないはずです! うまくいかない場合は、いつでもAlchemy Discord (opens in a new tab)でメッセージを送ることができます。

ステップ15: デプロイスクリプトを作成する

コントラクトの作成と設定ファイルの作成が完了したら、いよいよコントラクトのデプロイのためのスクリプトを作成します。

scripts/フォルダに移動してdeploy.jsという名前の新しいファイルを作成し、次の内容を追加します:

1async function main() {
2 const HelloWorld = await ethers.getContractFactory("HelloWorld")
3
4 // デプロイを開始し、コントラクトオブジェクトに解決されるpromiseを返す
5 const hello_world = await HelloWorld.deploy("Hello World!")
6 console.log("Contract deployed to address:", hello_world.address)
7}
8
9main()
10 .then(() => process.exit(0))
11 .catch((error) => {
12 console.error(error)
13 process.exit(1)
14 })
すべて表示

Hardhatは、コントラクトのチュートリアル (opens in a new tab)で、これらのコードの各行が何をするかを非常にうまく説明しています。ここではその説明を採用しました。

1const HelloWorld = await ethers.getContractFactory("HelloWorld")

ethers.jsの ContractFactory は、新しいスマートコントラクトをデプロイするために使用される抽象化です。したがって、ここでの HelloWorld は、私たちのhello worldコントラクトのインスタンスのためのファクトリ (opens in a new tab)です。 hardhat-ethersプラグインを使用する場合、ContractFactoryContractのインスタンスは、デフォルトで最初の署名者 (所有者) に接続されます。

1const hello_world = await HelloWorld.deploy()

ContractFactorydeploy()を呼び出すとデプロイが開始され、Contractオブジェクトに解決されるPromiseが返されます。 これは、スマートコントラクトの各関数に対するメソッドを持つオブジェクトです。

ステップ16: コントラクトをデプロイする

ようやく、スマートコントラクトをデプロイする準備が整いました。 コマンドラインに移動し、次を実行します:

npx hardhat run scripts/deploy.js --network goerli

次のような画面が表示されるはずです。

コントラクトがデプロイされたアドレス: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570

このアドレスを保存してください。 このアドレスはチュートリアルの後半で使用します。

Goerli etherscan (opens in a new tab) にアクセスしてコントラクトアドレスを検索すると、正常にデプロイされたことを確認できるはずです。 トランザクションは以下のようなものになります。

FromアドレスはMetaMaskアカウントのアドレスと一致し、ToアドレスにはContract Creationと表示されます。 トランザクションをクリックすると、Toフィールドにコントラクトアドレスが表示されます。

おめでとうございます! Ethereumテストネットにスマートコントラクトをデプロイできました。

内部で何が起こっているのかを理解するために、Alchemyダッシュボード (opens in a new tab)のExplorerタブに移動してみましょう。 複数のAlchemyアプリをお持ちの場合は、必ずアプリでフィルタリングし、Hello Worldを選択してください。

ここでは、.deploy()関数を呼び出した際に、Hardhat/Ethersが内部で行ったいくつかのJSON-RPCメソッドを見ることができます。 ここでの2つの重要なメソッドは、コントラクトをGoerliチェーンに書き込むリクエストである eth_sendRawTransaction (opens in a new tab) と、ハッシュが与えられたトランザクションに関する情報を読み取るリクエストである eth_getTransactionByHash (opens in a new tab) です。 トランザクションの送信についてさらに詳しく知るには、Web3を使用したトランザクション送信に関するチュートリアルを確認してください。

パート2: スマートコントラクトとインタラクトする

スマートコントラクトをGoerliネットワークに正常にデプロイできました。それでは、スマートコントラクトとやり取りする方法について学びましょう。

interact.jsファイルを作成する

このファイルに、インタラクトするスクリプトを記述します。 パート1でインストールしたEthers.jsライブラリを使用します。

scripts/フォルダ内に、interact.jsという名前の新しいファイルを作成し、次のコードを追加します。

1// interact.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS

.envファイルを更新する

新しい環境変数を使用するため、以前に作成した.envファイルに定義する必要があります。

AlchemyのAPI_KEYとスマートコントラクトがデプロイされているCONTRACT_ADDRESSの定義を加える必要があります。

.envファイルは、以下のようになっていなければなりません。

# .env
API_URL = "https://eth-goerli.alchemyapi.io/v2/<your-api-key>"
API_KEY = "<your-api-key>"
PRIVATE_KEY = "<your-metamask-private-key>"
CONTRACT_ADDRESS = "0x<your contract address>"

コントラクトABIを取得する

コントラクトのは、スマートコントラクトとインタラクトするためのインターフェイスです。 Hardhatは自動的にABIを生成して、HelloWorld.jsonファイルに保存します。 ABIを使うには、interact.jsファイルに次のコードを追加して、コンテンツをパースする必要があります。

1// interact.js
2const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")

ABIを確認したい場合は、コンソールに出力できます:

1console.log(JSON.stringify(contract.abi))

コンソールに出力されたABIを確認するには、ターミナルに移動して次のコマンドを実行します。

npx hardhat run scripts/interact.js

コントラクトのインスタンスを作成する

コントラクトを操作するには、コード内にコントラクトのインスタンスを作成する必要があります。 Ethers.jsでこれを行うには、次の3つのコンセプトを扱う必要があります。

  1. Provider - ブロックチェーンへの読み取りおよび書き込みアクセスを提供するノードプロバイダです。
  2. Signer - トランザクションに署名するEthereumアカウントを表します。
  3. Contract - オンチェーンにデプロイされた特定のコントラクトを表すEthers.jsのオブジェクトです。

前の手順で取得したコントラクトABIを使って、コントラクトのインスタンスを作成します。

1// interact.js
2
3// Provider
4const alchemyProvider = new ethers.providers.AlchemyProvider(
5 (network = "goerli"),
6 API_KEY
7)
8
9// Signer
10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
11
12// Contract
13const helloWorldContract = new ethers.Contract(
14 CONTRACT_ADDRESS,
15 contract.abi,
16 signer
17)
すべて表示

Provider、Signer、Contractの詳細については、ethers.jsドキュメント (opens in a new tab)をご覧ください。

initメッセージを読み込む

initMessage = "Hello world!"を使用してコントラクトをデプロイしたことを覚えていますか? ここでは、スマートコントラクトに保存されているメッセージを読み取り、コンソールに出力します。

JavaScriptでは、ネットワークとのインタラクトで非同期関数を使います。 非同期関数についてさらに詳しく知るには、このmediumの記事 (opens in a new tab)をお読みください。

以下のコードを使用して、スマートコントラクトのmessage関数を呼び出し、initメッセージを読み取ります。

1// interact.js
2
3// ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("The message is: " + message)
8}
9main()
すべて表示

ターミナルで npx hardhat run scripts/interact.js を使用してファイルを実行すると、次のようなレスポンスが表示されます。

1The message is: Hello world!

おめでとうございます! Ethereumブロックチェーンからスマートコントラクトのデータを正常に読み取ることができました。お見事!

メッセージを更新する

メッセージを読み取るだけでなく、update関数を使ってスマートコントラクトに保存されたメッセージを更新することもできます。 かなりクールでしょう?

メッセージを更新するには、インスタンス化されたContractオブジェクトでupdate関数を直接呼び出します。

1// interact.js
2
3// ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("The message is: " + message)
8
9 console.log("Updating the message...")
10 const tx = await helloWorldContract.update("This is the new message.")
11 await tx.wait()
12}
13main()
すべて表示

11行目で、返されたトランザクションオブジェクトに対して .wait() を呼び出していることに注目してください。 これにより、スクリプトが関数を終了する前に、トランザクションがブロックチェーン上でマイニングされるまで待機することが保証されます。 .wait() の呼び出しを含めなかった場合、スクリプトは、コントラクト内で更新された message の値を表示しないことがあります。

新しいメッセージを読み込む

前の手順を繰り返して、更新された message の値を読み取ることができるはずです。 少し時間を取って、新しい値を表示するために必要な変更を加えられるか試してみましょう!

ヒントが必要な場合は、この時点でinteract.jsファイルがどのようになるかを示します。

1// interact.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
6
7const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")
8
9// provider - Alchemy
10const alchemyProvider = new ethers.providers.AlchemyProvider(
11 (network = "goerli"),
12 API_KEY
13)
14
15// signer - you
16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
17
18// contract instance
19const helloWorldContract = new ethers.Contract(
20 CONTRACT_ADDRESS,
21 contract.abi,
22 signer
23)
24
25async function main() {
26 const message = await helloWorldContract.message()
27 console.log("The message is: " + message)
28
29 console.log("Updating the message...")
30 const tx = await helloWorldContract.update("this is the new message")
31 await tx.wait()
32
33 const newMessage = await helloWorldContract.message()
34 console.log("The new message is: " + newMessage)
35}
36
37main()
すべて表示

このスクリプトを実行するだけで、古いメッセージ、更新ステータス、および新しいメッセージがターミナルに出力されるのを確認できるはずです。

npx hardhat run scripts/interact.js --network goerli

1The message is: Hello World!
2Updating the message...
3The new message is: This is the new message.

このスクリプトの実行中、新しいメッセージが読み込まれる前に、 Updating the message... のステップの読み込みにしばらく時間がかかることに気づくかもしれません。 これはマイニングプロセスによるものです。マイニング中のトランザクションの追跡に興味があるならば、Alchemyメンプール (opens in a new tab)にアクセスしてトランザクションのステータスを確認できます。 トランザクションがドロップされた場合は、Goerli Etherscan (opens in a new tab) を確認してトランザクションハッシュを検索することも役立ちます。

パート3: スマートコントラクトをEtherscanに公開する

あなたは、スマートコントラクトに命を吹き込むために大変な努力をしました。さあ、その成果を世界に共有しましょう!

Etherscanでスマートコントラクトを検証すると、誰でもソースコードを表示して、あなたのスマートコントラクトとインタラクトできるようになります。 さあ、始めましょう!

ステップ1: EtherscanアカウントでAPIキーを生成する

EtherscanのAPIキーは、公開しようとしているスマートコントラクトを所有していることを確認するために必要になります。

Etherscanアカウントをお持ちでない場合は、アカウントにサインアップ (opens in a new tab)してください。

ログインしたら、ナビゲーションバーでユーザー名を見つけ、その上にカーソルを合わせて My profile ボタンを選択します。

プロフィールページにサイドナビゲーションバーが表示されます。 サイドナビゲーションバーで、API Keysを選択します。 次に、「Add」ボタンを押して新しいAPIキーを作成し、アプリに hello-world という名前を付けて、「Create New API Key」ボタンを押します。

新しいAPIキーがAPIキーテーブルに表示されるはずです。 APIキーをクリップボードにコピーします。

次に、EtherscanのAPIキーを.envファイルに加える必要があります。

追加した後、.envファイルは次のようになります。

1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
2PUBLIC_KEY = "your-public-account-address"
3PRIVATE_KEY = "your-private-account-address"
4CONTRACT_ADDRESS = "your-contract-address"
5ETHERSCAN_API_KEY = "your-etherscan-key"

Hardhatでデプロイされたスマートコントラクト

hardhat-etherscanをインストールする

Hardhatを使ってコントラクトをEtherscanへ公開するのは簡単です。 まず、hardhat-etherscanプラグインをインストールする必要があります。 hardhat-etherscanは、スマートコントラクトのソースコードとEtherscan上のABIを自動的に検証します。 インストールするには、hello-worldディレクトリで次のコマンドを実行します。

1npm install --save-dev @nomiclabs/hardhat-etherscan

インストールしたら、hardhat.config.jsの先頭に次のステートメントを含め、Etherscanの構成オプションを追加します。

1// hardhat.config.js
2
3require("dotenv").config()
4require("@nomiclabs/hardhat-ethers")
5require("@nomiclabs/hardhat-etherscan")
6
7const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env
8
9module.exports = {
10 solidity: "0.7.3",
11 defaultNetwork: "goerli",
12 networks: {
13 hardhat: {},
14 goerli: {
15 url: API_URL,
16 accounts: [`0x${PRIVATE_KEY}`],
17 },
18 },
19 etherscan: {
20 // Etherscan用のAPIキー
21 // https://etherscan.io/ で取得
22 apiKey: ETHERSCAN_API_KEY,
23 },
24}
すべて表示

Etherscanでスマートコントラクトを検証する

すべてのファイルが保存され、すべての.env変数が正しく構成されていることを確認してください。

verifyタスクを実行し、コントラクトのアドレスと、コントラクトがデプロイされているネットワークを渡します。

1npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!'

DEPLOYED_CONTRACT_ADDRESSがGoerliテストネットワーク上にデプロイされたスマートコントラクトのアドレスであることを確認してください。 また、最後の引数('Hello World!')は、パート1のデプロイスクリプト作成手順で使用した文字列値と同じでなければなりません。

すべてが順調に進めば、ターミナルに次のメッセージが表示されます。

1Successfully submitted source code for contract
2contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address
3for verification on Etherscan. Waiting for verification result...
4
5
6Successfully verified contract HelloWorld on Etherscan.
7https://goerli.etherscan.io/address/<contract-address>#contracts

おめでとうございます! これで、あなたのスマートコントラクトコードはEtherscan上にあります。

Etherscanであなたのスマートコントラクトを確認しましょう!

ターミナルに表示されているリンクに移動すると、Etherscanで公開されているスマートコントラクトコードとABIが表示されます。

ヤッター!やりましたね! これで、誰でもスマートコントラクトを呼び出したり、書き込んだりできるようになりました。 次にあなたが何を構築するか楽しみにしています。

パート4 - スマートコントラクトをフロントエンドと統合する

このチュートリアルを終えると、次の方法がわかるようになります。

  • MetaMaskウォレットをdappに接続する
  • Alchemy Web3 (opens in a new tab) APIを使用してスマートコントラクトからデータを読み取る
  • MetaMaskを使用してEthereumトランザクションに署名する

このdappでは、フロントエンドフレームワークとしてReact (opens in a new tab)を使用します。ただし、このプロジェクトではWeb3機能を導入することに主に焦点を当てているため、Reactの基礎を詳しく説明する時間はあまりかけない点に注意してください。

前提条件として、Reactについて初心者レベルの理解をしている必要があります。 そうでない場合は、公式のReact入門チュートリアル (opens in a new tab)を完了することをお勧めします。

スターターファイルをクローンする

まず、hello-world-part-four GitHubリポジトリ (opens in a new tab)にアクセスして、このプロジェクトのスターターファイルを取得し、このリポジトリをローカルマシンにクローンします。

クローンしたリポジトリをローカルで開きます。 starter-filescompletedの2つのフォルダが含まれていることに注意してください。

  • starter-files - このディレクトリで作業します。UIをEthereumウォレットおよびパート3でEtherscanに公開したスマートコントラクトに接続します。
  • completedには、チュートリアル全体が完了したものが入っています。行き詰まった場合にのみ、参考として使ってください。

次に、starter-filesのコピーをお気に入りのコードエディタで開き、srcフォルダに移動します。

私たちが書くコードはすべてsrcフォルダの下に置かれます。 HelloWorld.jsコンポーネントとutil/interact.jsJavaScriptファイルを編集して、プロジェクトにWeb3機能を追加します。

スターターファイルを確認する

コーディングを開始する前に、スターターファイルで提供されるものを探索してみましょう。

Reactプロジェクトを実行する

まずは、ブラウザでReactプロジェクトを実行しましょう。 Reactの素晴らしいところは、一度ブラウザでプロジェクトを実行すると、保存した変更がブラウザでも同時に更新されることです。

プロジェクトを実行するには、starter-filesフォルダのルートディレクトリに移動し、ターミナルでnpm installを実行してプロジェクトの依存関係をインストールします。

cd starter-files
npm install

インストールが完了したら、ターミナルでnpm startを実行します:

npm start

これにより、ブラウザでhttp://localhost:3000/ (opens in a new tab)が開かれ、プロジェクトのフロントエンドが表示されます。 これは、1つのフィールド (スマートコントラクトに保存されているメッセージを更新する場所)、「Connect Wallet」ボタン、および「Update」ボタンで構成されています。

どちらのボタンをクリックしても機能しないことに気づくでしょう。これは、まだその機能をプログラムする必要があるためです。

HelloWorld.jsコンポーネント

エディタのsrcフォルダに戻り、HelloWorld.jsファイルを開きましょう。 このファイルには、これから作業を進めていく主要なReactコンポーネントが含まれています。すべての内容を理解することが非常に重要です。

このファイルの先頭には、プロジェクトを実行するために必要な、Reactライブラリ、useEffectフックとuseStateフック、./util/interact.jsからのいくつかのアイテム (これらについては、すぐに詳しく説明します!)、そしてAlchemyのロゴなど、いくつかのimport文があることに気づくでしょう。

1// HelloWorld.js
2
3import React from "react"
4import { useEffect, useState } from "react"
5import {
6 helloWorldContract,
7 connectWallet,
8 updateMessage,
9 loadCurrentMessage,
10 getCurrentWalletConnected,
11} from "./util/interact.js"
12
13import alchemylogo from "./alchemylogo.svg"
すべて表示

次に、特定のイベントの後に更新するステート変数があります。

1// HelloWorld.js
2
3//State variables
4const [walletAddress, setWallet] = useState("")
5const [status, setStatus] = useState("")
6const [message, setMessage] = useState("No connection to the network.")
7const [newMessage, setNewMessage] = useState("")

それぞれの変数は以下の内容を表します。

  • walletAddress - ユーザーのウォレットアドレスを格納する文字列
  • status- ユーザーにdappのインタラクト方法を案内する補助メッセージを格納する文字列
  • message - スマートコントラクトの現在のメッセージを格納する文字列
  • newMessage - スマートコントラクトに書き込まれる新しいメッセージを格納する文字列

ステート変数の後に、useEffectaddSmartContractListeneraddWalletListenerconnectWalletPressedonUpdatePressedの5つの未実装の関数があります。 次に、それらが何をするのかを説明します。

1// HelloWorld.js
2
3//called only once
4useEffect(async () => {
5 //TODO: implement
6}, [])
7
8function addSmartContractListener() {
9 //TODO: implement
10}
11
12function addWalletListener() {
13 //TODO: implement
14}
15
16const connectWalletPressed = async () => {
17 //TODO: implement
18}
19
20const onUpdatePressed = async () => {
21 //TODO: implement
22}
すべて表示
  • useEffect (opens in a new tab) - これは、コンポーネントがレンダリングされた後に呼び出されるReactフックです。 空の配列[]のプロップが渡されているため (4行目を参照)、コンポーネントの_最初の_レンダリングでのみ呼び出されます。 ここでは、スマートコントラクトに保存されている現在のメッセージをロードし、スマートコントラクトとウォレットリスナーを呼び出し、ウォレットが既に接続されているかどうかを反映してUIを更新します。
  • addSmartContractListener - この関数では、HelloWorldコントラクトのUpdatedMessagesイベントを監視し、スマートコントラクトでメッセージが変更されたときにUIを更新するリスナーを設定します。
  • addWalletListener - この関数では、ユーザーがウォレットを切断したときやアドレスを切り替えたときなど、ユーザーのMetaMaskウォレットのステートの変化を検出するリスナーを設定します。
  • connectWalletPressed- この関数は、ユーザーのMetaMaskウォレットをdappに接続するのに呼び出されます。
  • onUpdatePressed - この関数は、ユーザーがスマートコントラクトに保存されているメッセージを更新したいときに呼び出されます。

このファイルの終盤には、コンポーネントのUIがあります。

1// HelloWorld.js
2
3//the UI of our component
4return (
5 <div id="container">
6 <img id="logo" src={alchemylogo}></img>
7 <button id="walletButton" onClick={connectWalletPressed}>
8 {walletAddress.length > 0 ? (
9 "Connected: " +
10 String(walletAddress).substring(0, 6) +
11 "..." +
12 String(walletAddress).substring(38)
13 ) : (
14 <span>Connect Wallet</span>
15 )}
16 </button>
17
18 <h2 style={{ paddingTop: "50px" }}>Current Message:</h2>
19 <p>{message}</p>
20
21 <h2 style={{ paddingTop: "18px" }}>New Message:</h2>
22
23 <div>
24 <input
25 type="text"
26 placeholder="Update the message in your smart contract."
27 onChange={(e) => setNewMessage(e.target.value)}
28 value={newMessage}
29 />
30 <p id="status">{status}</p>
31
32 <button id="publishButton" onClick={onUpdatePressed}>
33 Update
34 </button>
35</div>
36
37</div>
38)
すべて表示

このコードを注意深く読むと、さまざまなステート変数がUIのどの場所で使用されているかがわかります。

  • 6~12行目では、ユーザーのウォレットが接続されている場合 (すなわちwalletAddress.length > 0)、ID「walletButton」のボタンに省略されたユーザーのwalletAddressが表示されます。それ以外の場合は、単に「Connect Wallet」と表示されます。
  • 17行目では、message文字列でキャプチャされたスマートコントラクトに保存されている現在のメッセージを表示します。
  • 23~26行目では、制御されたコンポーネント (opens in a new tab)を使用して、テキストフィールドの入力が変化したときにnewMessageステート変数を更新します。

ステート変数に加えて、IDがpublishButtonwalletButtonであるボタンがそれぞれクリックされると、connectWalletPressedおよびonUpdatePressed関数が呼び出されることがわかります。

最後に、このHelloWorld.jsコンポーネントがどこに加えられるかについて説明します。

App.jsファイルは、他のすべてのコンポーネントのコンテナとして機能するReactのメインコンポーネントですが、このファイルを表示すると、HelloWorld.jsコンポーネントが7行目に挿入されていることが分かります。

最後に、提供されているもう1つのファイル、interact.jsファイルを確認してみましょう。

interact.jsファイル

M-V-C (opens in a new tab)パラダイムに従うため、dappのロジック、データ、ルールを管理するすべての関数を含む個別のファイルを作成し、それらの関数をフロントエンド (私たちのHelloWorld.jsコンポーネント) にエクスポートできるようにします。

👆🏽まさにこれがinteract.jsファイルの目的です!

srcディレクトリのutilフォルダに移動すると、interact.jsというファイルが含まれていることがわかります。これには、すべてのスマートコントラクトとのインタラクション、ウォレット関数、変数が含まれています。

1// interact.js
2
3//export const helloWorldContract;
4
5export const loadCurrentMessage = async () => {}
6
7export const connectWallet = async () => {}
8
9const getCurrentWalletConnected = async () => {}
10
11export const updateMessage = async (message) => {}
すべて表示

ファイルの先頭で、helloWorldContractオブジェクトがコメントアウトされていることがわかります。 このチュートリアルの後半で、このオブジェクトのコメントを外し、この変数でスマートコントラクトをインスタンス化し、それをHelloWorld.jsコンポーネントにエクスポートします。

helloWorldContractオブジェクトの後の4つの未実装の関数は、次のことを行います。

  • loadCurrentMessage - この関数は、スマートコントラクトに保存されている現在のメッセージをロードするロジックを扱います。 Alchemy Web3 API (opens in a new tab)を使用して、Hello Worldスマートコントラクトへの_読み取り_呼び出しを行います。
  • connectWallet - この関数は、ユーザーのMetaMaskをdappに接続します。
  • getCurrentWalletConnected - この関数は、ページの読み込み時にEthereumアカウントが既にdappに接続されているかどうかを確認し、それに応じてUIを更新します。
  • updateMessage - この関数は、スマートコントラクトに保存されているメッセージを更新します。 Hello Worldスマートコントラクトで_書き込み_呼び出しが行われるため、ユーザーのMetaMaskウォレットでは、メッセージを更新するためにEthereumトランザクションに署名する必要があります。

何をするか理解したので、スマートコントラクトから読み取る方法を解明していきましょう。

ステップ3: スマートコントラクトから読み取る

スマートコントラクトから読み取るには、次の設定を正しく行う必要があります。

  • EthereumチェーンへのAPI接続
  • スマートコントラクトのロードされたインスタンス
  • スマートコントラクトの関数を呼び出す関数
  • スマートコントラクトから読み取っているデータが変更されたときの更新を監視するリスナー

手順がたくさんあるように感じますが、心配しないでください! それぞれの方法を1つずつ説明していきます。 :​)

EthereumチェーンへのAPI接続を確立する

このチュートリアルのパート2で、Alchemy Web3キーを使用してスマートコントラクトから読み取った (opens in a new tab)ことを覚えていますか? チェーンから読み取るには、dappでAlchemy Web3キーも必要になります。

まだインストールしていない場合は、まずstarter-filesのルートディレクトリに移動し、ターミナルで次のコマンドを実行してAlchemy Web3 (opens in a new tab)をインストールしてください。

1npm install @alch/alchemy-web3

Alchemy Web3 (opens in a new tab)Web3.js (opens in a new tab)のラッパーであり、強化されたAPIメソッドやその他の重要なメリットを提供し、web3開発者としての作業を容易にします。 最小限の設定で使えるように設計されているので、アプリですぐに使用可能です。

次に、dotenv (opens in a new tab)パッケージをプロジェクトディレクトリにインストールします。これにより、APIキーを取得した後に安全な場所に保管できるようになります。

1npm install dotenv --save

dappでは、HTTP APIキーの代わりにWebsockets APIキーを使用します。これにより、スマートコントラクトに保存されたメッセージが変更されたときに検出するリスナーを設定できます。

APIキーを取得したら、ルートディレクトリに .envファイルを作成し、Alchemy Websocketsの URLをそのファイルに追加します。 その後、.envファイルは次のようになります。

1REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/<key>

これで、dappにAlchemy Web3エンドポイントを設定する準備が整いました。 utilフォルダー内に入っているinteract.jsに戻り、ファイルの先頭に次のコードを加えてください。

1// interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8//export const helloWorldContract;

上記のコードでは、まず.envファイルから Alchemyキーをインポートし、次にalchemyKeycreateAlchemyWeb3に渡してAlchemy Web3エンドポイントを確立しています。

エンドポイントの準備できたので、スマートコントラクトをロードするときです!

Hello Worldスマートコントラクトをロードする

Hello Worldスマートコントラクトをロードするには、そのコントラクトアドレスとABIが必要です。これらは、このチュートリアルのパート3を完了していれば、Etherscanで見つけることができます。

EtherscanからコントラクトABIを取得する方法

このチュートリアルのパート3を飛ばした場合は、アドレス0x6f3f635A9762B47954229Ea479b4541eAF402A6A (opens in a new tab)のHelloWorldコントラクトを使用できます。 そのABIはこちら (opens in a new tab)で見つけることができます。

コントラクトのABIは、コントラクトが呼び出す関数を指定し、関数が期待するフォーマットでデータを確実に返すようにするために必要です。 コントラクトABIをコピーしたら、それをcontract-abi.jsonという名前のJSONファイルとしてsrcディレクトリに保存しましょう。

contract-abi.jsonは、srcフォルダーに格納されている必要があります。

コントラクトアドレス、ABI、Alchemy Web3エンドポイントを用意したので、contractメソッド (opens in a new tab)を使ってスマートコントラクトのインスタンスをロードすることができます。 コントラクトABIをinteract.jsファイルにインポートし、コントラクトアドレスを加えます。

1// interact.js
2
3const contractABI = require("../contract-abi.json")
4const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"

ついに、helloWorldContract変数のコメントを外し、AlchemyWeb3エンドポイントを使用してスマートコントラクトをロードできるようになりました。

1// interact.js
2export const helloWorldContract = new web3.eth.Contract(
3 contractABI,
4 contractAddress
5)

要約すると、interact.jsの最初の12行は次のようになります。

1// interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8const contractABI = require("../contract-abi.json")
9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"
10
11export const helloWorldContract = new web3.eth.Contract(
12 contractABI,
13 contractAddress
14)
すべて表示

コントラクトがロードされたので、loadCurrentMessage関数を実装できます!

interact.jsファイルにloadCurrentMessageを実装する

これは非常にシンプルな関数です。 コントラクトから読み取るために、単純な非同期のweb3呼び出しを作成します。 この関数では、スマートコントラクトに保存されているメッセージを返します。

interact.jsファイルの loadCurrentMessageを次のように更新してください。

1// interact.js
2
3export const loadCurrentMessage = async () => {
4 const message = await helloWorldContract.methods.message().call()
5 return message
6}

このスマートコントラクトをUIに表示したいので、HelloWorld.jsコンポーネントの useEffect関数を次のように更新します。

1// HelloWorld.js
2
3//called only once
4useEffect(async () => {
5 const message = await loadCurrentMessage()
6 setMessage(message)
7}, [])

注: loadCurrentMessageは、コンポーネントの最初のレンダリング時に1回だけ呼び出されるようにします。 この後、addSmartContractListenerを実装して、スマートコントラクト内のメッセージが変更された後にUIを自動的に更新できるようにします。

リスナーについて詳しく説明する前に、これまでの内容を確認してみましょう! HelloWorld.jsファイルとinteract.jsファイルを保存し、http://localhost:3000/ (opens in a new tab)にアクセスしてください。

現在、「ネットワークに接続されていません」というメッセージが表示されなくなっていることがわかります。 代わりに、スマートコントラクトに保存されているメッセージが反映されます。 カッコイイ!

UIにスマートコントラクトに保存されたメッセージが反映されるはずです

それでは、リスナーについて説明していきます。

addSmartContractListenerを実装する

このチュートリアルシリーズのパート1で作成したHelloWorld.solファイル (opens in a new tab)を思い出すと、スマートコントラクトのupdate関数が呼び出された後にUpdatedMessagesというスマートコントラクトイベントが発行されることを思い出すでしょう(9行目と27行目を参照)。

1// HelloWorld.sol
2
3// Solidityのバージョンをセマンティックバージョニングで指定します。
4// 詳細: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
5pragma solidity ^0.7.3;
6
7// `HelloWorld` という名前のコントラクトを定義します。
8// コントラクトは関数とデータ (その状態) の集合です。一度デプロイされると、コントラクトはEthereumブロックチェーン上の特定のアドレスに存在します。詳細: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 // update関数が呼び出されたときに発行されます
12 //スマートコントラクトのイベントは、ブロックチェーン上で何かが起こったことをコントラクトがアプリのフロントエンドに伝える方法です。フロントエンドは特定のイベントを「リッスン」し、それが起こったときに行動を起こすことができます。
13 event UpdatedMessages(string oldStr, string newStr);
14
15 // `string` 型の状態変数 `message` を宣言します。
16 // 状態変数は、その値がコントラクトのストレージに永続的に保存される変数です。`public` キーワードにより、変数はコントラクトの外部からアクセス可能になり、他のコントラクトやクライアントが値をアクセスするために呼び出せる関数が作成されます。
17 string public message;
18
19 // 多くのクラスベースのオブジェクト指向言語と同様に、コンストラクタはコントラクト作成時に一度だけ実行される特別な関数です。
20 // コンストラクタはコントラクトのデータを初期化するために使用されます。詳細:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
21 constructor(string memory initMessage) {
22
23 // 文字列引数 `initMessage` を受け取り、その値をコントラクトの `message` ストレージ変数に設定します)。
24 message = initMessage;
25 }
26
27 // 文字列引数を受け取り、 `message` ストレージ変数を更新する公開関数です。
28 function update(string memory newMessage) public {
29 string memory oldMsg = message;
30 message = newMessage;
31 emit UpdatedMessages(oldMsg, newMessage);
32 }
33}
すべて表示

スマートコントラクトイベントは、ブロックチェーンで何かが起こったこと (すなわち、_イベント_の発生) をフロントエンドアプリケーションに伝える方法です。フロントエンドは特定のイベントを「リスニング」して、それが起きた時にアクションを実行します。

addSmartContractListener関数は、具体的にはHello WorldスマートコントラクトのUpdatedMessagesイベントをリッスンし、新しいメッセージを表示するようにUIを更新します。

addSmartContractListenerを次のように変更します。

1// HelloWorld.js
2
3function addSmartContractListener() {
4 helloWorldContract.events.UpdatedMessages({}, (error, data) => {
5 if (error) {
6 setStatus("😥 " + error.message)
7 } else {
8 setMessage(data.returnValues[1])
9 setNewMessage("")
10 setStatus("🎉 メッセージが更新されました!")
11 }
12 })
13}
すべて表示

リスナーがイベントを検出したときに何が起こるかを詳しく解説します。

  • イベントの発行時にエラーが発生した場合、そのエラーはstatusステート変数を介してUIに反映されます。
  • それ以外の場合は、返されたdataオブジェクトを使います。 data.returnValuesは、0からインデックス付けされた配列で、配列の最初の要素には前のメッセージが格納され、2番目の要素には更新されたメッセージが格納されます。 つまり、イベントが成功すると、message文字列を更新されたメッセージに設定し、newMessage文字列をクリアし、statusステート変数を更新して、新しいメッセージがスマートコントラクトに公開されたことを反映させます。

最後に、useEffect関数でリスナーを呼び出して、HelloWorld.jsコンポーネントの最初のレンダリング時にリスナーが初期化されるようにしましょう。 全体として、useEffect関数は次のようになります。

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7}, [])

スマートコントラクトから読み取れるようになったので、スマートコントラクトに書き込む方法も理解できると素晴らしいですね! ただし、dappに書き込むには、まずEthereumウォレットをdappに接続する必要があります。

それでは、次にEthereumウォレット (MetaMask) を設定し、それをdappに接続することに取り組みましょう!

ステップ4: Ethereumウォレットを設定する

Ethereumチェーンに何かを書き込むには、ユーザーは仮想ウォレットの秘密鍵を使ってトランザクションに署名しなければなりません。 このチュートリアルでは、Ethereumアカウントアドレスの管理に使用されるブラウザの仮想ウォレットであるMetaMask (opens in a new tab)を使用します。これにより、エンドユーザーは、このトランザクションの署名が非常に簡単になります。

イーサリアム上のトランザクションの仕組みについてさらに詳しく知りたい場合は、イーサリアム・ファウンデーションのこちらのページをご覧ください。

MetaMaskをダウンロードする

MetaMaskアカウントは、こちら (opens in a new tab)から無料でダウンロードして作成できます。 アカウントを作成するとき、またはすでにアカウントをお持ちの場合は、右上の「Goerli Test Network」に必ず切り替えてください (実在の通貨を扱わないようにするため)。

フォーセットからetherを追加する

Ethereumブロックチェーンでトランザクションに署名するには、偽のEthが必要です。 Ethを取得するには、FaucETH (opens in a new tab)にアクセスしてGoerliアカウントアドレスを入力し、「Request funds」をクリックし、ドロップダウンで「Ethereum Testnet Goerli」を選択し、最後に「Request funds」ボタンを再度クリックします。 MetamaskアカウントにETHが表示されるはずです。

残高を確認する

残高を確認するために、Alchemyのcomposerツール (opens in a new tab)を使用してeth_getBalance (opens in a new tab)リクエストを行いましょう。 このリクエストをすると、ウォレット内のETHの額が返されます。 MetaMaskアカウントアドレスを入力して「Send Request」をクリックすると、次のようなレスポンスが表示されます。

1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}

注: この結果はethではなくwei単位です。 weiはETHの最小単位として使われています。 weiからETHへ変換すると、1 eth = 10¹⁸ weiになります。 つまり、0xde0b6b3a7640000を10進数に変換すると、1*10¹⁸となり、1 ETHに相当します。

ふう! これで、偽のお金を手に入れました。 🤑

ステップ5: MetaMaskをUIに接続する

MetaMaskウォレットが設定されたので、分散型アプリケーション(Dapp)を接続しましょう。

connectWallet関数

interact.jsファイルでconnectWallet関数を実装します。この関数は、HelloWorld.jsコンポーネントで呼び出します。

connectWalletを次のように変更しましょう。

1// interact.js
2
3export const connectWallet = async () => {
4 if (window.ethereum) {
5 try {
6 const addressArray = await window.ethereum.request({
7 method: "eth_requestAccounts",
8 })
9 const obj = {
10 status: "👆🏽 上のテキストフィールドにメッセージを書き込んでください。",
11 address: addressArray[0],
12 }
13 return obj
14 } catch (err) {
15 return {
16 address: "",
17 status: "😥 " + err.message,
18 }
19 }
20 } else {
21 return {
22 address: "",
23 status: (
24 <span>
25 <p>
26 {" "}
27 🦊 <a target="_blank" href={`https://metamask.io/download`}>
28 ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。
29 </a>
30 </p>
31 </span>
32 ),
33 }
34 }
35}
すべて表示

この巨大なコードブロックは、正確には何をするのでしょうか?

まず、ブラウザでwindow.ethereumが有効になっているかどうかをチェックしています。

window.ethereumは、MetaMaskやその他のウォレットプロバイダーによって挿入されるグローバルAPIで、WebサイトがユーザーのEthereumアカウントを要求できるようにするものです。 承認されると、ユーザーが接続しているブロックチェーンからデータを読み取ったり、メッセージやトランザクションへの署名をユーザーに提案したりできるようになります。 詳細についてはMetaMaskのドキュメント (opens in a new tab)をご覧ください。

window.ethereumが_存在しない_場合、それはMetaMaskがインストールされていないことを意味します。 これにより、返されるaddressが空の文字列で、status JSXオブジェクトがユーザーにMetaMaskをインストールするよう促すJSONオブジェクトが返されます。

さて、window.ethereumが_存在する_場合、ここからが面白くなります。

try/catchループを使用して、window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab)を呼び出してMetaMaskへの接続を試みます。 この関数を呼び出すと、ブラウザでMetaMaskが開き、ユーザーはウォレットを分散型アプリケーション(Dapp)に接続するように求められます。

  • ユーザーが接続を選んだ場合、method: "eth_requestAccounts"は、dappに接続されているすべてのユーザーのアカウントアドレスを含む配列を返します。 まとめると、connectWallet関数は、この配列の_最初の_address(9行目参照)と、ユーザーにスマートコントラクトへのメッセージを書き込むよう促すstatusメッセージを含むJSONオブジェクトを返します。
  • ユーザーが接続を拒否した場合、JSONオブジェクトには返されるaddressの空文字列と、ユーザーが接続を拒否したことを示すstatusメッセージが含まれます。

これで、connectWallet関数を作成できたので、次のステップでは、この関数をHelloWorld.jsコンポーネントに呼び出します。

connectWallet関数をHelloWorld.jsUIコンポーネントに追加する

HelloWorld.jsにある connectWalletPressed関数に移動し、次のように更新します。

1// HelloWorld.js
2
3const connectWalletPressed = async () => {
4 const walletResponse = await connectWallet()
5 setStatus(walletResponse.status)
6 setWallet(walletResponse.address)
7}

interact.jsファイルによって、機能の大部分がHelloWorld.jsコンポーネントからどのように抽象化されているかに注目してください。 これは、モデルビューコントローラ(M-V-C)パラダイムに準拠しているためです。

connectWalletPressedでは、インポートしたconnectWallet関数をawaitで呼び出し、そのレスポンスを使って状態フックを介してstatuswalletAddress変数を更新します。

それでは、両方のファイル (HelloWorld.jsinteract.js) を保存して、これまでのUIをテストしてみましょう。

http://localhost:3000/ (opens in a new tab)ページでブラウザを開き、ページ右上にある「Connect Wallet」ボタンを押します。

MetaMaskがインストールされている場合は、ウォレットを分散型アプリケーション(Dapp)に接続するように求められます。 接続リクエストを承認します。

ウォレットボタンに、接続した自分のアドレスが表示されているはずです! やった!🔥

次に、ページを再読み込みしてみてください... これは奇妙です。 ウォレットボタンによって、すでに接続しているにもかかわらずMetaMaskに接続するよう求められます。

しかし、恐れることはありません! これを簡単に修正できます(分かりましたか?) getCurrentWalletConnectedを実装することで、アドレスがすでにdappに接続されているかどうかを確認し、それに応じてUIを更新できます!

getCurrentWalletConnected関数

interact.jsファイルのgetCurrentWalletConnected関数を次のように更新します。

1// interact.js
2
3export const getCurrentWalletConnected = async () => {
4 if (window.ethereum) {
5 try {
6 const addressArray = await window.ethereum.request({
7 method: "eth_accounts",
8 })
9 if (addressArray.length > 0) {
10 return {
11 address: addressArray[0],
12 status: "👆🏽 上のテキストフィールドにメッセージを書き込んでください。",
13 }
14 } else {
15 return {
16 address: "",
17 status: "🦊 右上のボタンを使ってMetaMaskに接続してください。",
18 }
19 }
20 } catch (err) {
21 return {
22 address: "",
23 status: "😥 " + err.message,
24 }
25 }
26 } else {
27 return {
28 address: "",
29 status: (
30 <span>
31 <p>
32 {" "}
33 🦊 <a target="_blank" href={`https://metamask.io/download`}>
34 ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。
35 </a>
36 </p>
37 </span>
38 ),
39 }
40 }
41}
すべて表示

このコードは、前のステップで作成したconnectWallet関数に非常に似ています。

主な違いは、ユーザーがウォレットを接続するためにMetaMaskを開くeth_requestAccountsメソッドを呼び出す代わりに、ここではeth_accountsメソッドを呼び出している点です。これは、現在dappに接続されているMetaMaskのアドレスを含む配列を単に返すだけです。

この関数を動作させるため、HelloWorld.jsコンポーネントのuseEffect関数で呼び出しましょう。

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11}, [])
すべて表示

walletAddressstatusの状態変数を更新するのに、getCurrentWalletConnectedの呼び出しのレスポンスを使用していることに注目してください。

このコードを加えたら、ブラウザウィンドウを更新してみてください。

素晴らしい! リフレッシュ後も、ボタンには接続されていることが示されており、接続されたウォレットのアドレスのプレビューが表示されているはずです。

addWalletListenerを実装する

分散型アプリケーション(Dapp)ウォレットの設定の最終ステップは、ウォレットリスナーを実装することです。これにより、ユーザーが接続を切断したり、アカウントを切り替えたりした場合など、ウォレットの状態が変更されたときにUIが更新されます。

HelloWorld.jsファイルで、addWalletListener関数を次のように変更します。

1// HelloWorld.js
2
3function addWalletListener() {
4 if (window.ethereum) {
5 window.ethereum.on("accountsChanged", (accounts) => {
6 if (accounts.length > 0) {
7 setWallet(accounts[0])
8 setStatus("👆🏽 上のテキストフィールドにメッセージを書き込んでください。")
9 } else {
10 setWallet("")
11 setStatus("🦊 右上のボタンを使ってMetaMaskに接続してください。")
12 }
13 })
14 } else {
15 setStatus(
16 <p>
17 {" "}
18 🦊 <a target="_blank" href={`https://metamask.io/download`}>
19 ブラウザに仮想EthereumウォレットであるMetaMaskをインストールする必要があります。
20 </a>
21 </p>
22 )
23 }
24}
すべて表示

この時点で何が起こっているかを理解するのに私たちの助けは必要ないと思いますが、念のため、簡単に説明します。

  • まず、この関数はwindow.ethereumが有効になっているか(つまり、MetaMaskがインストールされているか)をチェックします。
    • 有効でない場合、status状態変数を、ユーザーにMetaMaskのインストールを促すJSX文字列に設定するだけです。
    • 有効になっている場合、3行目のリスナーwindow.ethereum.on("accountsChanged")を設定します。これはMetaMaskウォレットの状態変更をリッスンします。これには、ユーザーがdappに追加のアカウントを接続した場合、アカウントを切り替えた場合、アカウントを切断した場合が含まれます。 少なくとも1つのアカウントが接続されていれば、walletAddress状態変数は、リスナーから返されたaccounts配列の最初のアカウントとして更新されます。 それ以外の場合、walletAddressには空の文字列が設定されます。

最後に、useEffect関数で次のように呼び出す必要があります。

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11
12 addWalletListener()
13}, [])
すべて表示

完成です! ウォレットのすべての機能をプログラミングしました。 次は最後のタスクです。スマートコントラクトに保存されているメッセージを更新します。

ステップ6: updateMessage関数を実装する

さあ、最終段階にたどり着きました。 interact.jsファイルのupdateMessageで、次のことを実行します。

  1. スマートコントラクトに公開したいメッセージが有効であることを確認する。
  2. MetaMaskを使用してトランザクションに署名する
  3. HelloWorld.jsフロントエンドコンポーネントでこの関数を呼び出す。

これには、それほど時間はかかりません。dappを完成させましょう!

入力エラー処理

当然ながら、関数の開始時に何らかの入力エラー処理を行うことは理にかなっています。

MetaMaskエクステンションがインストールされていない場合、接続されているウォレットがない場合 (つまり、渡された addressが空の文字列の場合) 、またはmessageが空の文字列の場合は、関数が早期にリターンするようにします。 次のエラー処理をupdateMessageに追加しましょう。

1// interact.js
2
3export const updateMessage = async (address, message) => {
4 if (!window.ethereum || address === null) {
5 return {
6 status:
7 "💡 ブロックチェーン上のメッセージを更新するには、MetaMaskウォレットを接続してください。",
8 }
9 }
10
11 if (message.trim() === "") {
12 return {
13 status: "❌ メッセージを空の文字列にすることはできません。",
14 }
15 }
16}
すべて表示

入力エラーを適切に処理できるようなりました。それでは、MetaMaskを介してトランザクションに署名をします。

トランザクションに署名する

従来のweb3 Ethereumトランザクションにすでに慣れている場合は、次に記述するコードは非常に馴染みのあるものになるでしょう。 入力エラー処理コードの下に、次のupdateMessageを加えます。

1// interact.js
2
3//トランザクションパラメータを設定する
4const transactionParameters = {
5 to: contractAddress, // コントラクト公開時以外は必須。
6 from: address, // ユーザーのアクティブなアドレスと一致する必要がある。
7 data: helloWorldContract.methods.update(message).encodeABI(),
8}
9
10//トランザクションに署名する
11try {
12 const txHash = await window.ethereum.request({
13 method: "eth_sendTransaction",
14 params: [transactionParameters],
15 })
16 return {
17 status: (
18 <span>
19{" "}
20 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
21 Etherscanでトランザクションのステータスを表示してください!
22 </a>
23 <br />
24 ℹ️ トランザクションがネットワークによって検証されると、メッセージは自動的に更新されます。
25 </span>
26 ),
27 }
28} catch (error) {
29 return {
30 status: "😥 " + error.message,
31 }
32}
すべて表示

何をしているか、説明していきましょう。 まず、次のようにトランザクションパラメータを設定します。

  • toは受信者アドレス(スマートコントラクト)を指定します
  • fromはトランザクションの署名者を指定します。これは関数に渡したaddress変数です。
  • dataには、Hello Worldスマートコントラクトのupdateメソッドへの呼び出しが含まれており、message文字列変数を入力として受け取っています。

次に、window.ethereum.requestをawaitで呼び出して、MetaMaskにトランザクションの署名を依頼します。 11行目と12行目で、ethメソッドeth_sendTransactionを指定し、transactionParametersを渡していることに注目してください。

この時点で、ブラウザでMetaMaskが開かれ、ユーザーにトランザクションの署名または拒否を求めます。

  • トランザクションが成功した場合、この関数は、status JSX文字列がEtherscanでトランザクションについての詳細を確認するようユーザーに促すJSONオブジェクトを返します。
  • トランザクションが失敗した場合、この関数は、エラーメッセージを伝えるstatus文字列が入ったJSONオブジェクトを返します。

全体として、updateMessage関数は次のようになります。

1// interact.js
2
3export const updateMessage = async (address, message) => {
4 //入力エラー処理
5 if (!window.ethereum || address === null) {
6 return {
7 status:
8 "💡 ブロックチェーン上のメッセージを更新するには、MetaMaskウォレットを接続してください。",
9 }
10 }
11
12 if (message.trim() === "") {
13 return {
14 status: "❌ メッセージを空の文字列にすることはできません。",
15 }
16 }
17
18 //トランザクションパラメータを設定する
19 const transactionParameters = {
20 to: contractAddress, // コントラクト公開時以外は必須。
21 from: address, // ユーザーのアクティブなアドレスと一致する必要がある。
22 data: helloWorldContract.methods.update(message).encodeABI(),
23 }
24
25 //トランザクションに署名する
26 try {
27 const txHash = await window.ethereum.request({
28 method: "eth_sendTransaction",
29 params: [transactionParameters],
30 })
31 return {
32 status: (
33 <span>
34{" "}
35 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
36 Etherscanでトランザクションのステータスを表示してください!
37 </a>
38 <br />
39 ℹ️ トランザクションがネットワークによって検証されると、メッセージは自動的に更新されます。
40 </span>
41 ),
42 }
43 } catch (error) {
44 return {
45 status: "😥 " + error.message,
46 }
47 }
48}
すべて表示

最後に、updateMessage関数を HelloWorld.jsコンポーネントに接続する必要があります。

updateMessageHelloWorld.jsフロントエンドに接続する

onUpdatePressed関数では、インポートされたupdateMessage関数へのawait呼び出しを行い、トランザクションが成功したか失敗したかを反映するようにstatusステート変数を次のように変更する必要があります。

1// HelloWorld.js
2
3const onUpdatePressed = async () => {
4 const { status } = await updateMessage(walletAddress, newMessage)
5 setStatus(status)
6}

とてもクリーンでシンプルです。 そして、なんと... dappの完成です!!!

Updateボタンを試してみてください!

独自のカスタムdappを作成する

おめでとうございます!チュートリアルの最後までたどり着きました! おさらいすると、以下の方法を学びました。

  • MetaMaskウォレットをdappプロジェクトに接続する
  • Alchemy Web3 (opens in a new tab) APIを使用してスマートコントラクトからデータを読み取る
  • MetaMaskを使用してEthereumトランザクションに署名する

これで、このチュートリアルのスキルを応用して独自のカスタムdappプロジェクトを構築するための準備が整いました。 いつものように、ご質問があれば、Alchemy Discord (opens in a new tab)でお気軽にお問い合わせください。 🧙‍♂️

このチュートリアルを完了したら、Twitterで@alchemyplatform (opens in a new tab)にタグ付けして、感想やフィードバックをお知らせください!

最終更新: 2026年2月26日

このチュートリアルは役に立ちましたか?