初心者向けのHello Worldスマートコントラクト - フルスタック
このガイドは、ブロックチェーン開発の初心者で、どこから始めればよいか、スマートコントラクトのデプロイやインタラクトの方法がわからない方を対象としています。 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」という名前を付け、簡単な説明を記述します。 環境として Staging を、ネットワークとして Goerli を選択します。
注: 必ず 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-world2cd 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 contract4entry point: (index.js)5test command:6git repository:7keywords:8author:9license: (ISC)1011About to write to /Users/.../.../.../hello-world/package.json:1213{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 8882888 888 888 888 8883888 888 888 888 88848888888888 8888b. 888d888 .d88888 88888b. 8888b. 8888885888 888 "88b 888P" d88" 888 888 "88b "88b 8886888 888 .d888888 888 888 888 888 888 .d888888 8887888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888910👷 Welcome to Hardhat v2.0.11 👷1112What do you want to do? …13Create a sample project14❯ Create an empty hardhat.config.js15Quitすべて表示これにより、プロジェクトに hardhat.config.js ファイルが生成されます。 チュートリアルの後半で、これを使用してプロジェクトのセットアップを指定します。
ステップ9: プロジェクトフォルダを追加する
プロジェクトを整理するために、2つの新しいフォルダを作成しましょう。 コマンドラインで、hello-world プロジェクトのルートディレクトリに移動し、次のように入力します:
1mkdir contracts2mkdir scriptscontracts/には、hello worldスマートコントラクトのコードファイルを保存しますscripts/には、コントラクトをデプロイして対話するためのスクリプトを保存します
ステップ10: コントラクトを作成する
「いつになったらコードを書くのだろう?」と思っているかもしれませんね。 その時が来ました!
お好きなエディタでhello-worldプロジェクトを開いてください。 スマートコントラクトは、最も一般的にはSolidityで記述されており、今回もSolidityを使ってスマートコントラクトを作成します。
contractsフォルダに移動し、HelloWorld.solという名前の新しいファイルを作成します。- 以下は、このチュートリアルで使用するHello Worldスマートコントラクトのサンプルです。 以下の内容を
HelloWorld.solファイルにコピーしてください。
注: このコントラクトが何をするのかを理解するために、必ずコメントをお読みください。
1// Solidityのバージョンをセマンティックバージョニングで指定します。2// 詳細: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma3pragma solidity >=0.7.3;45// `HelloWorld` という名前のコントラクトを定義します。6// コントラクトは関数とデータ (その状態) の集合です。一度デプロイされると、コントラクトはEthereumブロックチェーン上の特定のアドレスに存在します。詳細: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html7contract HelloWorld {89 // update関数が呼び出されたときに発行されます10 //スマートコントラクトのイベントは、ブロックチェーン上で何かが起こったことをコントラクトがアプリのフロントエンドに伝える方法です。フロントエンドは特定のイベントを「リッスン」し、それが起こったときに行動を起こすことができます。11 event UpdatedMessages(string oldStr, string newStr);1213 // `string` 型の状態変数 `message` を宣言します。14 // 状態変数は、その値がコントラクトのストレージに永続的に保存される変数です。`public` キーワードにより、変数はコントラクトの外部からアクセス可能になり、他のコントラクトやクライアントが値をアクセスするために呼び出せる関数が作成されます。15 string public message;1617 // 多くのクラスベースのオブジェクト指向言語と同様に、コンストラクタはコントラクト作成時に一度だけ実行される特別な関数です。18 // コンストラクタはコントラクトのデータを初期化するために使用されます。詳細:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors19 constructor(string memory initMessage) {2021 // 文字列引数 `initMessage` を受け取り、その値をコントラクトの `message` ストレージ変数に設定します)。22 message = initMessage;23 }2425 // 文字列引数を受け取り、 `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 など、他の名前にしないでください。
- 秘密鍵をエクスポートするためのこれらの手順 (opens in a new tab)に従ってください
- HTTP Alchemy API URLを取得するには、以下を参照してください
.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').HardhatUserConfig3 */45require("dotenv").config()6require("@nomiclabs/hardhat-ethers")78const { API_URL, PRIVATE_KEY } = process.env910module.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 compileSPDX 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")34 // デプロイを開始し、コントラクトオブジェクトに解決されるpromiseを返す5 const hello_world = await HelloWorld.deploy("Hello World!")6 console.log("Contract deployed to address:", hello_world.address)7}89main()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プラグインを使用する場合、ContractFactoryとContractのインスタンスは、デフォルトで最初の署名者 (所有者) に接続されます。
1const hello_world = await HelloWorld.deploy()ContractFactoryでdeploy()を呼び出すとデプロイが開始され、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.js23const API_KEY = process.env.API_KEY4const PRIVATE_KEY = process.env.PRIVATE_KEY5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS.envファイルを更新する
新しい環境変数を使用するため、以前に作成した.envファイルに定義する必要があります。
AlchemyのAPI_KEYとスマートコントラクトがデプロイされているCONTRACT_ADDRESSの定義を加える必要があります。
.envファイルは、以下のようになっていなければなりません。
# .envAPI_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.js2const 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つのコンセプトを扱う必要があります。
- Provider - ブロックチェーンへの読み取りおよび書き込みアクセスを提供するノードプロバイダです。
- Signer - トランザクションに署名するEthereumアカウントを表します。
- Contract - オンチェーンにデプロイされた特定のコントラクトを表すEthers.jsのオブジェクトです。
前の手順で取得したコントラクトABIを使って、コントラクトのインスタンスを作成します。
1// interact.js23// Provider4const alchemyProvider = new ethers.providers.AlchemyProvider(5 (network = "goerli"),6 API_KEY7)89// Signer10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)1112// Contract13const helloWorldContract = new ethers.Contract(14 CONTRACT_ADDRESS,15 contract.abi,16 signer17)すべて表示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.js23// ...45async 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.js23// ...45async function main() {6 const message = await helloWorldContract.message()7 console.log("The message is: " + message)89 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.js23const API_KEY = process.env.API_KEY4const PRIVATE_KEY = process.env.PRIVATE_KEY5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS67const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")89// provider - Alchemy10const alchemyProvider = new ethers.providers.AlchemyProvider(11 (network = "goerli"),12 API_KEY13)1415// signer - you16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)1718// contract instance19const helloWorldContract = new ethers.Contract(20 CONTRACT_ADDRESS,21 contract.abi,22 signer23)2425async function main() {26 const message = await helloWorldContract.message()27 console.log("The message is: " + message)2829 console.log("Updating the message...")30 const tx = await helloWorldContract.update("this is the new message")31 await tx.wait()3233 const newMessage = await helloWorldContract.message()34 console.log("The new message is: " + newMessage)35}3637main()すべて表示このスクリプトを実行するだけで、古いメッセージ、更新ステータス、および新しいメッセージがターミナルに出力されるのを確認できるはずです。
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.js23require("dotenv").config()4require("@nomiclabs/hardhat-ethers")5require("@nomiclabs/hardhat-etherscan")67const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env89module.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 contract2contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address3for verification on Etherscan. Waiting for verification result...456Successfully 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-filesとcompletedの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-filesnpm 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.js23import React from "react"4import { useEffect, useState } from "react"5import {6 helloWorldContract,7 connectWallet,8 updateMessage,9 loadCurrentMessage,10 getCurrentWalletConnected,11} from "./util/interact.js"1213import alchemylogo from "./alchemylogo.svg"すべて表示次に、特定のイベントの後に更新するステート変数があります。
1// HelloWorld.js23//State variables4const [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- スマートコントラクトに書き込まれる新しいメッセージを格納する文字列
ステート変数の後に、useEffect、addSmartContractListener、addWalletListener、connectWalletPressed、onUpdatePressedの5つの未実装の関数があります。 次に、それらが何をするのかを説明します。
1// HelloWorld.js23//called only once4useEffect(async () => {5 //TODO: implement6}, [])78function addSmartContractListener() {9 //TODO: implement10}1112function addWalletListener() {13 //TODO: implement14}1516const connectWalletPressed = async () => {17 //TODO: implement18}1920const onUpdatePressed = async () => {21 //TODO: implement22}すべて表示useEffect(opens in a new tab) - これは、コンポーネントがレンダリングされた後に呼び出されるReactフックです。 空の配列[]のプロップが渡されているため (4行目を参照)、コンポーネントの_最初の_レンダリングでのみ呼び出されます。 ここでは、スマートコントラクトに保存されている現在のメッセージをロードし、スマートコントラクトとウォレットリスナーを呼び出し、ウォレットが既に接続されているかどうかを反映してUIを更新します。addSmartContractListener- この関数では、HelloWorldコントラクトのUpdatedMessagesイベントを監視し、スマートコントラクトでメッセージが変更されたときにUIを更新するリスナーを設定します。addWalletListener- この関数では、ユーザーがウォレットを切断したときやアドレスを切り替えたときなど、ユーザーのMetaMaskウォレットのステートの変化を検出するリスナーを設定します。connectWalletPressed- この関数は、ユーザーのMetaMaskウォレットをdappに接続するのに呼び出されます。onUpdatePressed- この関数は、ユーザーがスマートコントラクトに保存されているメッセージを更新したいときに呼び出されます。
このファイルの終盤には、コンポーネントのUIがあります。
1// HelloWorld.js23//the UI of our component4return (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>1718 <h2 style={{ paddingTop: "50px" }}>Current Message:</h2>19 <p>{message}</p>2021 <h2 style={{ paddingTop: "18px" }}>New Message:</h2>2223 <div>24 <input25 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>3132 <button id="publishButton" onClick={onUpdatePressed}>33 Update34 </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がpublishButtonとwalletButtonであるボタンがそれぞれクリックされると、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.js23//export const helloWorldContract;45export const loadCurrentMessage = async () => {}67export const connectWallet = async () => {}89const getCurrentWalletConnected = async () => {}1011export 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-web3Alchemy 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 --savedappでは、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.js23require("dotenv").config()4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")6const web3 = createAlchemyWeb3(alchemyKey)78//export const helloWorldContract;上記のコードでは、まず.envファイルから Alchemyキーをインポートし、次にalchemyKeyをcreateAlchemyWeb3に渡して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.js23const contractABI = require("../contract-abi.json")4const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"ついに、helloWorldContract変数のコメントを外し、AlchemyWeb3エンドポイントを使用してスマートコントラクトをロードできるようになりました。
1// interact.js2export const helloWorldContract = new web3.eth.Contract(3 contractABI,4 contractAddress5)要約すると、interact.jsの最初の12行は次のようになります。
1// interact.js23require("dotenv").config()4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")6const web3 = createAlchemyWeb3(alchemyKey)78const contractABI = require("../contract-abi.json")9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"1011export const helloWorldContract = new web3.eth.Contract(12 contractABI,13 contractAddress14)すべて表示コントラクトがロードされたので、loadCurrentMessage関数を実装できます!
interact.jsファイルにloadCurrentMessageを実装する
これは非常にシンプルな関数です。 コントラクトから読み取るために、単純な非同期のweb3呼び出しを作成します。 この関数では、スマートコントラクトに保存されているメッセージを返します。
interact.jsファイルの loadCurrentMessageを次のように更新してください。
1// interact.js23export const loadCurrentMessage = async () => {4 const message = await helloWorldContract.methods.message().call()5 return message6}このスマートコントラクトをUIに表示したいので、HelloWorld.jsコンポーネントの useEffect関数を次のように更新します。
1// HelloWorld.js23//called only once4useEffect(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.sol23// Solidityのバージョンをセマンティックバージョニングで指定します。4// 詳細: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma5pragma solidity ^0.7.3;67// `HelloWorld` という名前のコントラクトを定義します。8// コントラクトは関数とデータ (その状態) の集合です。一度デプロイされると、コントラクトはEthereumブロックチェーン上の特定のアドレスに存在します。詳細: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html9contract HelloWorld {1011 // update関数が呼び出されたときに発行されます12 //スマートコントラクトのイベントは、ブロックチェーン上で何かが起こったことをコントラクトがアプリのフロントエンドに伝える方法です。フロントエンドは特定のイベントを「リッスン」し、それが起こったときに行動を起こすことができます。13 event UpdatedMessages(string oldStr, string newStr);1415 // `string` 型の状態変数 `message` を宣言します。16 // 状態変数は、その値がコントラクトのストレージに永続的に保存される変数です。`public` キーワードにより、変数はコントラクトの外部からアクセス可能になり、他のコントラクトやクライアントが値をアクセスするために呼び出せる関数が作成されます。17 string public message;1819 // 多くのクラスベースのオブジェクト指向言語と同様に、コンストラクタはコントラクト作成時に一度だけ実行される特別な関数です。20 // コンストラクタはコントラクトのデータを初期化するために使用されます。詳細:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors21 constructor(string memory initMessage) {2223 // 文字列引数 `initMessage` を受け取り、その値をコントラクトの `message` ストレージ変数に設定します)。24 message = initMessage;25 }2627 // 文字列引数を受け取り、 `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.js23function 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.js23useEffect(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.js23export 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 obj14 } 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.js23const 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で呼び出し、そのレスポンスを使って状態フックを介してstatusとwalletAddress変数を更新します。
それでは、両方のファイル (HelloWorld.jsとinteract.js) を保存して、これまでのUIをテストしてみましょう。
http://localhost:3000/ (opens in a new tab)ページでブラウザを開き、ページ右上にある「Connect Wallet」ボタンを押します。
MetaMaskがインストールされている場合は、ウォレットを分散型アプリケーション(Dapp)に接続するように求められます。 接続リクエストを承認します。
ウォレットボタンに、接続した自分のアドレスが表示されているはずです! やった!🔥
次に、ページを再読み込みしてみてください... これは奇妙です。 ウォレットボタンによって、すでに接続しているにもかかわらずMetaMaskに接続するよう求められます。
しかし、恐れることはありません! これを簡単に修正できます(分かりましたか?) getCurrentWalletConnectedを実装することで、アドレスがすでにdappに接続されているかどうかを確認し、それに応じてUIを更新できます!
getCurrentWalletConnected関数
interact.jsファイルのgetCurrentWalletConnected関数を次のように更新します。
1// interact.js23export 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.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()78 const { address, status } = await getCurrentWalletConnected()9 setWallet(address)10 setStatus(status)11}, [])すべて表示walletAddressとstatusの状態変数を更新するのに、getCurrentWalletConnectedの呼び出しのレスポンスを使用していることに注目してください。
このコードを加えたら、ブラウザウィンドウを更新してみてください。
素晴らしい! リフレッシュ後も、ボタンには接続されていることが示されており、接続されたウォレットのアドレスのプレビューが表示されているはずです。
addWalletListenerを実装する
分散型アプリケーション(Dapp)ウォレットの設定の最終ステップは、ウォレットリスナーを実装することです。これにより、ユーザーが接続を切断したり、アカウントを切り替えたりした場合など、ウォレットの状態が変更されたときにUIが更新されます。
HelloWorld.jsファイルで、addWalletListener関数を次のように変更します。
1// HelloWorld.js23function 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.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()78 const { address, status } = await getCurrentWalletConnected()9 setWallet(address)10 setStatus(status)1112 addWalletListener()13}, [])すべて表示完成です! ウォレットのすべての機能をプログラミングしました。 次は最後のタスクです。スマートコントラクトに保存されているメッセージを更新します。
ステップ6: updateMessage関数を実装する
さあ、最終段階にたどり着きました。 interact.jsファイルのupdateMessageで、次のことを実行します。
- スマートコントラクトに公開したいメッセージが有効であることを確認する。
- MetaMaskを使用してトランザクションに署名する
HelloWorld.jsフロントエンドコンポーネントでこの関数を呼び出す。
これには、それほど時間はかかりません。dappを完成させましょう!
入力エラー処理
当然ながら、関数の開始時に何らかの入力エラー処理を行うことは理にかなっています。
MetaMaskエクステンションがインストールされていない場合、接続されているウォレットがない場合 (つまり、渡された addressが空の文字列の場合) 、またはmessageが空の文字列の場合は、関数が早期にリターンするようにします。 次のエラー処理をupdateMessageに追加しましょう。
1// interact.js23export const updateMessage = async (address, message) => {4 if (!window.ethereum || address === null) {5 return {6 status:7 "💡 ブロックチェーン上のメッセージを更新するには、MetaMaskウォレットを接続してください。",8 }9 }1011 if (message.trim() === "") {12 return {13 status: "❌ メッセージを空の文字列にすることはできません。",14 }15 }16}すべて表示入力エラーを適切に処理できるようなりました。それでは、MetaMaskを介してトランザクションに署名をします。
トランザクションに署名する
従来のweb3 Ethereumトランザクションにすでに慣れている場合は、次に記述するコードは非常に馴染みのあるものになるでしょう。 入力エラー処理コードの下に、次のupdateMessageを加えます。
1// interact.js23//トランザクションパラメータを設定する4const transactionParameters = {5 to: contractAddress, // コントラクト公開時以外は必須。6 from: address, // ユーザーのアクティブなアドレスと一致する必要がある。7 data: helloWorldContract.methods.update(message).encodeABI(),8}910//トランザクションに署名する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が開かれ、ユーザーにトランザクションの署名または拒否を求めます。
- トランザクションが成功した場合、この関数は、
statusJSX文字列がEtherscanでトランザクションについての詳細を確認するようユーザーに促すJSONオブジェクトを返します。 - トランザクションが失敗した場合、この関数は、エラーメッセージを伝える
status文字列が入ったJSONオブジェクトを返します。
全体として、updateMessage関数は次のようになります。
1// interact.js23export const updateMessage = async (address, message) => {4 //入力エラー処理5 if (!window.ethereum || address === null) {6 return {7 status:8 "💡 ブロックチェーン上のメッセージを更新するには、MetaMaskウォレットを接続してください。",9 }10 }1112 if (message.trim() === "") {13 return {14 status: "❌ メッセージを空の文字列にすることはできません。",15 }16 }1718 //トランザクションパラメータを設定する19 const transactionParameters = {20 to: contractAddress, // コントラクト公開時以外は必須。21 from: address, // ユーザーのアクティブなアドレスと一致する必要がある。22 data: helloWorldContract.methods.update(message).encodeABI(),23 }2425 //トランザクションに署名する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コンポーネントに接続する必要があります。
updateMessageをHelloWorld.jsフロントエンドに接続する
onUpdatePressed関数では、インポートされたupdateMessage関数へのawait呼び出しを行い、トランザクションが成功したか失敗したかを反映するようにstatusステート変数を次のように変更する必要があります。
1// HelloWorld.js23const 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日





