WEBアプリ上でNFTキャラクターを選ぶページを作ろう
🐱 NFT キャラクターをフロントエンドに表示する
前回のレッスンでは、Webアプリケーションからスマートコントラクトを呼び出すコードを実装し、 SelectCharacter
コンポーネントを作成しました。
これから、スマートコントラクトからNFTキャラクターを取得してフロントエンドに表示させていきましょう。
👀 deploy.js
を整理する
Webアプリケーションの開発を進める前に、contract/scripts
にある、deploy.js
ファイルを整理しましょう。
mintCharacterNFT
やattackBoss
関数を排除していきます。
もうすでに排除されている場合は、このプロセスをスキップして問題ありません。
deploy.js
が下記のようになっていることを確認したら、次に進みましょう。
const main = async () => {
const gameContractFactory = await hre.ethers.getContractFactory("MyEpicGame");
const gameContract = await gameContractFactory.deploy(
["ZORO", "NAMI", "USOPP"], // キャラクターの名前
[
"https://i.imgur.com/TZEhCTX.png", // キャラクターの画像
"https://i.imgur.com/WVAaMPA.png",
"https://i.imgur.com/pCMZeiM.png",
],
[100, 200, 300],
[100, 50, 25],
"CROCODILE", // Bossの名前
"https://i.imgur.com/BehawOh.png", // Bossの画像
10000, // Bossのhp
50 // Bossの攻撃力
);
// ここでは、nftGame コントラクトが、
// ローカルのブロックチェ ーンにデプロイされるまで待つ処理を行っています。
const nftGame = await gameContract.deployed();
console.log("Contract deployed to:", nftGame.address);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
deploy.js
を整理することにより、フロントエンドにおける状態エラーを防ぐことができます。
deploy.js
を更新した後、もう一度スマートコントラクトをデプロイすると、これからWebアプリケーション上でNFTキャラクターをMintするプロセスがスムーズになります。
復習も兼ねて、下記を実行していきましょう。
1 . 再度、コントラクトをデプロイする。
yarn contract deploy
を実行する必要があります。
2 . フロントエンド( constants.js
)のCONTRACT_ADDRESS
を更新する。
3 . の ABI ファイルを更新する。
contract/artifacts/contracts/MyEpicGame.sol/MyEpicGame.json
の中身を新しく作成するclient/src/utils/MyEpicGame.json
の中に貼り付ける必要があります。
♻️ index.js
を更新する
client/src/Components/SelectCharacter
にあるindex.js
は、プログラムの中 で何度も登場する変数や関数をまとめているファイルです。
これから、index.js
の中身を更新していきます。
まず、index.js
のimport
の部分を下記のように更新してください。
import React, { useEffect, useState } from "react";
import "./SelectCharacter.css";
import { ethers } from "ethers";
import { CONTRACT_ADDRESS, transformCharacterData } from "../../constants";
import myEpicGame from "../../utils/MyEpicGame.json";
次に、SelectCharacter
を下記のように更新し ましょう。
// SelectCharacter コンポーネントを定義しています。
const SelectCharacter = ({ setCharacterNFT }) => {
const [characters, setCharacters] = useState([]);
const [gameContract, setGameContract] = useState(null);
// ページがロードされた瞬間に下記を実行します。
useEffect(() => {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const gameContract = new ethers.Contract(
CONTRACT_ADDRESS,
myEpicGame.abi,
signer
);
// gameContract の状態を更新します。
setGameContract(gameContract);
} else {
console.log("Ethereum object not found");
}
}, []);
return (
<div className="select-character-container">
<h2>⏬ 一緒に戦う NFT キャラクターを選択 ⏬</h2>
</div>
);
};
export default SelectCharacter;
追加したコードを詳しく見ていきましょう。
const [characters, setCharacters] = useState([]);
const [gameContract, setGameContract] = useState(null);
ここでは、いくつかの状態変数を設定していきます。
-
characters
: コントラクトから返されるNFTキャラクターのメタデータを保持するプロパティ。 -
setCharacters
:characters
の状態を更新するプロパティ。 -
gameContract
: コントラクトの状態を初期化して保存するプロパティ。プログラムの中でコントラクトは複数回呼び出されるので、いったん初期化した状態で保存し、コントラクト全体で使用できるようにします。
-
setGameContract
:gameContract
の状態を更新するプロパティ。
次に、下記のコードを見ていきましょう。
// ページがロードされた瞬間に下記を実行します。
useEffect(() => {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const gameContract = new ethers.Contract(
CONTRACT_ADDRESS,
myEpicGame.abi,
signer
);
// gameContract の状態を更新します。
setGameContract(gameContract);
} else {
console.log("Ethereum object not found");
}
}, []);
ここではuseEffect
を使って、SelectCharacter
コンポーネントが呼び出されたら、すぐにgameContract
を作成して、使用できるようにしています。
この処理により、フロントエンドでNFTキャラクターを表示する準備が整います。
😎 NFT キャラクターのデータを取得する
NFTキャラクターのデータをスマートコントラクトから取得するために、getCharacters
関数を作成します。
gameContract
を使用する準備ができたら、すぐにgetCharacters
関数を呼び出したいので、ここでもuseEffect
を使用していきます。
それでは、SelectCharacter
の中に記載したuseEffect
関数を確認しましょう。
useEffect(() => {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const gameContract = new ethers.Contract(
CONTRACT_ADDRESS,
myEpicGame.abi,
signer
);
// gameContract の状態を更新します。
setGameContract(gameContract);
} else {
console.log("Ethereum object not found");
}
}, []);
この関数の直下に、下記を追加していきましょう。
useEffect(() => {
// NFT キャラクターのデータをスマートコントラクトから取得します。
const getCharacters = async () => {
try {
console.log("Getting contract characters to mint");
// ミント可能な全 NFT キャラクター をコントラクトをから呼び出します。
const charactersTxn = await gameContract.getAllDefaultCharacters();
console.log("charactersTxn:", charactersTxn);
// すべてのNFTキャラクターのデータを変換します。
const characters = charactersTxn.map((characterData) =>
transformCharacterData(characterData)
);
// ミント可能なすべてのNFTキャラクターの状態を設定します。
setCharacters(characters);
} catch (error) {
console.error("Something went wrong fetching characters:", error);
}
};
// gameContractの準備ができたら、NFT キャラクターを読み込みます。
if (gameContract) {
getCharacters();
}
}, [gameContract]);
コードの中身を見ていきましょう。
// ミント可能な全 NFT キャラクター をコントラクトをから呼び出します。
const charactersTxn = await gameContract.getAllDefaultCharacters();
ここでは、gameContract
を使用して、MyEpicGame.sol
に記載したgetAllDefaultCharacters
関数を呼び出しています。
✍️:
getAllDefaultCharacters
は、3 体の NFT キャラクターのデフォルト情報を取得する関数です。
次に、下記のコードを見ていきましょう。
// すべてのNFTキャラクターのデータを変換します。
const characters = charactersTxn.map((characterData) =>
transformCharacterData(characterData)
);
ここでは、transformCharacterData
を使用して、NFTキャラクターのデータをWebアプリケーションで扱えるオブジェクトに変換しています。
✍️:
map()
の使い方map()
は配列データに使うメソッドです。map()
メソッドを使って、配列に入っている NFT キャラクターそれぞれの属性情報( HP など)に対してtransformCharacterData
を実行し、その結果を新しい配列(characters
)として返しています。
次に下記のコードを見ていきましょう。
// ミント可能なすべてのNFTキャラクターの状態を設定します。
setCharacters(characters);
ここでは、コントラクトから取得したNFTキャラクターのデ ータを状態として保存しています。
この処理により、NFTキャラクターのデータをフロントエンドで使い始めることができます。
最後に、下記のコードを見ていきましょう。
// gameContractの準備ができたら、NFT キャラクターを読み込みます。
if (gameContract) {
getCharacters();
}
ここでは、gameContract
が更新されるたびに、中身がnull
でないことを確認し、getCharacters