译文出自:登链翻译计划 [1]
译者:翻译小组 [2]
校对:Tiny 熊 [3]
本文是 NFT 教程的的第二部分,本教程主要介绍 Flow 区块链、NFT 和 IPFS。请先阅读第一部分 NFT 教程 - 用 Flow 和 IPFS 创建 NFT[4]。
在本文中,我们将构建一个简单的 React 应用程序,该应用程序与 Flow 智能合约交互,以验证和获取用户拥有的 NFT。然后,我们将解析 NFT 的元数据,以获得 NFT 的底层标的资产(在本例中为视频)的 IPFS 位置。提醒一下我们正在打造的 NFT,就像 NBA Top Shot 一样,只不过是不同的视频内容。
项目设置
本教程中,你需要运行 Flow 模拟器。如果你不记得如何启动,可以看看之前的文章或者查看 Flow CLI 文档。需要注意的是,Flow 模拟器是 Flow 区块链在内存的模拟。所以,如果关闭了模拟器,你需要继续做以下工作:
-
启动 Flow 模拟器
-
部署项目
-
铸造你的 NFT
在本教程第一部分 NFT 教程 - 用 Flow 和 IPFS 创建 NFT[5] 详细介绍了其中的每一个步骤。
此外,还需要在你的机器上安装 NodeJS。你可以在这里安装 [6]。
和之前一样,你需要一个文本编辑器。
初始化 React 和安装依赖
在第一部分教程中创建的
pinata-party
项目目录下创建 React 应用(你也可以在一个全新的目录中创建你的 React 应用)。
要创建我们的应用程序,运行以下命令:
npx create-react-app pinata-party-frontend
当一切安装完成后,你会有一个新的目录,叫做
pinata-party-frontend
, 切换到该目录,安装依赖。
首先,参考 Flow 文档 [7],需要安装 Flow JS SDK。前端的设置我们只需要按照 Flow 的文档进行即可:
npm i @onflow/fcl @onflow/types
一些值需要作为应用程序的全局变量来存储,这里使用环境变量。在 react 中,创建一个
.env
文件,并设置键值对,其中键值前缀为
REACT_APP
。在 Flow 的文档里,设置为与 Flow 的测试网连接。在本教程中,我们将连接到 Flow 模拟器。所以,需要做一些改变。在
.env
文件中添加以下内容:
REACT_APP_ACCESS_NODE=http://localhost:8080 REACT_APP_WALLET_DISCOVERY=https://fcl-discovery.onflow.org/testnet/authn REACT_APP_CONTRACT_PROFILE=0xf8d6e0586b0a20c7
将
REACT_APP_ACCESS_NODE
的值替换为上述的本地模拟器 url。用部署项目时获得的地址替换
REACT_APP_CONTRACT_PROFILE
值。
还需要创建一个配置文件,用来与 Flow JS SDK 交互。在
hide
目录下创建一个名为
config.js
的文件。增加以下内容:
import {config} from "@onflow/fcl" config() .put("accessNode.api", process.env.REACT_APP_ACCESS_NODE) .put("challenge.handshake", process.env.REACT_APP_WALLET_DISCOVERY) .put("0xProfile", process.env.REACT_APP_CONTRACT_PROFILE)
这个配置文件只是帮助 JS SDK 与 Flow 区块链(或本例中的模拟器)一起工作。要使这个文件在整个应用程序中可用,打开
index.js
文件并添加这一行。
import "./config"
现在,让我们连接一些认证。如果你不想的话,可以不必强迫人们认证后进入网站,教程的第三篇文章,在实现 NFT 资产的转移时,认证将是很重要的。
我们需要创建一个认证组件。在你的
hide
目录下,创建一个名为
AuthCluster.js
的文件。在该文件内,添加以下内容:
import React, {useState, useEffect} from 'react' import * as fcl from "@onflow/fcl" const AuthCluster = () => { const [user, setUser] = useState({loggedIn: null}) useEffect(() => fcl.currentUser().subscribe(setUser), []) if (user.loggedIn) { return ( <p> <span>{user?.addr ?? "No Address"}span> <button className="btn-primary" onClick={fcl.unauthenticate}>Log Outbutton> p> ) } else { return ( <p> <button className="btn-primary" onClick={fcl.logIn}>Log Inbutton> <button className="btn-secondary" onClick={fcl.signUp}>Sign Upbutton> p> ) } } export default AuthCluster // rawAuthCluster.js
代码很简单,使用一个登录和注册按钮,利用 Flow JS SDK 的能力连接到钱包提供者, 你可以注册一个账户或用现有账户登录。
现在需要把这个组件放到我们的应用程序中。我们先简单点吧。将你的
App.js
文件替换为以下内容。
import './App.css'; import AuthCluster from './AuthCluster';function App() { return ( <p className="App"> <AuthCluster /> p> ); } export default App;
如果你现在启动应用程序 (npm start),你应该会看到一个有登录和注册按钮的页面。事实上,这两个按钮都是有功能的, 试试吧。
好了,现在 React 应用已经基本设置好了,让我们开始构建获取账户的 NFT 并显示它们。
从 Flow 中获取 NFT
为了显示我们在第一篇文章 [8] 中铸币的 NFT,需要与 Flow 区块链进行通信。现在是与 Flow 模拟器进行通信。设置
.env
文件时,已经告诉应用程序,模拟器是在 8080 端口上运行的。但现在,如何使用 JavaScript 与 Flow 交互?
幸运的是,Flow 在他们的 JS SDK 中内置了这个功能。如果你还记得,我们之前写了一个脚本,根据一个 NFT 的 token id 来查找它,并返回 token 的元数据。它看起来像这样:
import PinataPartyContract from 0xf8d6e0586b0a20c7 pub fun main() : {String : String} { let nftOwner = getAccount(0xf8d6e0586b0a20c7) // log("NFT Owner") let capability = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver) let receiverRef = capability.borrow() ?? panic("Could not borrow the receiver reference") return receiverRef.getMetadata(id: 1) }
CheckTokenMetadata.cdc
现在,我们只需要将其转换为 JavaScript 调用即可。让我们创建一个新的组件,既能获取数据,又能最终显示 NFT 数据。在你的
hide
目录下,创建一个名为
TokenData.js
的文件。在该文件中,添加以下内容:
import React, { useState } from "react"; import * as fcl from "@onflow/fcl"; const TokenData = () => { const [nftInfo, setNftInfo] = useState(null) const fetchTokenData = async () => { const encoded = await fcl .send([ fcl.script` import PinataPartyContract from 0xf8d6e0586b0a20c7 pub fun main() : {String : String} { let nftOwner = getAccount(0xf8d6e0586b0a20c7) let capability = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver) let receiverRef = capability.borrow() ?? panic("Could not borrow the receiver reference") return receiverRef.getMetadata(id: 1) } ` ]) const decoded = await fcl.decode(encoded) setNftInfo(decoded) }; return ( <p className="token-data"> <p className="center"> <button className="btn-primary" onClick={fetchTokenData}>Fetch Token Databutton> p> { nftInfo && <p> { Object.keys(nftInfo).map(k => { return ( <p>{k}: {nftInfo[k]}p> ) }) } <button onClick={() => setNftInfo(null)} className="btn-secondary">Clear Token Infobutton> p> } p> ); }; export default TokenData; //rawTokenData.js
在这个文件中,创建了一个组件,有一个按钮来获取代币数据。当点击获取按钮时,它会调用我们创建的一个名为
fetchTokenData
的函数。该函数使用 Flow JS SDK 来执行与在本教程第一部分中从命令行执行的脚本完全相同的脚本,但在 React 中。我们把执行的结果,设置到一个名为
nftInfo
的状态变量中。React 会根据
nftInfo
显示 NFT 元数据中的键值对。另外还有一个让清除数据的按钮。
我还加了一点 CSS,让他漂亮一些,
App.css
定义如下:
.App { display: flex; flex-direction: column; min-height: 500px; justify-content: center; align-items: center; } button { padding: 10; height: 30px; min-width: 100px; cursor: pointer; } .btn-primary { border: none; background: rgb(255, 224, 0); color: #282828; } .btn-secondary { border: none; background: rgb(0, 190, 221); color: #282828; } .center { text-align: center; } .token-data { margin-top: 100px; }
现在,只要将新组件添加到
App.js
中,放在
AuthCluster
组件下面:
import './App.css'; import AuthCluster from './AuthCluster'; import TokenData from './TokenData';function App() { return ( "App"> ); }export default App;
运行应用程序并尝试获取代币数据,它应该是这样:
这真是太酷了!我们正在查找指定的账户所拥有的 NFT,然后从该代币中获取元数据。并显示该元数据,我们知道该元数据中的一个值解析为一个视频文件。让我们把它显示出来。
从 IPFS 获取媒体文件
你已经注册了一个 Pinata[9] 账户,并通过 Pinata 上传界面将你的视频文件添加到 IPFS。这意味着你已经可以从 IPFS 中获取内容了。在 Pin Explorer[10] 中,当你点击一个哈希值时,你会被带到 Pinata IPFS 网关,在那里你的 IPFS 内容被解析并显示。为了教程更通用,我们还是从 Protocol Labs 网关中获取它。
回到
TokenData.js
文件中,让我们添加一个方法来显示从 IPFS 中检索到的视频文件,修改代码:
import React, { useState } from "react"; import * as fcl from "@onflow/fcl"; const TokenData = () => { const [nftInfo, setNftInfo] = useState(null) const fetchTokenData = async () => { const encoded = await fcl .send([ fcl.script` import PinataPartyContract from 0xf8d6e0586b0a20c7 pub fun main() : {String : String} { let nftOwner = getAccount(0xf8d6e0586b0a20c7) let capability = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver) let receiverRef = capability.borrow() ?? panic("Could not borrow the receiver reference") return receiverRef.getMetadata(id: 1) } ` ]) const decoded = await fcl.decode(encoded) setNftInfo(decoded) }; return ( <p className="token-data"> <p className="center"> <button className="btn-primary" onClick={fetchTokenData}>Fetch Token Databutton> p> { nftInfo && <p> { Object.keys(nftInfo).map(k => { return ( <p>{k}: {nftInfo[k]}p> ) }) } <p className="center video"> <video id="nft-video" canplaythrough controls width="85%"> <source hide={`https://ipfs.io/ipfs/${nftInfo["uri"].split("://")[1]}`} type="video/mp4" /> video> <p> <button onClick={() => setNftInfo(null)} className="btn-secondary">Clear Token Infobutton> p> p> p> } p> ); }; export default TokenData; // rawTokenData.js
我们已经添加了一个 video 标签,它指向 IPFS 上的文件。你会注意到,这里拆分了
uri
值,以获得 IPFS 哈希值,这样就可以从 IPFS 网关获取对应内容。先介绍下那个 URI。
我们用 NFT 创建的 uri 看起来像
ipfs://Qm...
。我们之所以这样创建,是因为 IPFS 桌面客户端默认允许你点击并打开这样的链接。另外,Brave 浏览器也支持粘贴这样的链接。并且我们认为这种链接形式会随着 IPFS 的发展得到越来越多的支持。
然而,在这里下,我们需要在利用哈希来从 IPFS 公共网关获取内容,并在页面上显示。因此链接会是这样:
https://ipfs.io/ipfs/QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6
现在,如果你访问我们的应用程序中获取代币数据,会看到如下界面:
这是一个真正的活的数字资产 ! 你的视频可能会有所不同,但希望你在应用中感受到相同的体验。
最后
这是一个非常简单的应用,你可以做很多事情让它变得更漂亮,让它的交互性更强,甚至可以为它添加更多的 Flow 元素。Flow JS SDK 的功能很强大,所以我推荐大家阅读一下文档。
在第二部分成功地使用 Flow 为应用添加了身份验证,创建了一个接口来获取 NFT 的信息,创建了一种方法来显示了原始元数据以及对应的底层标的资产。这一切都由 Flow 区块链和 IPFS 来保障。我们知道 NFT 是由谁拥有,也知道显示的内容是有效性,因为哈希值被编码到 NFT 中。
在本教程的最后一篇,我们将专注于创建一个迷你交易市场,让我们转移 NFT。
本翻译由 Cell Network[11] 赞助支持。
参考资料
[1]
登链翻译计划 : https://github.com/lbc-team/Pioneer
[2]
翻译小组 : https://learnblockchain.cn/people/412
[3]
Tiny 熊 : https://learnblockchain.cn/people/15
[4]
NFT 教程 - 用 Flow 和 IPFS 创建 NFT: https://learnblockchain.cn/article/2271
[5]
NFT 教程 - 用 Flow 和 IPFS 创建 NFT: https://learnblockchain.cn/article/2271
[6]
这里安装 : https://nodejs.org/en/
[7]
Flow 文档 : https://docs.onflow.org/flow-js-sdk/flow-app-quickstart
[8]
第一篇文章 : https://learnblockchain.cn/article/2271
[9]
Pinata: https://pinata.cloud/
[10]
Pin Explorer: https://pinata.cloud/pinexplorer
[11]
Cell Network: https://www.cellnetwork.io/?utm_souce=learnblockchain