コレクティブル ポータル作成の最終ステップへようこそ! (パート 1 については、 参照してください) こちらを このパートでは、パズルの最後のピースであるフロントエンドの構築に焦点を当てます。 達成することは次のとおりです。 フローウォレットを接続します。 アカウントを初期化し、NFTを鋳造します。 コレクション内の NFT ID を確認してください。 コレクションにある NFT ID を使用して NFT を表示します。 使用してフロントエンドを構築します。 Next.js を 始めましょう! 1. インストール セットアップ プロジェクトの ディレクトリを開きます。それから、走ってください ターミナルで と入力し、 を押します。 flow-collectible-portal npx create-next-app@latest frontend enter これにより、いくつかのオプションが提供されます。このチュートリアルでは、 、 ESLint 、または を使用せず、この記事の時点では ディレクトリと App ルーターを使用します。 Typescript TailwindCSS src これで、新しい Web アプリが準備できました。 フロントエンド フォルダーは次のようになります。 2. 構成 と対話するには、Flow クライアント ライブラリ (FCL) を使用してウォレット接続を管理し、スクリプトを実行し、アプリケーションでトランザクションを送信します。これにより、完全な Cadence 関数を作成し、JavaScript 関数として実行できるようになります。 Flow ブロックチェーン まず、次のコマンドを実行してアプリの FCL をインストールしましょう。 npm install @onflow/fcl --save FCL をインストールした後、それを構成する必要があります。行う必要があるのは次のとおりです。 フォルダー内に という名前の新しいフォルダーを作成し、 という名前のファイルを追加します。 app flow config.js このファイルでは、アクセス ノードとウォレット検出エンドポイントの指定など、FCL の構成をセットアップします。これは、テストネットを使用するかローカル エミュレーターを使用するかを選択するのに役立ちます。 パート 1 で展開した Collectibles コントラクトのアドレスも指定する必要があります。 次のコードを ファイルに追加します。 config.js import { config } from "@onflow/fcl"; config({ "app.detail.title": "Flow Name Service", "app.detail.icon": "https://placekitten.com/g/200/200", "accessNode.api": "https://rest-testnet.onflow.org", "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn", "0xCollectibles": "ADD YOUR CONTRACT ACCOUNT ADDRESS", "0xNonFungibleToken": "0x631e88ae7f1d7c20", }); これで、アプリで FCL を使用するための設定がすべて完了しました。 3. 認証 アプリでユーザーの ID を確認するには、いくつかの関数を使用できます。 ログインするには、 を呼び出します。 fcl.logIn() サインアップするには、 を呼び出します。 fcl.signUp() ログアウトするには、 を呼び出します。 fcl.unauthenticate() これらの 関数をフロントエンドに実装する方法を学びましょう。 fcl まず、app ディレクトリ内の ファイルに次のコードを追加します。これにより、いくつかの依存関係がインポートされ、アプリの一部に初期 が設定され、基本的な UI が構築されます。 page.js useState 見栄えを良くするには、アプリ ディレクトリ内の ファイルを削除し、代わりに page.css というファイルを作成します。次に、 の内容をその中に貼り付けます。これで、最初のページを書き出すことができます。 page.module.css このファイル "use client"; import React, { useState, useEffect, useRef } from "react"; import * as fcl from "@onflow/fcl"; import "./page.css"; import "./flow/config"; export default function Page() { const [currentUser, setCurrentUser] = useState({ loggedIn: false, addr: undefined, }); const urlInputRef = useRef(); const nameInputRef = useRef(); const idInputRef = useRef(); const [isInitialized, setIsInitialized] = useState(); const [collectiblesList, setCollectiblesList] = useState([]); const [loading, setLoading] = useState(false); const [ids, setIds] = useState([]); const [nft, setNFT] = useState({}); useEffect(() => fcl.currentUser.subscribe(setCurrentUser), []); function handleInputChange(event) { const inputValue = event.target.value; if (/^\d+$/.test(inputValue)) { idInputRef.current = +inputValue; } else { console.error("Invalid input. Please enter a valid integer."); } } return ( <div> <div className="navbar"> <h1>Flow Collectibles Portal</h1> <span>Address: {currentUser?.addr ?? "NO Address"}</span> <button onClick={currentUser.addr ? fcl.unauthenticate : fcl.logIn}> {currentUser.addr ? "Log Out" : "Connect Wallet"} </button> </div> {currentUser.loggedIn ? ( <div className="main"> <div className="mutate"> <h1>Mutate Flow Blockchain</h1> <form onSubmit={(event) => { event.preventDefault(); }} > <input type="text" placeholder="enter name of the NFT" ref={nameInputRef} /> <input type="text" placeholder="enter a url" ref={urlInputRef} /> <button type="submit">Mint</button> </form> <mark>Your Collection will be initialized while minting NFT.</mark> </div> <div className="query"> <h1>Query Flow Blockchain</h1> <mark>Click below button to check 👇</mark> <button>Check Collection</button> <p> Is your collection initialized: {isInitialized ? "Yes" : "No"} </p> <button onClick={viewIds}> View NFT IDs you hold in your collection </button> <p>NFT Id: </p> </div> <div className="view"> <h1>View Your NFT</h1> <input type="text" placeholder="enter your NFT ID" onChange={handleInputChange} /> <button>View NFT</button> <div className="nft-card"> <p>NFT id: </p> <p>NFT name: </p> <img src="" alt="" /> </div> </div> </div> ) : ( <div className="main-2"> <h1>Connect Wallet to mint NFT!!</h1> </div> )} </div> ); } このコードを追加した後、 実行して、すべてが正しく読み込まれることを確認します。 npm run dev 4. Flowブロックチェーンのクエリ を使用して Flow ブロックチェーンをクエリする方法を詳しく説明する前に、 ファイルの 関数の後に次の Cadence スクリプト コードを追加します。 fcl page.js handleInput const CHECK_COLLECTION = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(address: Address): Bool? { return Collectibles.checkCollection(_addr: address) }` const GET_NFT_ID = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(user: Address): [UInt64] { let collectionCap = getAccount(user).capabilities.get <&{Collectibles.CollectionPublic}>(/public/NFTCollection) ?? panic("This public capability does not exist.") let collectionRef = collectionCap.borrow()! return collectionRef.getIDs() } ` const GET_NFT = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(user: Address, id: UInt64): &NonFungibleToken.NFT? { let collectionCap= getAccount(user).capabilities.get<&{Collectibles.CollectionPublic}>(/public/NFTCollection) ?? panic("This public capability does not exist.") let collectionRef = collectionCap.borrow()! return collectionRef.borrowNFT(id: id) } Cadence スクリプトの準備が整ったので、いくつかの Javascript 関数を宣言し、Cadence 定数を クエリに渡すことができます。 `fcl` async function checkCollectionInit() { const isInit = await fcl.query({ cadence: CHECK_COLLECTION, args: (arg,t) => [arg(currentUser?.addr, t.Address)], }); console.log(isInit); } async function viewNFT() { console.log(idInputRef.current); const nfts = await fcl.query({ cadence: GET_NFT, args: (arg,t) => [arg(currentUser?.addr,t.Address), arg(idInputRef.current, t.UInt64)] }); setNFT(nfts); console.log(nfts); } async function viewIds() { const ids = await fcl.query({ cadence: GET_NFT_ID, args: (arg,t) => [arg(currentUser?.addr,t.Address)] }); setIds(ids); console.log(ids); } それでは、作成したすべての関数を見てみましょう。注意すべき点が 2 つあります。 fcl.query そして 行。 args: (arg,t) => [arg(addr,t.Address)], スクリプトは Solidity の 関数に似ており、実行にガス料金を必要としないため、基本的にはブロックチェーンにクエリを実行するだけです。そこで、 を使用して Flow 上でスクリプトを実行します。 view fcl.query 何かをクエリするには、引数を渡す必要があります。そのために、引数を表す文字列値を取る関数である arg と、Cadence が持つさまざまなデータ型をすべて含むオブジェクトである を使用します。したがって、渡す引数をエンコードおよびデコードする方法を に伝えることができます。 t arg 5. Flowブロックチェーンの変異 以前の関数は単なる「読み取り専用」でしたが、次の関数にはブロックチェーンの状態を変更して書き込みできるアクションが含まれます。別名「NFTのミント」。 これを行うには、定数として別の Cadence スクリプトを作成します。 const MINT_NFT = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles transaction(name:String, image:String){ let receiverCollectionRef: &{NonFungibleToken.CollectionPublic} prepare(signer:AuthAccount){ // initialise account if signer.borrow<&Collectibles.Collection>(from: Collectibles.CollectionStoragePath) == nil { let collection <- Collectibles.createEmptyCollection() signer.save(<-collection, to: Collectibles.CollectionStoragePath) let cap = signer.capabilities.storage.issue<&{Collectibles.CollectionPublic}>(Collectibles.CollectionStoragePath) signer.capabilities.publish( cap, at: Collectibles.CollectionPublicPath) } //takes the receiver collection refrence self.receiverCollectionRef = signer.borrow<&Collectibles.Collection>(from: Collectibles.CollectionStoragePath) ?? panic("could not borrow Collection reference") } execute{ let nft <- Collectibles.mintNFT(name:name, image:image) self.receiverCollectionRef.deposit(token: <-nft) } } 次に、 ファイルのトランザクション コードの後に以下の関数を追加します。 page.js async function mint() { try{ const txnId = await fcl.mutate({ cadence: MINT_NFT, args: (arg,t) => [arg(name,t.String), arg(image, t.String)], payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit:999,}); } catch(error){ console.error('Minting failed:' error) } console.log(txnId); } 関数に関しては、 構文は と同じです。ただし、次のような追加のパラメーターがいくつか提供されています。 fcl.mutate fcl.query payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit: 50, これらは、どのアカウントがトランザクションの支払いを行うか (支払者)、トランザクションをブロードキャストするか (提案者)、および承認が必要なアカウントを定義するフロー固有のものです。 (アカウントに複数のキーがアタッチされている場合、マルチシグ ウォレットのように動作する可能性があります。) 現在接続されているアカウントを指します。 fcl.authz 、イーサリアムの世界における GasLimit に似ており、最大計算量に上限を設けます。計算が制限を超えると、トランザクションは失敗します。 limit 先ほど作成した 関数を呼び出して処理する関数をもう 1 つ追加する必要があります。 mintNFT const saveCollectible = async () => { if (urlInputRef.current.value.length > 0 && nameInputRef.current.value.length > 0) { try { setLoading(true); const transaction = await mintNFT(nameInputRef.current.value, urlInputRef.current.value); console.log('transactionID:', transaction); // Handle minting success (if needed) } catch (error) { console.error('Minting failed:', error); // Handle minting failure (if needed) } finally { setLoading(false); } } else { console.log('Empty input. Try again.'); } }; 6. 最終コード 主要な関数を配置したら、それらを UI にプラグインできるようになります。 ただし、その前に、初期状態を読み込むのに役立つ 呼び出しをいくつか追加します。これらは、既存の 呼び出しのすぐ上に追加できます。 useEffect useEffect useEffect(() => { checkCollectionInit(); viewNFT(); }, [currentUser]); useEffect(() => { if (currentUser.loggedIn) { setCollectiblesList(collectiblesList); console.log('Setting collectibles...'); } }, [currentUser]); UI を含む セクションに戻り、アプリの適切な部分に関数を追加できます。 return return ( <div> <div className="navbar"> <h1>Flow Collectibles Portal</h1> <span>Address: {currentUser?.addr ?? "NO Address"}</span> <button onClick={currentUser.addr ? fcl.unauthenticate : fcl.logIn}> {currentUser.addr ? "Log Out" : "Connect Wallet"} </button> </div> {currentUser.loggedIn ? ( <div className="main"> <div className="mutate"> <h1>Mutate Flow Blockchain</h1> <form onSubmit={(event) => { event.preventDefault(); saveCollectible(); }} > <input type="text" placeholder="enter name of the NFT" ref={nameInputRef} /> <input type="text" placeholder="enter a url" ref={urlInputRef} /> <button type="submit">Mint</button> </form> <mark>Your Collection will be initialized while minting NFT.</mark> </div> <div className="query"> <h1>Query Flow Blockchain</h1> <mark>Click below button to check 👇</mark> <button onClick={checkCollectionInit}>Check Collection</button> <p> Is your collection initialized: {isInitialized ? "Yes" : "No"} </p> <button onClick={viewIds}> View NFT IDs you hold in your collection </button> <p>NFT Id: </p> {ids.map((id) => ( <p key={id}>{id}</p> ))} </div> <div className="view"> <h1>View Your NFT</h1> <input type="text" placeholder="enter your NFT ID" onChange={handleInputChange} /> <button onClick={viewNFT}>View NFT</button> <div className="nft-card"> <p>NFT id: {nft.id}</p> <p>NFT name: {nft.name}</p> <img src={nft.image} alt={nft.name} /> </div> </div> </div> ) : ( <div className="main-2"> <h1>Connect Wallet to mint NFT!!</h1> </div> )} </div> ); 最終的なコードを 確認してください。 ここで アプリが完成したので、使い方を見ていきましょう。 まず、右上の「ウォレットを接続」ボタンをクリックしてウォレットを接続します。 これでNFTを鋳造できるようになりました! NFT の名前を入力し、使用する画像へのリンクを貼り付けます。 「ミント」をクリックすると、ウォレットでのトランザクションに署名するよう求められます。 トランザクションが完了するまでに少し時間がかかる場合があります。完了したら、下のボタンをクリックして NFT の ID を表示できるようになります。初めての場合は、ID は「1」だけにしてください。 これで、NFT の ID をコピーし、[表示] セクションに貼り付けて、[NFT を表示] をクリックできます。 結論 よくやった! Collectibles ポータル プロジェクトのパート 2 が完了しました。要約すると、私たちは Collectibles ポータルのフロントエンドの構築に重点を置きました。 これは次のようにして行いました。 Next.js を使用したアプリの作成 フローウォレットの接続 鋳造用に独自の NFT を作成する NFT の表示 本当に素晴らしい一日をお過ごしください! こちらでも公開しております。