在本节中,我们将使用应用程序和用户界面讨论 NFT 铸造过程。为了获得一些东西并显示元数据如何与 Flow 上的 NFT 一 起工作,我们将使用命令行和 Cadence 脚本。
在我们的 pinata-party 项目的根目录中创建一个新目录,并将它们称为“事务”。创建文件夹后,在其中创建一个名为 MintPinataParty.cdc 的新文件。
我们应该在我们提供给 NFT 的元数据中引用一个文件。文件通过 Pinata 上传到 IPFS。在本教程中,NFT 的重点是可交 易的皮纳塔在派对上被砸的视频。在这个演示中,我们将上传一个孩子在生日聚会上击打皮纳塔的视频。您可以上传任何您想 要的媒体文件并将其与 NFT 相关联。
文件上传后,您将获得 IPFS 哈希。复制哈希,因为它将在铸造过程中使用。现在,在 MintPinataParty.cdc 文件中添加 以下代码。
从 0xf8d6e0586b0a20c7 导入 PinataPartyContract 交易{ 让 receiverRef: & { PinataPartyContract. NFT 接收器} 让 minterRef: &PinataPartyContract. NFTMinter 准备(帐户:AuthAccount ){ 自己。接收者参考= 帐户。getCapability < & { PinataPartyContract. NFTReceiver }>( /public/NFTReceiver ) . 借() ?? 恐慌(“无法借用接收者参考” ) 自己。minterRef = 帐户。借< &PinataPartyContract. NFTMinter >(来自:/storage/NFTMinter ) ?? 恐慌(“无法借用铸币厂参考” ) }执行{ 让元数据:{字符串:字符串} = { “名称”:“大秋千”, “swing_velocity”:“ 29 ”, “swing_angle”:“ 45 ”, “评分”:“ 5 ”, “uri”:“ipfs ://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6” }让 newNFT < - 自我。minterRef 。mintNFT () 自己。接收器参考。存款(令牌:<- newNFT,元数据:元数据) 日志(“NFT 铸造并存入账户 2 的收藏” ) }}
首先,我们定义了两个引用变量,minterRef 和 receiverRef。在这种情况下,我们既是 NFT 的接收者又是铸造者。这些变 量引用在合同中创建的资源。如果执行该事务的人无权访问该资源,则该事务将失败。
上述合约将铸造和存入 NFT。现在,我们将发送交易并铸造 NFT。但在此之前,我们需要准备好账户。在项目的根文件夹中 创建用于从命令行签名的私钥。
运行以下命令:
流键生成
它将为您提供公钥和私钥。
确保保护您的私钥。
您将需要私钥来签署需要粘贴到我们的 flow.json 文件中的交易。
指定签名算法也很重要,以下是 flow.json 文件中的帐户
对象应如下所示:
“帐户”:{ “模拟器帐户”:{ “地址”:“您的帐户地址”, “privateKey”:“你的私钥”, “链”:“流模拟器”, “sigAlgorithm”:“ECDSA_P256”, “哈希算法”:“SHA3_256” }}
如果您想将此项目中的任何内容存储在远程 git 存储库或 Github 上,则不应包含私钥。您可能需要 .gitignore 整个 flow.json。虽然我们只使用本地模拟器,但最好保护密钥。
我们需要做的最后一件事是验证令牌是否在我们的帐户中并获取元数据。为了验证它,我们需要编写一个简单的脚本并从命令 行调用它。
从项目的根目录创建一个名为 scripts 的新文件夹。在文件夹中创建一个名为 CheckTokenMetadata.cdc 的文件。
在该文 件中添加以下代码:
从 0xf8d6e0586b0a20c7 导入 PinataPartyContract 酒吧乐趣主要():{字符串:字符串} { let nftOwner = getAccount ( 0xf8d6e0586b0a20c7 ) // log(“NFT Owner”) 让能力 = nftOwner。getCapability < & { PinataPartyContract. NFTReceiver }>( /public/NFTReceiver ) 让 receiverRef = 能力。借() ?? 恐慌(“无法借用接收者参考” ) 返回接收器参考。获取元数据(id:1 ) }
我们正在从这个脚本中的部署地址导入合约。我们定义了 main 函数,并在其中定义了三个变量: nftOwner 该账户拥有 NFT。 能力 能力是受访问控制的。如果尝试借用它的地址无法使用某个功能,则脚本将失败。在此示例中,我们从 NFTReceiver 资 源中借用功能。 receiverRef 变量获取我们的能力并声明要从部署的合约中借用的脚本。 我们希望确保问题中的地址已收到我们铸造的 NFT,然后我们希望查看与令牌关联的元数据。
使用以下命令运行脚本,看看我们得到了什么: 流脚本执行 ./scripts/CheckTokenMetadata。CDC 你会得到这样的输出: { “名称” :“大摇摆” ,“swing_velocity” :“29” ,“swing_angle” :“45” ,“评级” :“5” ,“uri” : “ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6” }
然后,我们将构建一个前端 React 应用程序,允许您通过获取元数据来显示 NFT。
展示 NFT 收藏品 我们将构建一个简单的 React 应用程序,与 Flow 智能合约交互以验证和获取用户拥有的 NFT。 设置 React 和依赖项 在父目录 pinata-party 中创建 React 应用程序。运行以下命令来创建一个 React 应用程序: npx create-react-app pinata-party-frontend 完成安装后,您将看到一个名为 pinata-party-frontend 的新目录。切换到该目录并安装依赖项。对于前端设置的第一部 分,运行: npm i @onflow/fcl @onflow/types 我们将一些值存储为我们的应用程序的全局变量并使用环境变量。在 react 中,这意味着创建一个 .env 文件并设置键值对, 您需要在其中添加 REACT_APP 前缀。 然后,创建一个用于与 Flow JS SDK 交互的配置文件。在 src 目录下创建一个文件 config.js 并添加以下代码:
从“@onflow/fcl”导入{配置} 配置() . 把(“接入节点。API ”的过程。ENV 。REACT_APP_ACCESS_NODE ) . 把(“挑战。握手”过程。ENV 。REACT_APP_WALLET_DISCOVERY ) . 把(“0xProfile”过程。ENV 。REACT_APP_CONTRACT_PROFILE )
这个配置文件只是帮助 JS SDK 与 Flow 区块链(或本例中的模拟器)一起 工作。要使该文件在整个应用程序中可用, 请打开该 index.js 文件并添加以下行:
导入“./config
在应用程序中具有身份验证功能以启用 NFT 资产的安全转移非常重要。我们需要一个身份验证组件。在 src 目录中创建文件 AuthCluster.js。在该文件中添加以下内容:
从“反应”导入反应,{useState,useEffect} 从“@onflow/fcl”导入 * 作为 fcl 常量 AuthCluster = () => { const [user, setUser] = useState({loggedIn: null}) useEffect(() => fcl.currentUser().subscribe(setUser), []) 如果(user.loggedIn){ 返回 ( < div > <跨度> {user?.addr ?? “无地址”} </ span > < button classname = "”btn-primary”" onclick = "{fcl.unauthenticate}" >注销</ button > </ div > )} 别的 { 返回 ( < div > <按钮类名= “”btn-primary”onClick={fcl.logIn}” >登录</按钮> < button classname = ""btn-secondary"" onclick = "{fcl.signUp}" >注册</ button > </ div > )}}导出默认 AuthCluster
要将此组件放入应用程序,请将 app.js 文件替换为以下内容:
导入'./App.css'; 从 './AuthCluster' 导入 AuthCluster;function App() { 返回 ( < div 类名= “”应用程序”” > <认证集群> </ authcluster > </ div > ); }导出默认应用;
添加上述代码后,您将在启动应用程序时看到一个带有登录注册按钮的页面。现在,是时候构建为帐户获取 NFT 并显示它们 的能力了。
从 Flow 中获取
NFT 要显示我们创建的 NFT,必须与 Flow 区块链进行通信。在本教程中,我们应该能够与 Flow 模拟器进行通信。 让我们构建一个允许获取数据和显示 NFT 数据的组件。在 src 目录中创建一个文件 TokenData.js 并在该文件中添加以下 代码:
从“反应”导入反应,{ useState}; 从“@onflow/fcl”导入 * 作为 fcl; 常量 TokenData = () => { const [nftInfo, setNftInfo] = useState(null) const fetchTokenData = async () => { 常量编码 = 等待 fcl 。发送([ fcl.script` 从 0xf8d6e0586b0a20c7 导入 PinataPartyContract 酒吧乐趣 main() : {String : String} { 让 nftOwner = getAccount(0xf8d6e0586b0a20c7) 让能力 = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver) 让 receiverRef = capability.borrow() ?? 恐慌(“无法借用接收者参考”) 返回 receiverRef.getMetadata(id: 1) }`])常量解码 = 等待 fcl.decode(编码) setNftInfo(解码) };返回 ( < div 类名= ""令牌数据"" > < div 类名= ""center"" > < button classname = "”btn-primary”" onclick = "{fetchTokenData}" >获取令牌数据</ button > </ div > {nftInfo && < div > {Object.keys(nftInfo).map(k => { 返回 ( < p > {k}: {nftInfo[k]} </ p > )})}< button onclick = "{()=" > setNftInfo(null)} className=”btn-secondary”>清除令牌信息</ button > </ div > }</ div > );};导出默认 TokenData;
在这个文件中,我们正在创建一个带有按钮的组件来获取令牌数据。我们还创建了一个按钮来清除令牌数据。单击获取按钮时, 将调用函数 fetchTokenData。该函数使用 Flow JS SDK 运行我们从命令行执行的脚本。获取执行结果并将结果设置为状 态变量 nftInfo。如果变量存在,则从屏幕上的 NFT 元数据和一个用于清除数据的按钮呈现键值对。 从 IPFS 获取媒体
由于我们已经注册了 Pinata 帐户并通过 Pinata 上传界面将视频文件添加到 IPFS,当您在 Pin Explorer 中单击哈希时, 您将被导航到 Pinata IPFS 网关,其中显示 IPFS 内容。 在 TokenData.js 文件中,添加一种显示从 IPFS 检索到的视频文件的方式。将文件更新为如下所示:
从“反应”导入反应,{ useState } ; 从“@onflow/fcl”导入*作为 fcl; 常量 TokenData = ( ) => { const [ nftInfo, setNftInfo ] = useState ( null ) const fetchTokenData = async ( ) => { 常量编码 =等待 fcl . 发送([ 整箱 脚本` 从 0xf8d6e0586b0a20c7 导入 PinataPartyContract 酒吧乐趣 main() : {String : String} { 让 nftOwner = getAccount(0xf8d6e0586b0a20c7) 让能力 = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver) 让 receiverRef = capability.borrow() ?? 恐慌(“无法借用接收者参考”) 返回 receiverRef.getMetadata(id: 1) }`] ) 常量解码 =等待 fcl。解码(编码) setNftInfo (解码) } ; 返回( < div classname= ""token-data"" > < div classname= ""center"" > < button classname= ""btn-primary"" onclick= "{fetchTokenData}" >获取令牌数据< /button> </div > {nftInfo && < div > {目的。键(nftInfo )。地图( k => { 返回( < p > { k } : { nftInfo [ k ] } < / p> )})}<div classname=""center" video"=""> <video id=""nft-video"" canplaythrough="" controls="" width=""85%""> <源 SRC = “{`的 https:/ 。/ IPF 问题 IO / IPF 问题/ $ { nftInfo [ ‘URI’ ] 。分裂(‘ ://’)[1]}`}”类型=“”视频 / MP4”” > < /视频> <div> <button onclick="{()" ==""> setNftInfo(null)} className=”btn-secondary”>清除令牌信息</button > < /div> </div > < /div> }</div > ) ; } ; 导出默认 TokenData;
带有源的视频元素指向 IPFS 上的文件。使用 NFT 创建的 URI 看起来像 ipfs://Qm… 我们这样做是因为 IPFS 桌面客户端允许点击和打开链接。 NFT 现在将成为区块链上真正的实时数字资产。
现在,我们将启用 NFT 的转移。 转移 NFT 首先,我们需要创建建立市场的合同。将为以下对象创建合同: 可替代代币的支付机制 代币供应设置 代币转移能力 让我们创建一个可替代的代币合约,用于购买 NFT 时的支付。 我们将通过定义空合约来创建一个可替代的代币合约: 发布合约 PinnieToken {} 与令牌和提供者资源相关的令牌发布变量需要添加到合约中。
pub var totalSupply: UFix64 pub var tokenName: 字符串 发布资源接口提供者{ 酒吧乐趣撤回(金额:UFix64 ):@Vault { 发布{ 结果。余额== UFix64 (金额): “提款金额必须与提款金库的余额相同。” }}}
在空合约中添加上述合约。 名为 Provider 的资源接口定义了一个公共函数,但账户所有者只能调用它。然后,我们将定义另外两个公共资源接口:
发布资源接口接收器{ 酒吧乐趣存款(来自@Vault ) }pub 资源接口 Balance { 酒吧 var 余额:UFix64 }
上述接口直接位于 Provider 资源接口的下方。Receiver 接口包含任何人都可以执行的功能。它确保只要接收方初始化保险库 以处理通过合约创建的代币,就可以向账户中存款。Balance 资源为任何提供的帐户返回新令牌的余额。 让我们创建 Vault 资源并在 Balance 资源下添加以下代码:
发布资源库:提供者,接收者,余额{ 酒吧 var 余额:UFix64 初始化(平衡:UFix64 ){ 自己。余额=余额 }酒吧乐趣撤回(金额:UFix64 ):@Vault { 自己。平衡=自我。余额- 金额 return < -create Vault (余额:金额) }酒吧乐趣存款(来自:@Vault ){ 自己。平衡=自我。余额+从。平衡 摧毁}}
在 Vault 界面下添加以下函数: 酒吧乐趣 createEmptyVault () : @Vault { return < -create Vault (余额: 0 . 0 ) }顾名思义,该函数为帐户创建一个空的 Vault 资源。余额为 0。 因此,我们现在需要设置铸币能力。在 createEmptyVault 函数下方添加以下代码:
发布资源 VaultMinter { pub fun mintTokens (数量:UFix64,收件人:Capability < &AnyResource { Receiver }>) {让 recipientRef = 收件人。借() ?? 恐慌(“无法借用接收者对保险库的引用” ) 平尼代币。totalSupply = PinnieToken。totalSupply + UFix64 (金额) 收件人参考。存款(来自:< -create Vault (余额:金额)) }}
VaultMinter 资源是公开的,但仅对合约账户所有者可用。 VaultMinter 资源仅包含一个功能:mintTokens,它需要一个铸币量和一个接收者。只要接收者存储了 Vault 资源,新铸 造的代币就可以存入该帐户。 铸造代币时需要更新 totalSupply 变量。因此,铸造的数量被添加到先前的供应中以获得新的供应。 现在,我们需要初始化合约并在 VaultMinter 资源之后添加以下代码:
初始化(){ 自己。总供应量= 30.0 自己。tokenName = “Pinnie” 让 Vault < - 创建 Vault (余额:self.totalSupply ) 自己。帐户。保存(< -vault,到:/storage/MainVault ) 自己。帐户。保存(< -create VaultMinter () ,以便:/存储/ MainMinter ) 自己。帐户。链接< &VaultMinter >( /private/Minter, 目标:/storage/MainMinter ) }
在初始化合约时设置总供应量是必不可少的。在此示例中,我们使用 30 的供应量初始化合约并将代币名称设置为“Pinnie”。 部署和铸造代币 更新项目中的 flow.json 文件以部署新合约。确保 flow.json 文件引用了新合约并具有 emulator-account 密钥引用:
{“模拟器”:{ “默认”:{ “端口”:3569 , “serviceAccount”:“模拟器帐户” }} , “合同”:{ “PinataPartyContract”:“./cadence/contracts/PinataPartyContract. 疾控中心”, “PinnieToken”:“./cadence/contracts/PinnieToken. 疾控中心” } , “网络”:{ “模拟器”:{ “主机”:“ 127.0 . 0.1 :3569 ”, “链”:“流模拟器” }} , “帐户”:{ “模拟器帐户”:{ “地址”:“f8d6e0586b0a20c7”, “钥匙”:“e5ca2b0946358223f0555206144fe4d74e65cbd58b0933c5232ce195b9058cdd” }} , “部署”:{ “模拟器”:{ “模拟器帐户”:[ “PinataPartyContract”,“PinnieToken” ] }}}
在您的 pinata-party 项目目录中的另一个终端窗口中,运行流程项目部署。现在,让我们测试一下铸币功能。我们将创建 一个允许我们创建 Pinnie 代币的交易。但首先,我们需要更新 flow.json 文件。 修改 emulator-account 下的 json:
“模拟器帐户”:{ “地址”:“f8d6e0586b0a20c7”, “私钥”:“e5ca2b0946358223f0555206144fe4d74e65cbd58b0933c5232ce195b9058cdd”, “sigAlgorithm” :“ECDSA_P256” , “哈希算法” :“SHA3_256” , “链” :“流模拟器” }
key 字段变成 privateKey 字段,我们添加属性,包括 sigAlgorithm、chain 和 hashAlgorithm。 开发 NFT 市场 在从事市场前端工作之前,我们应该有一份合同来处理市场的创建和管理。 在 cadence/contracts 文件夹中,创建一个名为 MarketplaceContract.cdc 的新文件。
从 0xf8d6e0586b0a20c7 导入 PinataPartyContract 从 0xf8d6e0586b0a20c7 导入 PinnieToken 发布合同 MarketplaceContract { pub 事件 ForSale ( id: UInt64, price: UFix64 ) 发布事件 PriceChanged ( id: UInt64, newPrice: UFix64 ) pub event TokenPurchased ( id: UInt64, price: UFix64 ) 发布事件 SaleWithdrawn ( id: UInt64 ) 发布资源接口 SalePublic { 酒馆乐趣购买(tokenID:UINT64,收件人:AnyResource { PinataPartyContract。NFTReceiver } ,buyTokens:@PinnieToken 库)pub fun idPrice ( tokenID: UInt64 ) : UFix64? pub fun getIDs () : [ UInt64 ] }}
我们需要同时导入 NFT 合约和可替代代币合约。我们在合约定义中定义了四个事件: 出售:出售 NFT PriceChanged:NFT 价格的变化 TokenPurchased:购买 NFT 时 SaleWithdrawn:当 NFT 从市场中移除时 我们在事件发射器下方添加了一个名为 SalePublic 的资源接口。该接口应该对所有人公开,而不仅仅是合约所有者。 我们需要在 SalePublic 接口下方添加一个 SaleCollection 资源。我们在此资源中定义了一些变量。例如,销售代币的映射、 每个销售代币的价格映射以及一个只能由合约所有者访问的受保护变量,称为 ownerVault。 在资源上定义变量时,我们需要初始化变量。它在 init 函数中完成,并使用所有者的保险库资源和空值进行初始化。 然后,重要的是定义函数来控制 NFT 市场的行为。功能是: 待售清单 改变价格 提取 idPrice 购买
破坏 获取 ID 如上所述,其中三个功能是公开可用的,这意味着 listForSale、withdraw、destroy 和 changePrice 仅对上架的 NFT 所 有者可用。例如,changePrice 不公开,因为我们不希望任何人更改 NFT 的价格。市场合约的最后一部分是 CreateSaleCollection 函数。 它允许将集合作为资源添加到帐户。编写该合约后,我们将使用模拟器帐户部署它。从项目的根目录运行以下命令: 流程项目部署 它将最终部署市场合约并允许我们在前端应用程序中使用它。
评论(0)