lesson-1_WEBアプリをアップグレードしよう
🌊 ユーザー に gemcase のリンクを提供する
NFTが発行された後、gemcaseでNFTへのリンクを共有できます。
gemcaseのNFTへのリンクは次のようになります。
https://gemcase.vercel.app/view/evm/sepolia/0x42d097396b8fe79a06f896db6fed76664777600a/2
リンクには、下記2つの変数が組み込まれています。
https://gemcase.vercel.app/view/evm/sepolia/あなたのコントラクトアドレス/tokenId
🗂 コントラクトを更新して tokenId を取得する
現在、App.js
にはコントラクトアドレスが記載されています。
// App.js
const CONTRACT_ADDRESS = "0x.."; ← こちら
ですが、現在App.js
には、tokenId
が記載されていません。
これから、tokenId
を取得するコードをMyEpicNFT.sol
に追加し、再度デプロイしていきます。
下記2点の変更をMyEpicNFT.sol
に反映させましょう。
1 . NewEpicNFTMinted
イベントを定義する
string[] thirdWords
が定義されているコードの直下に、下記のコードを追加してください。
// MyEpicNFT.sol
event NewEpicNFTMinted(address sender, uint256 tokenId);
2 . NewEpicNFTMinted
イベントをemit
する
makeAnEpicNFT
関数の一番下に、下記のコードを追加しましょう。
// MyEpicNFT.sol
emit NewEpicNFTMinted(msg.sender, newItemId);
このコードが、makeAnEpicNFT
関数の最後の行になるように注意してくだい。
✍️: Solidity では、
event
とemit
が頻繁に使用されます。
上記の実装は、NewEpicNFTMinted
イベントがemit
されるごとに、コントラクトに書き込まれたデータをWebアプリケーションのフロントエンドに反映させることを目的としています。
-
コントラクトでイベントが
emit
されると、フロントエンド(App.js
)でその情報を受け取ります。 -
NewEpicNFTMinted
イベントがemit
される際、フロントエンド(App.js
)で使用する変数msg.sender
とnewItemId
をフロントエンドに送信しています。
✅ 自動テストを更新しよう
makeAnEpicNFT
関数に、新たにNewEpicNFTMinted
イベントのemit
機能を追加したので、正しく機能するかをテストしてみましょう。
それでは、test/MyEpicNFT.js
を更新しましょう。以下を参考に、makeAnEpicNFT
関数のテストをpickRandomThirdWord
関数のテスト下に追加します。
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");
const { ethers } = require("hardhat");
const { expect } = require("chai");
describe("MyEpicNFT", function () {
// 各テストの前に呼び出す関数です。テストで使用する変数やコントラクトのデプロイを行います。
async function deployMyEpicNFTFixture() {
// === 省略 ===
}
describe("pickRandomFirstWord", function () {
// === 省略 ===
});
describe("pickRandomSecondWord", function () {
// === 省略 ===
});
describe("pickRandomThirdWord", function () {
// === 省略 ===
});
// === 追加するテスト ===
describe("makeAnEpicNFT", function () {
it("emit a NewEpicNFTMinted event", async function () {
const { MyEpicNFT, owner } = await loadFixture(deployMyEpicNFTFixture);
await expect(MyEpicNFT.makeAnEpicNFT())
.to.emit(MyEpicNFT, "NewEpicNFTMinted")
.withArgs(owner.address, 0);
});
});
// ===================
});
makeAnEpicNFT
関数のイベント発行のテストを追加しました。
to.emit(コントラクト名, イベント名).withArgs(emitされる値)
と定義することで、発行されるイベントの値を確認することができます。今回のNewEpicNFTMinted
イベントは、第一引数にNFTを受け取るアドレス、第二引数にNFTのIDを設定するので期待する値を上記のようにテストしています。
それでは、自動テストを実行してみましょう。ETH-NFT-Collection
ディレクトリ直下で次のコマンドを実行します。
yarn contract test
以下のような出力があり、全てのテストに通過したことが確認できたら完了です!
MyEpicNFT
pickRandomFirstWord
This is my NFT contract.
rand - seed: 96777463446932378109744360884080025980584389114515208476196941633474201541706
rand - first word: 0
✔ should get strings in firstWords (1037ms)
pickRandomSecondWord
✔ should get strings in secondWords
pickRandomThirdWord
✔ should get strings in thirdWords
makeAnEpicNFT
rand - seed: 96777463446932378109744360884080025980584389114515208476196941633474201541706
rand - first word: 0
----- SVG data -----
<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 350 350'><style>.base { fill: white; font-family: serif; font-size: 24px; }</style><rect width='100%' height='100%' fill='black' /><text x='50%' y='50%' class='base' dominant-baseline='middle' text-anchor='middle'>EpicPopBird</text></svg>
--------------------
----- Token URI ----
data:application/json;base64,eyJuYW1lIjogIkVwaWNQb3BCaXJkIiwgImRlc2NyaXB0aW9uIjogIkEgaGlnaGx5IGFjY2xhaW1lZCBjb2xsZWN0aW9uIG9mIHNxdWFyZXMuIiwgImltYWdlIjogImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjRiV3h1Y3owbmFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNQzl6ZG1jbklIQnlaWE5sY25abFFYTndaV04wVW1GMGFXODlKM2hOYVc1WlRXbHVJRzFsWlhRbklIWnBaWGRDYjNnOUp6QWdNQ0F6TlRBZ016VXdKejQ4YzNSNWJHVStMbUpoYzJVZ2V5Qm1hV3hzT2lCM2FHbDBaVHNnWm05dWRDMW1ZVzFwYkhrNklITmxjbWxtT3lCbWIyNTBMWE5wZW1VNklESTBjSGc3SUgwOEwzTjBlV3hsUGp4eVpXTjBJSGRwWkhSb1BTY3hNREFsSnlCb1pXbG5hSFE5SnpFd01DVW5JR1pwYkd3OUoySnNZV05ySnlBdlBqeDBaWGgwSUhnOUp6VXdKU2NnZVQwbk5UQWxKeUJqYkdGemN6MG5ZbUZ6WlNjZ1pHOXRhVzVoYm5RdFltRnpaV3hwYm1VOUoyMXBaR1JzWlNjZ2RHVjRkQzFoYm1Ob2IzSTlKMjFwWkdSc1pTYytSWEJwWTFCdmNFSnBjbVE4TDNSbGVIUStQQzl6ZG1jKyJ9
--------------------
An NFT w/ ID 0 has been minted to 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
✔ emit a NewEpicNFTMinted event (89ms)
4 passing (1s)
🛩 もう一度デプロイする
コントラクトを更新したので、下記を実行する必要があります。
1. 再度コントラクトをデプロイする
2. フロントエンドのコントラクトアドレスを更新する(更新するファイル: App.js
)
3. フロントエンドのABIファイルを更新する(更新するファイル: packages/client/src/utils/MyEpicNFT.json
)
コントラクトを更新するたび、これらの 3 つのステップを実行する必要があります。
復習もかねて、丁寧に実行していきましょう。
1. ターミナル上でETH-NFT-Collection
ディレクトリ直下に移動します。
下記を実行し、コントラクトを再度デプロイしましょう。
yarn contract deploy:sepolia
下記のように、ターミナルに出力されたコントラクトアドレス(0x..
)をコピーしましょう。
Contract deployed to: 0x... ← あなたのコントラクトアドレスをコピー
2. コピーしたアドレスをApp.js
のconst CONTRACT_ADDRESS = "こちら"
に貼り付けましょう。
3. 以前と同じようにartifacts
から ABI ファイルを取得します。下記のステップを実行してください。
1. ターミナル上でpackages/contract
ディレクトリにいることを確認する(もしくは移動する)。
2. ターミナル上で下記を実行する。
code artifacts/contracts/MyEpicNFT.sol/MyEpicNFT.json
3. VS CodeでMyEpicNFT.json
ファイルが開かれるので、中身をすべてコピーする。※ VS Codeのファインダーを使って、直接MyEpicNFT.json
を開くことも可能です。
4. コピーしたpackages/contract/artifacts/contracts/MyEpicNFT.sol/MyEpicNFT.json
の中身をpackages/client/src/utils/MyEpicNFT.json
の中身と交換する。
繰り返しますが、コントラクトを更新するたびにこれを行う必要があります。
🪄 フロントエンドを更新する
下記のように、App.js
を更新してください。
まず、const TWITTER_HANDLE = 'こちら'
に、あなたのTwitterハンドルを貼り付けてみてください。あなたのWebサイトからあなたのTwitterアカウントをリンクさせることができます。
次に、下記2つのコードブロックにsetupEventListener()
を設定しましょう。
1つ目のイベントリスナを設定。
// App.js
//ユーザーが認証可能なウォレットアドレスを持っている場合は、ユーザーに対してウォレットへのアクセス許可を求める。許可されれば、ユーザーの最初のウォレットアドレスを accounts に格納する。
const accounts = await ethereum.request({ method: "eth_accounts" });
if (accounts.length !== 0) {
const account = accounts[0];
console.log("Found an authorized account:", account);
setCurrentAccount(account);
// **** イベントリスナーをここで設定 ****
// この時点で、ユーザーはウォレット接続が済んでいます。
setupEventListener();
} else {
console.log("No authorized account found");
}
2つ目のイベントリスナを設定。
// App.js
// connectWallet メソッドを実装します。
const connectWallet = async () => {
try {
const { ethereum } = window;
if (!ethereum) {
alert("Get MetaMask!");
return;
}
// ウォレットアドレスに対してアクセスをリクエストしています。
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
console.log("Connected", accounts[0]);
// ウォレットアドレスを currentAccount に紐付けます。
setCurrentAccount(accounts[0]);
// **** イベントリスナーをここで設定 ****
setupEventListener();
} catch (error) {
console.log(error);
}
};
次に、connectWallet
関数の直下に、下記のsetupEventListener
関数を追加してください。
// App.js
// setupEventListener 関数を定義します。
// MyEpicNFT.sol の中で event が emit された時に、
// 情報を受け取ります。
const setupEventListener = async () => {
try {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
// NFT が発行されます。
const connectedContract = new ethers.Contract(
CONTRACT_ADDRESS,
myEpicNft.abi,
signer
);
// Event が emit される際に、コントラクトから送信される情報を受け取っています。
connectedContract.on("NewEpicNFTMinted", (from, tokenId) => {
console.log(from, tokenId.toNumber());
alert(
`あなたのウォレットに NFT を送信しました。gemcase に表示されるまで数分かかることがあります。NFT へのリンクはこちらです: https://gemcase.vercel.app/view/evm/sepolia/${CONTRACT_ADDRESS}/${tokenId.toNumber()}`
);
});
console.log("Setup event listener!");
} else {
console.log("Ethereum object doesn't exist!");
}
} catch (error) {
console.log(error);
}
};
setupEventListener
関数は、NFTが発行される際にemit
されるNewEpicNFTMinted
イベントを受信します。
tokenId
を取得して、新しくミントされたNFTへのgemcaseリンクをユーザーに提供しています。