paint-brush
如何使用 Flow 和 Cadence 构建数字收藏品门户(第 2 部分)经过@johnjvester
222 讀數

如何使用 Flow 和 Cadence 构建数字收藏品门户(第 2 部分)

经过 John Vester15m2024/02/27
Read on Terminal Reader

太長; 讀書

通过 Flow 区块链上的 Collectibles 门户并部署到测试网,我们现在可以在本系列的结论中专注于使用 React 创建前端。
featured image - 如何使用 Flow 和 Cadence 构建数字收藏品门户(第 2 部分)
John Vester HackerNoon profile picture
0-item


欢迎来到创建收藏品门户的最后一步! (第 1 部分,请参见此处


在这一部分中,我们将专注于构建前端——拼图的最后一块。


这是我们将实现的目标:


  1. 连接 Flow 钱包。
  2. 初始化您的帐户并铸造您的 NFT。
  3. 检查您收藏中的 NFT ID。
  4. 使用您收藏中的 NFT ID 查看 NFT。


我们将使用Next.js构建前端。


让我们开始吧!


1. 安装

配置

打开您的项目flow-collectible-portal目录。然后,运行
在终端中npx create-next-app@latest frontend并按enter


这将为您提供多种选择。在本教程中,我们不会使用Typescript 、 ESLint 或TailwindCSS ,并且在本文中我们将使用src目录和 App 路由器。


现在您已经准备好了一个新的网络应用程序。

这是您的前端文件夹的外观:



2. 配置

为了与Flow 区块链交互,我们将使用 Flow 客户端库 (FCL) 来管理钱包连接、运行脚本并在应用程序中发送交易。它将允许我们编写完整的 Cadence 函数并将它们作为 Javascript 函数运行。


首先,让我们通过运行以下命令为我们的应用程序安装 FCL:


 npm install @onflow/fcl --save


安装FCL后,我们需要对其进行配置。您需要执行以下操作:


  1. app文件夹内创建一个名为flow的新文件夹并添加一个名为config.js的文件。
  2. 在此文件中,设置 FCL 的配置,例如指定访问节点和钱包发现端点。这可以帮助您选择使用测试网还是本地模拟器。
  3. 您还需要指定我们在第 1 部分中部署的收藏品合约地址。


将以下代码添加到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. 认证

要在应用程序中验证用户身份,您可以使用以下几个函数:


  1. 要登录,请调用fcl.logIn()
  2. 如需注册,请调用fcl.signUp()
  3. 要注销,请调用fcl.unauthenticate()


让我们了解如何在前端实现这些fcl函数。


首先,我们将以下代码添加到 app 目录内的page.js文件中。这将导入一些依赖项,为我们的应用程序的某些部分设置一些初始useState ,并构建一个基本的 UI。


为了确保它看起来不错,请删除应用程序目录中的page.module.css文件,并创建一个名为 page.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区块链

在深入研究如何使用fcl查询 Flow 区块链之前,请在page.js文件中的handleInput函数后面添加这些 Cadence 脚本代码。


 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); }


现在让我们看一下我们编写的所有函数。有两点需要注意:


  1. fcl.query
  2. 以及args: (arg,t) => [arg(addr,t.Address)],行。


由于脚本类似于 Solidity 中的view函数,并且不需要任何 Gas 费用来运行,因此我们本质上只是查询区块链。因此我们使用fcl.query在 Flow 上运行脚本。


要查询某些内容,我们需要传递一个参数。为此,我们使用 arg(一个采用表示参数的字符串值的函数)和t (一个包含 Cadence 拥有的所有不同数据类型的对象)。因此我们可以告诉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指当前连接的帐户。
  • limit就像以太坊世界中的gasLimit,对最大计算量设置了上限。如果计算超过限制,则交易将失败。


我们需要再添加一个函数来调用和处理我们刚刚创建的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”。


结论

做得好!您已完成收藏品门户项目的第 2 部分。总之,我们专注于构建收藏品门户的前端。


我们通过以下方式做到了这一点:


  • 使用 Next.js 创建应用程序
  • 连接 Flow 钱包
  • 创建我们自己的用于铸造的 NFT
  • 查看您的 NFT


祝你有美好的一天!


也发布在这里。