当前位置: 首页 > 科技观察

如何使用Flow和IPFS创建NFT

时间:2023-03-14 12:10:54 科技观察

如何使用Flow和IPFS创建NFT本文转载请联系区块链研究实验室公众号。随着不可替代代币(NFT)市场达到顶峰,回顾NFT相对早期的日子并记住CryptoKitties所面临的挑战是很有趣的。DapperLabs团队构建的平台是潜在大规模采用的第一个真实例子,也是对以太坊区块链的第一个真正压力。从那时起,NFT开始腾飞,Rarible、OpenSea、Foundation和Sorare等平台也是如此。每月有数百万美元流经这些平台。尽管早期存在困难,但大部分都发生在以太坊区块链上。然而,根据他们在CryptoKitties方面的经验,DapperLabs的团队着手构建一个新的区块链,该区块链将具有通用性,但也非常适合NFT用例。这样做的目的是解决以太坊上NFT遇到的许多问题,同时为该领域的开发者和收藏者提供更好的体验。今天我们讨论如何在IPFS支持的Flow上创建NFT。Flow区块链的主要早期应用之一是NBATopShot。我们将构建一个非常基本的NFT铸造过程副本,然后在IPFS上备份NFT元数据和资产。这是一个分为三部分的教程:创建一个合约和铸币创建一个应用程序来查看通过这个合约创建的NFT创建一个市场来将NFT转移给他人,同时也在IPFS上转移NFT的基础资产今天让我们开始吧让我们开始吧与第一个教程。配置我们需要安装FlowCLI:苹果系统:brewinstallflow-cliLinux:sh-ci"$(curl-fsSLhttps://storage.googleapis.com/flow-cli/install.sh)"Windows:iex"&{$(irm'https://storage.googleapis.com/flow-cli/install.ps1')}"我们将在IPFS上存储资产文件。为了让事情更简单,我们可以使用Pinata。您可以在此处注册一个免费帐户并在此处获取API密钥。在本教程的第二篇文章中,我们将使用API,但在本文中,我们将使用Pinata网站。我们还需要安装NodeJS和一个文本编辑器来帮助Flow智能合约代码(用Cadence语言编写)的语法高亮显示。您可以在此处安装Node。VisualStudioCode有一个扩展来支持Cadence。安装后,让我们创建一个目录来存放我们的项目:mkdirpinata-party转到该目录并初始化一个新的流程项目:cdpinata-partyflowprojectinit现在,在您喜欢的代码编辑器中打开该项目(同样,如果您使用VisualStudioCode,请抓住Cadence扩展),然后开始工作。您会看到一个flow.json文件,我们很快就会用到它。首先,创建一个名为cadence的文件夹。在该文件夹中,添加另一个名为contracts的文件夹。最后,在名为contracts的文件夹中创建文件PinataPartyContract.cdc。在继续之前,重要的是要指出,从现在开始,我们对Flow区块链所做的一切都将在模拟器上完成。但是,将项目部署到测试网或主网就像更新flow.json文件中的配置设置一样简单。现在让我们为模拟器环境设置这个文件,然后我们就可以开始编写合约了。更新contract对象,flow.json如下:"contracts":{"PinataPartyContract":"./cadence/contracts/PinataPartyContract.cdc"}然后,更新deployments文件中的对象如下:"deployments":{"emulator":{"emulator-account":["PinataPartyContract"]}}这是告诉FlowCLI使用模拟器来部署我们的合约,它还引用了这个账户(在模拟器上),我们将要编写合约。ContractFlow有一个关于创建NFT合约的优秀教程。这是一个很好的参考点,但正如Flow指出的那样,他们还没有解决NFT元数据问题。他们想在链上存储元数据。这是个好主意,他们肯定会想出一个合乎逻辑的方法。但是,我们现在想要创建一些带有元数据的代币,我们想要与NFT关联的媒体文件。元数据只是一个组成部分。我们还需要指明通证最终代表的媒介。如果您熟悉以太坊区块链上的NFT,您可能知道这些代币返回的许多资产存储在传统数据存储和云托管提供商中。没关系,除非不是。我们过去曾写过关于内容可寻址内容的天才,以及在传统云平台上存储区块链相邻数据的缺点。归结为两点:资产应该是可验证的维护责任的转移应该是容易的IPFS兼顾了这两方面。然后以一种简单的方式对Pinata进行分层,以长期保护IPFS上的内容。这正是我们对支持NFT的媒体所需要的,对吧?我们要确保我们能够证明所有权(NFT),提供关于NFT(NFT)的数据,并确保我们能够完全控制基础资产(IPFS)(媒体或其他)拥有控制权,而不是控制某些副本。考虑到所有这些,让我们编写一个创建NFT的合约,将元数据与NFT相关联,并确保元数据指向存储在IPFS上的基础资产。打开PinataPartyContract.cdc让我们开始工作。pubcontractPinataPartyContract{pubresourceNFT{publetid:UInt64init(initID:UInt64){self.id=initID}}}第一步是定义我们的合约。我们将添加更多内容,但首先我们定义PinataPartyContract并在其中创建一个资源。资源是存储在用户帐户中的项目,可以通过访问控制进行访问。在这种情况下,NFT资源最终是通过拥有用来代表NFT的东西来拥有的。NFT必须是唯一可识别的。id属性允许我们识别令牌。接下来,我们需要创建一个资源接口,用于定义其他人(即非合约所有者的人)可以使用哪些功能:pubresourceinterfaceNFTReceiver{pubfundreposit(token:@NFT,metadata:{String:String})pubfungetIDs():[UInt64]pubfunidExists(id:UInt64):BoolpubfungetMetadata(id:UInt64):{String:String}}把这个放在NFT资源代码下面。这个NFTReceiver资源接口意味着我们定义为有权访问该资源的任何人都可以调用以下方法:depositgetIDsidExistsgetMetadata接下来我们需要定义我们的令牌收集接口。把它想象成一个持有所有用户NFT的钱包。pubresourceCollection:NFTReceiver{pubvarownedNFTs:@{UInt64:NFT}pubvarmetadataObjs:{UInt64:{String:String}}init(){self.ownedNFTs<-{}self.metadataObjs={}}pubfunwithdraw(withdrawID:UInt64):@NFT{lettoken<-self.ownedNFTs.remove(key:withdrawID)!return<-token}pubfundeposit(token:@NFT,metadata:{String:String}){self.metadataObjs[token.id]=metadataself.ownedNFTs[token.id]<-!token}pubfunidExists(id:UInt64):Bool{returnsself.ownedNFTs[id]!=nil}pubfungetIDs():[UInt64]{returnsself.ownedNFTs.keys}pubfunupdateMetadata(id:UInt64,metadata:{String:String}){self.metadataObjs[id]=metadata}pubfungetMetadata(id:UInt64):{String:String}{returnself.metadataObjs[id]!}destroy(){destroyself.ownedNFTs}}里面有很多这个资源要做的事情,但应该很快就有意义。首先,我们有一个名为ownedNFTs的变量。这很简单。它跟踪该合约中用户拥有的所有NFT。接下来,我们有一个名为metadataObjs的变量。这有点独特,因为我们正在扩展FlowNFT合约功能以存储每个NFT的元数据映射。此变量将令牌ID映射到其关联的元数据,这意味着我们需要先设置令牌ID,然后才能设置它。然后,我们初始化变量。这对于在Flow的资源中定义的变量是必需的。最后,我们拥有NFT收藏资源的所有可用功能。请注意,并非所有这些功能都可用。如果您还记得的话,我们之前在NFTReceiver资源接口中定义了任何人都可以使用的功能。我确实想指出存款功能。正如我们扩展了默认的FlowNFT合约以包含metadataObjs映射一样,我们也扩展了默认存款功能以获取额外的参数元数据。我们为什么要在这里这样做?我们需要确保只有代币的铸造者才能将该元数据添加到代币中。为了维护隐私,我们将元数据的初始添加限制为铸造执行。我们的合同代码几乎完成了。因此,在Collection资源下方,添加以下内容:(initID:self.idCount)self.idCount=self.idCount+1asUInt64return<-newNFT}}首先,我们有一个函数,当被调用时,它会创建一个空的NFT集合。这样,第一次与我们的合约交互的用户将拥有一个为我们定义的资源集合创建的存储位置。之后,我们创建另一个资源。这很重要,因为没有它我们将无法铸造代币。NFTMinter资源包括一个idCount,它会递增以确保我们的NFT上永远不会有重复的id。它还具有实际创建我们的NFT的功能。在NFTMinter资源下面,添加主合约初始化程序:init(){self.account.save(<-self.createEmptyCollection(),to:/storage/NFTCollection)self.account.link<&{NFTReceiver}>(/public/NFTReceiver,target:/storage/NFTMinter)self.account.save(<-createNFTMinter(),to:/storage/NFTMinter)}这个初始化函数只会在部署合约时调用。它做了三件事:为集合的部署者创建一个空集合,以便合约的所有者可以创建该合约的NFT并拥有该NFT。Collection引用了我们一开始创建的NFTReceiver接口,在公共位置发布。这就是我们告诉合约任何人都可以调用NFTReceiver上定义的函数的方式。NFTMinter资源保存在账户存储合约创建者中。这意味着只有合约的创建者才能铸造代币。完整的合同可以在这里找到。现在我们已经准备好合约,让我们部署它,好吗?好吧,我们或许应该在FlowPlayground上对其进行测试。去那里并单击左侧栏中的第一个帐户。将示例合约中的所有代码替换为我们的合约代码,然后单击部署。如果一切顺利,您应该会在屏幕底部的日志窗口中看到如下日志:flowprojectstart-emulator我们现在已准备好将合约部署到本地运行的模拟器。从命令行运行以下命令:flowprojectdeploy现在,在模拟器运行且flow.json文件配置正确的情况下,我们可以部署合约。只需运行以下命令:flowprojectdeploy如果一切顺利,您应该会看到类似于以下内容的输出:Deploying1contractsforaccounts:emulator-accountPinataPartyContract->0xf8d6e0586b0a20c7现在,我们在Flow模拟器上有一个合约,但我们想要铸造一个令牌。铸造NFT在本教程的第二篇文章中,我们将致力于通过应用程序和用户界面使铸造过程更加用户友好。为了说明和展示元数据如何与Flow上的NFT一起工作,我们将使用Cadence脚本和命令行。让我们在pinata-party项目的根目录下创建一个新目录并将其命名为transactions。创建该文件夹后,在其中创建一个名为MintPinataParty.cdc的新文件。为了写入交易,我们需要在提供给NFT的元数据中引用一个文件。为此,我们将通过Pinata将文件上传到IPFS,您可以上传任何您想要的视频文件。您几乎可以上传任何资产文件并将其与NFT相关联。上传文件后,您将获得一个IPFS哈希(通常称为内容标识符或CID)。复制此哈希值,因为我们将在铸造过程中使用它。现在,在MintPinataParty.cdc文件内添加以下内容:importPinataPartyContractfrom0xf8d6e0586b0a20c7transaction{letreceiverRef:&{PinataPartyContract.NFTReceiver}letminterRef:&PinataPartyContract.NFTMinterprepare(acct:AuthAccount){self.receiverRef=acct.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver).borrow()??panic("Couldnotborrowreceiverreference")self.minterRef=acct.borrow<&PinataPartyContract.NFTMinter>(from:/storage/NFTMinter)??panic("couldnotborrowminterreference")}执行{letmetadata:{字符串:字符串}={“名称”:“TheBigSwing”,“swing_velocity”:“29”,“swing_angle”:“45”,“rating”:“5”,“uri”:“ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6”}letnewNFT<-self.minterRef.mintNFT()self.receiverRef.deposit(token:<-newNFT,metadata:metadata)log("NFTMintedanddepositedtoAccount2'sCollection")}}这是一件非常简单的事情,这在很大程度上要归功于Flow为使事情变得简单所做的工作,但让我们先过一遍它,您会注意到顶部的import语句。如果您还记得,当我们部署合约时,我们会收到一个帐户。这就是我们需要参考的。因此,将0xf8d6e0586b0a20c7替换为您部署中的帐户地址。接下来,我们定义事务。这里发生的一切都与我们计划执行的交易有关。我们在交易中做的第一件事是定义两个引用变量receiverRef和minterRef。在这种情况下,我们既是NFT的接收者,也是NFT的铸币者。这两个变量指的是我们在合约中创建的资源。如果执行交易的人无权访问资源,则交易将失败。接下来,我们有一个准备功能。此函数获取尝试执行交易的人的帐户信息并进行一些验证。我们尝试“借用”我们定义的NFTMinter和两个资源上可用的函数NFTReceiver。如果执行交易的人无权访问这些资源,那么事情就会失败。最后,我们有我们的执行功能。这个功能是我们为NFT建立元数据,创建NFT,然后在将NFT存入我们的帐户之前关联元数据的地方。如果您注意到了,我创建了一个元数据变量。在那个变量中,我添加了一些关于令牌的信息。由于我们的令牌代表在派对上砸披萨的事件,并且由于我们试图复制您在NBATopShot中看到的大部分内容,因此我在元数据中定义了一些统计数据。孩子挥动棍子击打皮纳塔的速度、挥动角度和等级。我只是在玩弄这些统计数据。但是,您将以类似的方式输入对您的令牌有意义的任何信息。您会注意到我还在uri元数据中定义了一个属性。这将指向托管与NFT关联的资产文件的IPFS哈希。在这种情况下,它是Pi?ata被击中的实际视频。您可以将哈希值替换为之前上传文件后收到的哈希值。出于多种原因,我们在散列前加上ipfs://。这是IPFS上文件的正确参考,可以与IPFS的桌面客户端和浏览器扩展一起使用。现在我们已经原生支持IPFS内容,我们也可以将其直接粘贴到Brave浏览器中。我们调用mintNFT函数来创建令牌。然后我们必须调用存款功能将其存入我们的帐户。这也是我们传递元数据的地方。请记住,我们在存款函数中定义了一个变量关联,它将元数据添加到关联的代币ID中。最后,我们简单地注销代币被铸造和存放的事实。现在,我们几乎准备好发送交易并创建NFT。但首先,我们需要准备好我们的账户。从项目根文件夹中的命令行,让我们创建一个新的签名私钥。运行以下命令:flowkeysgenerate这将为您提供公钥和私钥。**始终保护您的私钥**我们需要私钥来签署交易,因此我们可以将其粘贴到我们的flow.json文件中。我们还需要指定签名算法。这是文件flow.json中的帐户对象,现在应该如下所示:flow-emulator","sigAlgorithm":"ECDSA_P256","hashAlgorithm":"SHA3_256"}},如果您打算将此项目的任何内容存储在github或任何远程git存储库中,请确保不要包含私钥。您可能想要.gitignore所有flow.json。即使我们只使用本地模拟器,保护您的密钥也是一个好习惯。现在我们已经更新了,我们可以发送交易了。这样做就像运行以下命令一样简单:flowtransactionssend--code./transactions/MintPinataParty.cdc--signeremulator-account我们从中引用我们的书面交易文件和签名者帐户flow.json。如果一切顺利,您应该会看到类似以下的输出:Gettinginformationforaccountwithaddress0xf8d6e0586b0a20c7...SubmittingtransactionwithID4a79102747a450f65b6aab06a77161af196c3f7151b2400b3b3d09ade3b69823...SuccessfullysubmittedtransactionwithID4a79102747a450f65b6aab06a77161af196c3f7151b2400b3b3d09ade3b69823现在,我们要做的最后一件事是验证Whetherthetokenisinouraccountandgetthemetadata.Todoso,we'llwriteaverysimplescriptandcallitfromthecommandline.Intherootdirectoryofyourproject,createanewfoldercalledscripts.CreateafilecalledCheckTokenMetadata.cdcinsideit.Inthisfile,addthefollowing:importPinataPartyContractfrom0xf8d6e0586b0a20c7pubfunmain():{String:String}{letnftOwner=getAccount(0xf8d6e0586b0a20c7)//log("NFTOwner")letcapability=nftOwner.getCapability<&{PinataPartyContract}/NFTRecNFTReceiver)letreceiverRef=capability.borrow()??panic("Couldnotborrowthereceiverreference")returnreceiverRef.getMetadata(id:1)}Thisscriptcanbeconsideredinasimilarwaytoaread-onlymethodonanEthereumsmartcontract.Theyarefree,justreturnthedatafromthecontract.Inourscript,weareimportingourcontractfromthedeployedaddress.Then,wedefineamainfunction(thisisthenameofthefunctionneededtorunthescript).Insidethisfunction,wedefinethreevariables:nftOwner:ThisissimplytheaccountthatownstheNFT.我们从一个也部署了合约的账户中铸造了NFT,因此在我们的示例中,这两个地址是相同的。根据未来的合约设计,这可能并不总是正确的。功能:我们需要从已部署的合约中“借用”可用功能(或多个功能)。请记住,这些函数是受访问控制的,因此如果某个函数对于试图借用它的地址不可用,脚本将失败。我们正在从NFTReceiver资源中借用功能。receiverRef:这个变量只是利用我们的能力,告诉脚本从已部署的合约中借用。现在,我们可以调用函数(可用函数)。在这种情况下,我们要确保相关地址确实收到了我们铸造的NFT,然后我们要查看与令牌关联的元数据。让我们运行我们的脚本,看看我们得到了什么。在命令行上运行以下命令:flowscriptsexecute./scripts/CheckTokenMetadata.cdc对于元数据输出,您应该看到类似于以下内容的输出:{"name":"TheBigSwing","swing_velocity":"29","swing_angle":"45","rating":"5","uri":"ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"}恭喜!您已经成功创建了一个Flow智能合约,铸造了一个代币和与该代币关联的元数据,并将该代币的底层数字资产存储在IPFS上。第二部分教程的下一部分将构建一个简单的React应用程序,该应用程序与Flow智能合约接口以验证并获取用户拥有的NFT,敬请期待。