NFT平台开发搭建系统基础文档

随着不可替代的代币市场数量庞大,回顾 NFT 的最初日子并记住 Cryptokitties 所揭示的挑战是件好事。

 ​​​​​​​

Cryptokitties 是 Dapper Labs 团队构建的第一个潜在 NFT 大规模采用的例子。从那时起,引入了新的 NFT 市场平台,如 Rarible、Sorare 和 OpenSea,以鼓励 NFT 的发展。NonFungible.com 上发表的研究表明,2020 年 NFT 交易总额与 2019 年相比增长了 299%,超过了 2.5 亿美元。专家预测,未来 10 年,不可替代的代币将成为虚拟经济背后的驱动力。

NFT 市场预计将大幅增长,而开发 NFT 市场是一个很好的机会。因此,我们整理了一份完整的指南,将回答有关构建 NFT 市场的问题。

什么是 NFT 市场?

NFT 市场将在 2021 年继续占据主导地位,并且 NFT 正在成长为加密领域的一种新资产类别。NFT 市场平台是一个去中心化的平台,允许用户创建、购买、出售和存储不可替代的代币。

NFT 市场专注于销售特定资产。例如,Valuables NFT 市场允许用户买卖推文。

利基市场获得了很大的吸引力,因为它们具有明确定义的目标受众。因此,在构建 NFT 市场之前,您应该考虑将要启动的平台类型。

NFT 市场如何运作?

在深入研究 NFT 市场的创建方式之前,有必要从客户端了解 NFT 市场的运作方式。通常,所有 NFT 平台共享相似的工作流程。首先,用户必须在平台上注册并安装一个数字钱包来存储 NFT。

然后,用户可以通过上传项目来展示他们的作品来创建他们的资产。用户还可以选择他们愿意为他们的艺术接受哪些支付代币,并在平台建议时设置费用。工作流程的下一步是列出待售商品。用户可以选择固定价格或拍卖的出价。列出待售商品时会创建交易,以启动用户钱包的个人交易智能合约。

平台可能需要收集审核,批准后 NFT 将出现在列表中。

如何开始 NFT 市场开发?

由于您知道 NFT 市场的运作方式以及市场上有哪些类型的平台,您需要了解您正在为谁开发平台以及它可以解决哪些问题。

您需要确定功能列表,并为您的市场选择技术堆栈和 NFT 标准。

以下是一些可以添加到 NFT 市场的功能:

  • 店面
  • NFT 市场应该有一个店面,为用户提供物品所需的所有信息:出价、所有者、预览或价格历史。
  • 过滤器
  • 使用过滤器,可以更轻松地浏览站点,特别是如果您计划为大量收藏品建立一个市场场所。通过添加过滤器功能,用户可以通过付款方式、列表状态、类别和集合来选择项目。
  • 搜索物品
  • NFT 市场平台应支持标记和类别管理,以允许用户搜索收藏品。使用网站上的搜索栏并添加类别。
  • 创建列表
  • 用户应该能够创建和提交收藏品。使用此功能,用户应上传文件并填写名称、标签、描述等令牌信息。
  • 购买和投标
  • NFT 市场平台应该有一个功能,允许用户购买和投标平台上列出的 NFT。投标功能应包括投标截止日期,并允许用户查看有关投标当前状态的详细信息。
  • 钱包
  • NFT 市场平台应该有一个钱包,允许用户存储、发送和接收不可替代的代币。集成此功能的最简单方法是为用户提供他们已经使用的连接钱包。例如,您可以集成最流行的钱包,如 Coinbase、Formic 或 MyEtherWallet。

一旦确定了要添加到平台的功能,下一步就是考虑项目的技术实施。要构建 NFT 市场,您应该首先确定开发它所需的技术堆栈。

让我们看看构建 NFT 市场平台所需的一些技术组件。

构建 NFT 市场平台所需的技术堆栈

区块链平台

  • 在 NFT 市场或不可替代的代币开发方面,以太坊Ethereum 是最受欢迎的平台之一。由于交易历史和代币元数据在以太坊上可公开验证,因此更容易证明所有权历史。由于所有以太坊产品共享公共后端,NFT 变得可跨产品移植。此外,以太坊永远不会下降;因此,代币将始终可供出售。
  • Flow
  • Flow 是一个快速、去中心化的区块链,专为新一代数字资产、应用程序和游戏而设计。该平台被广泛用于使用 Cadence 编程语言创建 NFT 市场和 NFT。
  • Tezos
  • Tezos 是一个开源区块链平台,适用于由构建者、研究人员和验证者社区支持的应用程序和资产。Tezos 使用用 LIGO 智能合约语言编写的预编译 FA2 NFT 合约,支持 NFT 市场和 NFT 的开发。
  • Cardano
  • Cardano 是一个区块链平台,具有为分散系统和应用程序提供无与伦比的安全性和可持续性所需的技术和工具。它还支持 NFT 和其他数字资产的开发。 

存储平台

  • IPFS
  • IPFS 是一种点对点超媒体协议,旨在以分散的方式存储媒体内容。由于与 NFT 相关的媒体文件不能直接存储在区块链上,IPFS 可以存储所有这些数据。
  • Filecoin
  • Filecoin 是一个去中心化的存储网络,专门设计用于存储最关键的信息,例如媒体文件。存储平台包括开发人员开始使用 NFT 的去中心化存储所需的一切。了解其好处后,Truffle Suite 还推出了带有 Filecoin Box 的 NFT 开发模板。
  • Pinata
  • Pinata 也是在 IPFS 上上传和管理文件的流行平台之一。它为 NFT 提供安全且可验证的文件。

NFT标准

  • ERC-721
  • ERC-1155
  • FA2
  • 商品
  • TRC-721

前端框架

  • 反应
  • 视图

本文演示了使用 Flow Blockchain 和 Pinata IPFS 网络构建 NFT 市场的示例。

如何使用 IPFS 和 Flow 开发 NFT Marketplace?

我们分享了创建合约、铸造代币、构建应用程序以查看通过该合约生成的 NFT 以及创建将 NFT 转移给其他用户的市场的示例。

让我们从合约的创建和代币铸造开始。

设置工具 

在您的系统上安装 Flow CLI。根据不同的操作系统,安装 CLI 有不同的命令。

例如,要在 macOS 上安装 Flow CLI,请使用以下命令:

brew install flow-cli < font >< /font >

<字体>< /字体>

在 Windows 上:

iex “& { $ ( irm 'https://storage.googleapis.com/flow-cli/install.ps1 ') }”<font></font>

<字体>< /字体>

在 Linux 上:

sh -ci “$ ( curl -fsSL https://storage.googleapis.com/flow-cli/install.sh )”<font></font>

<字体>< /字体>

资产文件将存储在 IPFS 上。在这个例子中,我们将使用 Pinata 来存储文件。您可以在此处注册一个免费帐户并获取 API 密钥。

安装 NodeJS 和文本编辑器来突出 Flow 智能合约代码也很重要。

第二步是使用以下命令为项目创建一个目录:

mkdir pinata-party < font >< /font >

<字体>< /字体>

初始化一个新的流项目并将其更改为该目录:

cd pinata-party < font >< /font >

<字体>< /字体>

现在,在您的代码编辑器中打开项目,让我们开始工作。首先,创建一个名为cadence的文件夹。在该文件夹中添加另一个名为 合同的 文件夹。最后,在合同文件夹中创建一个名为 PinataPartyContract.cdc 的文件。

在继续之前,必须指出我们对 Flow 区块链平台所做的一切。为模拟器环境设置文件,然后我们就可以开始编写合约了。

我们需要使用以下代码更新 flow.json 中的合约对象:

“合同”:{<字体>< /字体>

“PinataPartyContract”:“./cadence/contracts/PinataPartyContract. cdc <字体>< /font >

}<字体>< /字体>

使用以下代码更新该文件中的部署对象:

“部署”:{<字体>< /字体>

“模拟器”:{<字体>< /font >

“模拟器账户”:[ “PinataPartyContract” ]< font >< /font >

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

它将使 Flow CLI 能够使用模拟器来部署我们的合约。此代码还引用了我们即将编写的帐户和合约。

合同

我们需要创建用于铸造 NFT 的合约,将元数据与 NFT 相关联,并确保元数据指向存储在 IPFS 上的基础资产。

打开 PinataPartyContract.cdc 并执行以下代码:

pub 合约 PinataPartyContract {< font >< /font >

pub 资源 NFT {<字体>< /font >

pub let id: UInt64 < font >< /font >

init ( initID: UInt64 ) {<字体>< /字体> 

自己。id = initID <字体>< /font >

}<字体>< /字体>

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

第一步是定义合同。让我们首先定义 PinataPartyContract 并在其中创建一个资源。资源是保存在可通过访问控制措施访问的用户帐户中的项目。NFT 需要可识别,并且 id 属性允许您识别令牌。

然后,创建一个资源接口来定义其他人可以使用哪些功能。

pub 资源接口 NFTReceiver {< font >< /font >

pub fun存款( token:@NFT, metadata: { String : String })< font >< /font >

pub fun getIDs () : [ UInt64 ]< font >< /font >

pub fun idExists ( id: UInt64 ) : Bool < font >< /font >

pub fun getMetadata ( id: UInt64 ) : { String : String }< font >< /font > 

}<字体>< /字体>

<字体>< /字体>

将上面的代码放在 NFT 资源代码下面。NFTReceiver 资源接口表示该资源可以调用以下方法:

  • 获取ID
  • 身份存在
  • 订金
  • 获取元数据

然后,我们必须定义令牌收集接口。将其视为存储所有用户 NFT 的钱包。

pub资源集合:NFTReceiver {<字体>< /font >

pub var ownNFTs: @ { UInt64: NFT }< font >< /font >

pub var metadataObjs: { UInt64: { String : String }}< font >< /font >

init () {<字体>< /字体> 

自己。ownNFTs < - {}< font >< /font > 

自己。metadataObjs = {}< font >< /font >

}<字体>< /字体>

pub fun撤回withdrawID: UInt64 :@NFT {<字体>< /font >

让令牌< - self. 拥有的 NFT 删除关键:withdrawID <字体>< /字体>

return < -token < font >< /font > 

}<字体>< /字体>

pub fun存款令牌:@NFT,元数据:{ 字符串字符串}){<字体>< /字体> 

自己。metadataObjs [令牌。id ] = 元数据<字体>< /font >

自己。拥有的 NFTs [ 代币。id ] < -!令牌<字体>< /font > 

}<字体>< /字体>

pub fun idExists ( id: UInt64 ) : Bool {< font >< /font >

回归自我。ownNFTs [ id ] != nil < font >< /font >

}<字体>< /字体>

pub fun getIDs () : [ UInt64 ] {< font >< /font > 

回归自我。拥有的 NFT <字体>< /font >

}<字体>< /字体>

pub fun updateMetadata ( id: UInt64, metadata: { String: String }) {< font >< /font > 

自己。metadataObjs [ id ] = metadata < font >< /font >

}<字体>< /字体>

pub fun getMetadata ( id: UInt64 ) : { String : String } {< font >< /font > 

回归自我。元数据对象[ id ] <字体>< /字体>

}<字体>< /字体>

销毁() {<字体>< /字体> 

毁灭自我。拥有的NFTs < font >< /font >

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

变量 ownedNFTs 跟踪用户可以从联系器拥有的所有 NFT。一个名为 metadataObjs 的变量是独一无二的,因为我们正在扩展 Flow NFT 合约功能以存储每个 NFT 的元数据映射。

它将令牌 ID 映射到其相关元数据,这意味着在我们设置它之前需要令牌 ID。变量被初始化以在 Flow 的资源中定义它们。

最后,我们将拥有 NFT 集合资源所需的所有可用功能。默认 NFT 合约扩展为包含 metadataObjs 映射的方式,我们将扩展默认存款功能以获取额外的元数据参数。这样做是为了确保只有令牌铸造者才能将元数据添加到令牌中。我们将元数据的初始添加限制在铸造执行中以保持其私密性。在 Collection 资源下方添加以下代码:

pub fun createEmptyCollection () : @Collection {< font >< /font >

return < - 创建集合()<字体>< /font > 

}<字体>< /字体>

发布资源 NFTMinter {<字体>< /字体>

pub var idCount: UInt64 < font >< /font >

init () {<字体>< /字体> 

自己。idCount = 1 <字体>< /font >

}<字体>< /字体>

pub fun mintNFT () : @NFT {<字体>< /font >

var newNFT < - 创建NFT ( initID: self.idCount )< font >< /font >

自己。idCount = 自己。idCount + 1 as UInt64 < font >< /font >

return < -newNFT < font >< /font > 

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

首先,我们将有一个函数在调用时创建一个空的 NFT 集合。与合约交互的用户将拥有映射定义的集合资源的存储位置。

之后我们将再创建一个资源。没有它,我们就无法铸造代币。NFTMinter 包含一个 idCount,它每次都会增加,以确保我们没有 NFT 的重复 ID。它还包含一个用于创建 NFT 的函数。

在 NFTMinter 资源下方添加主合约初始化器:

init () {<字体>< /字体> 

自己。帐户保存(< -self。createEmptyCollection () ,以便:/存储/ NFTCollection )<字体> < / FONT >

自己。帐户链接< & { NFTReceiver }>( /public/NFTReceiver,target:/storage/NFTCollection )< font >< /font >

自己。帐户保存(< -create NFTMinter () ,以便:/存储/ NFTMinter )<字体> < / FONT >

}<字体>< /字体>

初始化函数仅在部署合约时调用。它执行三件事:

  • 为集合部署者创建一个空集合,以便所有者合约可以从合约创建和拥有 NFT。
  • NFTMinter 资源存储在合约创建者的账户存储中。这意味着只有合约创建者才能铸造代币。
  • 集合资源参考开头创建的 NFTReceiver 接口发布在公共位置。这就是我们告诉合约任何人都可以调用 NFTReceiver 上定义的函数的方式。

一旦我们准备好了合约,让我们部署它。在部署之前:

  1. 在 Flow Playground 上进行测试。
  2. 转到 Flow Playground 并单击左侧边栏中的第一个帐户。
  3. 将所有代码替换为合约代码,然后单击部署。

如果一切顺利,您应该在屏幕底部查看日志:

16:48:55 部署 部署合同到:0x01

现在是将合约部署到本地运行的模拟器的时候了,运行以下命令:

flow project start-emulator < font >< /font >

<字体>< /字体>

模拟器运行并配置 flow.json 文件后,可以使用以下命令部署合约:

流项目部署< font >< /font >

<字体>< /字体>

如果一切顺利,您应该查看如下输出:

账户部署1个合约:emulator-account < font >< /font >

<字体>< /字体>

PinataPartyContract → 0xf8d6e0586b0a20c7 <字体>< /font >

<字体>< /字体>

现在,我们将转向 NFT 的铸造。

铸造 NFT

 

在本节中,我们将讨论使用应用程序和用户界面的 NFT 铸造过程。为了得到一些东西并显示元数据如何在 Flow 上与 NFT 一起工作,我们将使用命令行和 Cadence 脚本。

在我们的 pinata-party 项目的根目录中创建一个新目录,并将它们称为“交易”。创建文件夹后,在其中创建一个名为 MintPinataParty.cdc 的新文件。

我们应该有一个文件可以在我们提供给 NFT 的元数据中引用。文件通过 Pinata 上传到 IPFS。在本教程中,NFT 专注于 Pinata 在派对上被砸的可交易视频。在这个演示中,我们将上传一个孩子在生日聚会上击打皮纳塔的视频。您可以上传您想要的任何媒体文件并将其与 NFT 关联。

文件上传后,您将获得一个 IPFS 哈希。复制哈希值,因为它将在铸造过程中使用。现在,在 MintPinataParty.cdc 文件中添加以下代码。

0xf8d6e0586b0a20c7导入 PinataPartyContract < font >< /font >

交易{<字体>< /字体>

让receiverRef: & { PinataPartyContract. NFTReceiver }< font >< /font >

让 minterRef: &PinataPartyContract. NFTMinter <字体>< /font >

准备帐户:AuthAccount ){<字体>< /字体> 

自己。接收者Ref =acct。getCapability < & { PinataPartyContract. NFTReceiver }>( /public/NFTReceiver )< font >< /font >

. 借用()<字体>< /字体>

?? 恐慌“无法借用接收者参考” )<字体>< /字体>

自己。minterRef = 帐户。< &PinataPartyContract. NFTMinter >(来自:/storage/NFTMinter )<字体>< /font >

?? 恐慌“无法借用铸币厂参考” )<字体>< /字体>

}<字体>< /字体>

执行{<字体>< /字体>

让元数据:{ 字符串字符串} = {<字体>< /字体> 

“名称”:“大摇摆”,< font >< /font >

“swing_velocity”:“ 29 ”,<字体>< /font >

“swing_angle”:“ 45 ”,<字体>< /font >

“评级”:“ 5 ”,<字体>< /字体>

“uri”:“ipfs ://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6”<font></font>

}<字体>< /字体>

让 newNFT < - self. minterRef mintNFT ()<字体>< /font >

自己。接收者参考存款令牌:< -newNFT,元数据:元数据)<字体>< /font >

日志“NFT 铸造并存入账户2的收藏” )< font >< /font >

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

首先,我们定义了两个引用变量,minterRef 和receiverRef。在这种情况下,我们既是 NFT 的接收者也是铸币者。这些变量引用在合约中创建的资源。如果执行它的人无权访问资源,则事务将失败。

上述合约将铸造和存入 NFT。现在,我们将发送交易并创建 NFT。但在此之前,我们需要准备帐户。在项目的根文件夹中创建一个用于从命令行签名的私钥。

运行以下命令:

流键生成< font >< /font >

<字体>< /字体>

它将为您提供公钥和私钥。确保保护您的私钥。

您将需要用于签署交易的私钥,该私钥需要粘贴到我们的 flow.json 文件中。指定签名算法也很重要,这里是 flow.json 文件中的帐户对象应如下所示:

“账户”:{<字体>< /字体>

“模拟器账户”:{<字体>< /字体>

“地址”:“您的帐户地址”,< font >< /font >

“privateKey”:“你的私钥”,< font >< /font >

“chain”: “flow-emulator”, < font >< /font >

“sigAlgorithm”: “ECDSA_P256”, < font >< /font >

“hashAlgorithm”: “SHA3_256” < font >< /font >

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

如果您想将此项目的任何内容存储在远程 git 存储库或 Github 上,则不应包含私钥。您可能需要 .gitignore 整个 flow.json。虽然我们只使用本地模拟器,但最好保护密钥。

我们需要做的最后一件事是验证令牌是否在我们的帐户中并获取元数据。为了验证它,我们需要编写一个简单的脚本并从命令行调用它。

从项目的根目录创建一个名为 scripts 的新文件夹。在文件夹内创建一个名为 CheckTokenMetadata.cdc 的文件。在该文件中添加以下代码:

0xf8d6e0586b0a20c7导入 PinataPartyContract < font >< /font >

pub fun main () : { String : String } {<字体>< /font > 

let nftOwner = getAccount ( 0xf8d6e0586b0a20c7 ) // log(“NFT Owner”)<font></font>

让能力 = nftOwner。getCapability < & { PinataPartyContract. NFTReceiver }>( /public/NFTReceiver )< font >< /font >

让receiverRef = 能力。借用()<字体>< /字体>

?? 恐慌“无法借用接收者参考” )<字体>< /字体>

返回<字体>< /字体>

接收者参考 getMetadata ( id: 1 )<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

我们正在此脚本中从部署的地址导入合约。我们定义了 main 函数,并在其中定义了三个变量:

  • nftOwner
  • 这个账户拥有 NFT。
  • 能力
  • 能力是访问控制的。如果尝试借用它的地址无法使用功能,则脚本将失败。在此示例中,我们从 NFTReceiver 资源借用功能。
  • receiverRef
  • 变量获取我们的能力并声明要从部署的合约中借用的脚本。

我们希望确保问题中的地址收到了我们铸造的 NFT,然后我们希望查看与令牌关联的元数据。

使用以下命令运行脚本,看看我们得到了什么:

流脚本执行 ./scripts/CheckTokenMetadata。cdc <字体>< /font >

<字体>< /字体>

你会得到这样的输出:

{ “名称” “大摆动” “swing_velocity” “29” “swing_angle” “45” ,等级” “5” “URI” “IPF问题:// QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6” } <字体> < /字体>

<字体>< /字体>

然后我们将构建一个前端 React 应用程序,允许您通过获取元数据来显示 NFT。

展示 NFT 收藏品

我们将构建一个简单的 React 应用程序,该应用程序与 Flow 智能合约交互以验证和获取用户拥有的 NFT。 

设置 React 和依赖项

在父目录 pinata-party 中创建 React 应用程序。运行以下命令来创建一个 React 应用程序:

npx create-react-app pinata-party-frontend < font >< /font >

<字体>< /字体>

安装完成后,您将看到一个名为 pinata-party-frontend 的新目录。切换到该目录并安装依赖项。对于前端设置的第一部分,运行:

npm i @onflow/fcl @onflow/types < font >< /font >

<字体>< /字体>

我们将一些值存储为我们的应用程序的全局变量并使用环境变量。在 React 中,这意味着创建一个 .env 文件并在需要使用 REACT_APP 前缀的位置设置键值对。

然后,创建将用于与 Flow JS SDK 交互的配置文件。在 src 目录下创建一个文件 config.js 并添加以下代码:

从“@onflow/fcl”导入{ 配置} <字体>< /font >

config ()<字体>< /font >

. “接入节点。API ”,过程。ENV REACT_APP_ACCESS_NODE )<字体> < / FONT >

. “挑战。握手”,过程。ENV REACT_APP_WALLET_DISCOVERY )<字体> < / FONT >

. “0xProfile”,过程。ENV REACT_APP_CONTRACT_PROFILE )<字体> < / FONT >

<字体>< /字体>

这个配置文件只是帮助 JS SDK 与 Flow 区块链(或模拟器在这种情况下)一起工作。要使该文件在整个应用程序中可用,请打开该 index.js 文件并添加以下行:

导入“./config” <字体>< /font >

<字体>< /字体>

在应用程序中具有身份验证功能以实现 NFT 资产的安全传输非常重要。我们需要一个身份验证组件。在 src 目录中创建一个文件 AuthCluster.js。在该文件中添加以下内容:

import React, {useState, useEffect} from 'react' < font > </ font >

<字体> </字体>

import * as fcl from “@onflow/fcl” < font > </ font >

<字体> </字体>

const AuthCluster = () => { < font > </ font >

<字体> </字体>

const [user, setUser] = useState({loggedIn: null}) < font > </ font >

<字体> </字体>

useEffect(() => fcl.currentUser().subscribe(setUser), []) < font > </ font >

<字体> </字体>

if (user.loggedIn) { < font > </ font >

<字体> </字体>

返回( <字体> </字体>

<字体> </字体>

< div > < font > </ font >

<字体> </字体>

< span > {user?.addr ?? “无地址”} </ span > < font > </ font >

<字体> </字体>

< button classname = ""btn-primary"" onclick = "{fcl.unauthenticate}" >注销</按钮> <字体> </字体> 

<字体> </字体>

</ div > < font > </ font >

<字体> </字体>

) <字体> </字体>

<字体> </字体>

} else { <字体> </字体>

<字体> </字体>

返回( <字体> </字体>

<字体> </字体>

< div > < font > </ font >

<字体> </字体>

<按钮类名= “”btn-primary”onClick={fcl.logIn}” >登录</按钮> <字体> </字体> 

<字体> </字体>

< button classname = "”btn-secondary”" onclick = "{fcl.signUp}" >注册</ button > < font > </ font > 

<字体> </字体>

</ div > < font > </ font >

<字体> </字体>

) <字体> </字体>

<字体> </字体>

} <字体> </字体>

<字体> </字体>

} <字体> </字体>

<字体> </字体>

导出默认的AuthCluster < font > </ font >

要将此组件添加到应用程序中,请将 app.js 文件替换为以下内容:

导入'./App.css'; <字体> </字体>

<字体> </字体>

import AuthCluster from './AuthCluster';function App() { < font > </ font >

<字体> </字体>

返回( <字体> </字体>

<字体> </字体>

< div classname = "”App”" > < font > </ font > 

<字体> </字体>

< authcluster > < font > </ font >

<字体> </字体>

</ authcluster > </ div > < font > </ font >

<字体> </字体>

); <字体> </字体>

<字体> </字体>

}导出默认应用程序;<字体> </字体>

添加上述代码后,您将在启动应用程序时看到一个带有登录注册按钮的页面。现在,是时候构建为帐户获取 NFT 并显示它们的能力了。

从 Flow 中获取 NFT

要显示我们创建的 NFT,必须与 Flow 区块链通信。在本教程中,我们应该能够与 Flow 模拟器进行通信。

让我们构建一个允许获取数据和显示 NFT 数据的组件。在 src 目录中创建一个文件 TokenData.js 并在该文件中添加以下代码:

import React, { useState } from “react”; <字体> </字体>

<字体> </字体>

从“@onflow/fcl”导入 * 作为 fcl;<字体> </字体>

<字体> </字体>

const TokenData = () => { < font > </ font >

<字体> </字体>

const [nftInfo, setNftInfo] = useState(null) < font > </ font >

<字体> </字体>

const fetchTokenData = async() => { < font > </ font >

<字体> </字体>

const 编码 = await fcl < font > </ font >

<字体> </字体>

.send([ <字体> </字体>

<字体> </字体>

fcl.script` < font > </ font >

<字体> </字体>

从 0xf8d6e0586b0a20c7 导入 PinataPartyContract < font > </ font >

<字体> </字体>

pub fun main() : {String : String} { < font > </ font >

<字体> </字体>

let nftOwner = getAccount(0xf8d6e0586b0a20c7) < font > </ font >

<字体> </字体>

letcapability = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver) < font > </ font >

<字体> </字体>

让receiverRef=capability.borrow() < font > </ font >

<字体> </字体>

?? 恐慌(“无法借用接收者参考”)< font > </ font >

<字体> </字体>

返回receiverRef.getMetadata(id:1) < font > </ font >

<字体> </字体>

} <字体> </字体>

<字体> </字体>

` <字体> </字体>

<字体> </字体>

]) <字体> </字体>

<字体> </字体>

constdecoded = await fcl.decode(encoded) < font > </ font >

<字体> </字体>

setNftInfo(decoded) < font > </ font >

<字体> </字体>

}; <字体> </字体>

<字体> </字体>

返回( <字体> </字体>

<字体> </字体>

< div classname = "”token-data”" > < font > </ font > 

<字体> </字体>

< div classname = ""center"" > < font > </ font > 

<字体> </字体>

< button classname = "”btn-primary”" onclick = "{fetchTokenData}" >获取Token数据</ button > < font > </ font > 

<字体> </字体>

</ div > < font > </ font >

<字体> </字体>

{ <字体> </字体>

<字体> </字体>

nftInfo && < font > </ font >

<字体> </字体>

< div > < font > </ font >

<字体> </字体>

{ <字体> </字体>

<字体> </字体>

Object.keys(nftInfo).map(k => { < font > </ font >

<字体> </字体>

返回( <字体> </字体>

<字体> </字体>

< p > {k}:{nftInfo[k]} </ p > < font > </ font >

<字体> </字体>

) <字体> </字体>

<字体> </字体>

}) <字体> </字体>

<字体> </字体>

} <字体> </字体>

<字体> </字体>

< button onclick = "{()=" > setNftInfo(null)} className=”btn-secondary”>清除Token信息</ button > < font > </ font > 

<字体> </字体>

</ div > < font > </ font >

<字体> </字体>

} <字体> </字体>

<字体> </字体>

</ div > < font > </ font >

<字体> </字体>

); <字体> </字体>

<字体> </字体>

}; <字体> </字体>

<字体> </字体>

导出默认的 TokenData;<字体> </字体>

在这个文件中,我们正在创建一个带有按钮的组件来获取令牌数据。我们还创建了一个按钮来清除令牌数据。单击 fetch 按钮时,将调用函数 fetchTokenData。该函数使用 Flow JS SDK 来运行我们从命令行执行的脚本。获取执行结果并将结果设置到状态变量 nftInfo。如果该变量存在,键值对将从屏幕上的 NFT 元数据和一个清除数据的按钮呈现。

从 IPFS 获取媒体

由于我们已经注册了 Pinata 帐户并通过 Pinata 上传界面将视频文件添加到 IPFS,因此当您单击 Pin Explorer 中的哈希时,您将导航到 Pinata IPFS 网关,其中显示 IPFS 内容。

在 TokenData.js 文件中,添加一种方式来显示从 IPFS 检索到的视频文件。将文件更新为如下所示:

import React, { useState } from “react”; <字体> < /字体>

<字体></字体>

从“@onflow/fcl”导入*作为fcl;<字体> < /字体>

<字体></字体>

const TokenData = ( ) => { <字体> < /font> 

<字体></字体>

const [ nftInfo, setNftInfo ] = useState ( null ) < font > < /font> 

<字体></字体>

const fetchTokenData = async ( ) => { < font > < /font> 

<字体></字体>

const encoding = await fcl < font > < /font> 

<字体></字体>

. 发送( [ <字体> < /font>

<字体></字体>

整箱 脚本`<font></font>

<字体></字体>

从 0xf8d6e0586b0a20c7<font></font> 导入 PinataPartyContract

<字体></字体>

pub fun main() : {String : String} {<font></font>

<字体></字体>

让 nftOwner = getAccount(0xf8d6e0586b0a20c7)<font></font>

<字体></字体>

让能力 = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver)<font></font>

<字体></字体>

让receiverRef = ability.borrow()<font></font>

<字体></字体>

?? 恐慌(“无法借用接收者参考”)<font></font>

<字体></字体>

返回receiverRef.getMetadata(id: 1)<font></font>

<字体></字体>

}<字体></字体>

<字体></字体>

` <字体> < /font>

<字体></字体>

] ) <字体> < /字体>

<字体></字体>

const解码 =等待fcl。解码编码)<字体> < /字体>

<字体></字体>

setNftInfo 解码)< font > < /font>

<字体></字体>

} ; <字体> < /字体>

<字体></字体>

返回( <字体> < /字体> 

<字体></字体>

< div classname= "”token-data”" > < font > < /font>

<字体></字体>

< div classname= ""center"" > < font > < /font>

<字体></字体>

< button classname= ""btn-primary"" οnclick= "{fetchTokenData}" >获取令牌数据< /button><font></font >

<字体> < /字体>

</div > <字体> < /font>

<字体></字体>

{ <字体> < /字体>

<字体></字体>

nftInfo && < font > < /font>

<字体></字体>

< div > < font > < /font>

<字体></字体>

{ <字体> < /字体>

<字体></字体>

目的。( nftInfo ) 地图k => { <字体> < /字体> 

<字体></字体>

返回( <字体> < /字体> 

<字体></字体>

< p > { k } : { nftInfo [ k ] } < /p><font></font >

<字体> < /字体>

)<字体></字体>

<字体> < /字体>

})<字体></字体>

<字体> < /字体>

}<字体></字体>

<字体> < /字体>

<div classname="”center" video”=""><font></font >

<字体> < /字体>

<video id="”nft-video”" canplaythrough=""controls=""width="”85%”"><font></font >

<字体> < /字体>

<源SRC = “{`的https:/ 。/ IPF问题IO / IPF问题/ $ { nftInfo [ ‘URI’ ] 分裂://’)[1]}`}”类型=“”视频/ MP4”” ><字体></字体>

<字体> < /字体>

</video > <字体> < /font>

<字体></字体>

< div > < font > < /font>

<字体></字体>

< button οnclick= "{()" == "" > setNftInfo ( null ) } className=”btn-secondary” >清除令牌信息< /button><font></font > 

<字体> < /字体>

</div > <字体> < /font>

<字体></字体>

< /div><font></font >

<字体> < /字体>

</div > <字体> < /font>

<字体></字体>

} <字体> < /字体>

<字体></字体>

< /div><font></font >

<字体> < /字体>

);<字体></字体>

<字体> < /字体>

};<字体></字体>

<字体> < /字体>

导出默认 TokenData;<font></font >

带有源的视频元素指向 IPFS 上的文件。使用 NFT 创建的 URI 看起来像 ipfs://Qm...

我们这样做是因为 IPFS 桌面客户端允许点击和打开链接。

NFT 现在将成为区块链上真正的实时数字资产。

现在,我们将启用 NFT 的传输。

传输 NFT

首先,我们需要为建立市场创建合同。将为以下对象创建合同:

  • 可替代代币的支付机制
  • 代币供应设置
  • 代币传输功能

让我们创建一个可替代的代币合约,用于在购买 NFT 时进行支付。

我们将通过定义空合约来创建一个可替代的代币合约:

pub 合约 PinnieToken {}

需要将与令牌和提供者资源关联的令牌发布变量添加到合约中。

pub var totalSupply: UFix64 < font >< /font >

pub var tokenName: String < font >< /font >

pub 资源接口 Provider {< font >< /font >

酒馆乐趣撤回量:UFix64 :@Vault {<字体> < / FONT >

{<字体>< /字体>

结果。余额== UFix64 金额<字体>< /字体>

“提现金额必须与提现的金库余额相同。” <字体>< /字体>

}<字体>< /字体>

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

在空合约中添加上述合约。

称为Provider的资源接口定义了一个公共函数,但账户所有者只能调用它。然后,我们将定义另外两个公共资源接口:

pub 资源接口 Receiver {< font >< /font >

酒吧趣味存款来自@Vault )< font >< /font >

}<字体>< /字体>

pub 资源接口 Balance {< font >< /font >

pub var balance: UFix64 < font >< /font >

}<字体>< /字体>

<字体>< /字体>

上述接口直接位于 Provider 资源接口下方。Receiver 接口涉及任何人都可以执行的功能。它确保只要接收者初始化一个保管库来处理通过合约创建的代币,就可以将存款存入账户。Balance 资源为任何提供的帐户返回新令牌的余额。

让我们创建 Vault 资源并在 Balance 资源下方添加以下代码:

pub资源库:提供者,接收者,余额{<字体>< /字体>

pub var balance: UFix64 < font >< /font >

初始化余额:UFix64 ){<字体>< /字体> 

自己。平衡=平衡<字体>< /字体>

}<字体>< /字体>

酒馆乐趣撤回量:UFix64 :@Vault {<字体> < / FONT >

自己。平衡=自我。余额-金额<字体>< /字体>

return < -create Vault ( balance: amount )< font >< /font > 

}<字体>< /字体>

pub fun存款来自:@Vault ){<字体>< /字体> 

自己。平衡=自我。余额+从。平衡<字体>< /字体>

< font >< /font >销毁

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

在Vault界面下添加以下功能:

pub fun createEmptyVault () : @Vault {<字体>< /font >

return < -create Vault ( balance: 0 . 0 )< font >< /font > 

}<字体>< /字体>

<字体>< /字体>

顾名思义,该函数为帐户创建一个空的 Vault 资源。余额为0。

因此,我们现在需要设置铸币能力。在 createEmptyVault 函数下方添加以下代码:

pub 资源 VaultMinter {< font >< /font >

pub fun mintTokens 金额:UFix64,接收方:Capability < &AnyResource { Receiver }>)< font >< /font >

{<字体>< /字体>

让收件人引用 = 收件人。借用()<字体>< /字体>

?? 恐慌“无法借用接收者对保险库的引用” )< font >< /font >

品尼令牌。totalSupply = PinnieToken。totalSupply + UFix64 数量)<字体>< /font >

收件人参考 存款来自:< -create Vault 余额:金额))<字体>< /字体>

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

VaultMinter 资源是公开的,但仅对合约账户所有者可用。

VaultMinter 资源仅包含一个功能:需要一定数量的 mintTokens 和一个接收者。只要接收者将存储 Vault 资源,就可以将新铸造的代币存入该帐户。

铸造代币时需要更新 totalSupply 变量。因此,铸造的数量被添加到之前的供应量中以获得新的供应量。

现在,我们需要初始化合约并在 VaultMinter 资源之后添加以下代码:

init () {<字体>< /字体> 

自己。totalSupply = 30.0 <字体>< /字体>

自己。tokenName = “Pinnie” < font >< /font >

let Vault < - 创建Vault ( balance: self.totalSupply )< font >< /font >

自己。帐户保存(< -vault, to: /storage/MainVault )< font >< /font >

自己。帐户保存(< -create VaultMinter () ,以便:/存储/ MainMinter )<字体> < / FONT >

自己。帐户link < &VaultMinter >( /private/Minter, target: /storage/MainMinter )< font >< /font >

}<字体>< /字体>

<字体>< /字体>

初始化合约时必须设置总供应量。在这个例子中,我们用 30 的供应量初始化了合约,并将代币名称设置为“Pinnie”。

部署和铸造代币

更新项目中的 flow.json 文件以部署新合约。确保 flow.json 文件引用新合约并具有 emulator-account 密钥引用:

{<字体>< /字体>

“模拟器”:{<字体>< /font >

“默认”:{<字体>< /字体>

“端口”:3569 , <字体>< /font >

“serviceAccount”: “emulator-account” < font >< /font >

}<字体>< /字体>

} , <字体>< /font >

“合同”:{<字体>< /字体>

“PinataPartyContract”:“./cadence/contracts/PinataPartyContract. cdc ”, <字体>< /font >

“PinnieToken”:“./cadence/contracts/PinnieToken. cdc <字体>< /font >

} , <字体>< /font >

“网络”:{<字体>< /字体>

“模拟器”:{<字体>< /font >

“主机”:“ 127.0 . 0.1 : 3569 ”, <字体>< /font >

“chain”: “flow-emulator” < font >< /font >

}<字体>< /字体>

} , <字体>< /font >

“账户”:{<字体>< /字体>

“模拟器账户”:{<字体>< /字体>

“地址”:“f8d6e0586b0a20c7”,<字体>< /font >

“键”:“e5ca2b0946358223f0555206144fe4d74e65cbd58b0933c5232ce195b9058cdd” <字体>< /font >

}<字体>< /字体>

} , <字体>< /font >

“部署”:{<字体>< /字体>

“模拟器”:{<字体>< /font >

“emulator-account”: [ “PinataPartyContract”, “PinnieToken” ]< font >< /font >

}<字体>< /字体>

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

在 pinata-party 项目目录中的另一个终端窗口中,运行 flow project deploy。现在,让我们测试铸币功能。我们将创建一个允许我们创建 Pinnie 代币的交易。但首先,我们需要更新 flow.json 文件。

更改 emulator-account 下的json:

“模拟器账户”:{<字体>< /字体>

“地址”:“f8d6e0586b0a20c7”,<字体>< /font >

“私钥”:“e5ca2b0946358223f0555206144fe4d74e65cbd58b0933c5232ce195b9058cdd”,<字体>< /font >

"sigAlgorithm" : "ECDSA_P256" , < font >< /font >

"hashAlgorithm" : "SHA3_256" , < font >< /font >

"chain" : "flow-emulator" < font >< /font >

}<字体>< /字体>

<字体>< /字体>

key 字段变为 privateKey 字段,我们添加属性,包括 sigAlgorithm、chain 和 hashAlgorithm。

开发 NFT 市场

在处理市场的前端之前,我们应该有一个合同来处理市场的创建和管理。

在 cadence/contracts 文件夹中,创建一个名为 MarketplaceContract.cdc 的新文件。

0xf8d6e0586b0a20c7导入 PinataPartyContract < font >< /font >

0xf8d6e0586b0a20c7导入 PinnieToken < font >< /font >

pub 合约 MarketplaceContract {< font >< /font >

pub event ForSale ( id: UInt64, price: UFix64 )< font >< /font >

pub 事件PriceChanged ( id: UInt64, newPrice: UFix64 )< font >< /font >

pub event TokenPurchased ( id: UInt64, price: UFix64 )< font >< /font >

pub 事件SaleWithdrawn ( id: UInt64 )< font >< /font >

pub 资源接口 SalePublic {<字体>< /font >

酒馆乐趣购买tokenID:UINT64,收件人:&AnyResource { PinataPartyContract。NFTReceiver } ,buyTokens:@PinnieToken保管箱)<字体> < / FONT >

pub fun idPrice ( tokenID: UInt64 ) : UFix64? <字体>< /字体>

pub fun getIDs () : [ UInt64 ]< font >< /font >

}<字体>< /字体>

}<字体>< /字体>

<字体>< /字体>

我们需要导入 NFT 合约和可替代代币合约。我们在合约定义中定义了四个事件:

  • 出售:出售 NFT
  • PriceChanged:NFT 价格的变化
  • TokenPurchased:购买 NFT 时
  • SaleWithdrawn:当 NFT 从市场上移除时

我们在事件发射器下方添加了一个名为 SalePublic 的资源接口。接口应该对所有人公开,而不仅仅是合同所有者。

我们需要在 SalePublic 接口下方添加一个 SaleCollection 资源。我们在此资源中定义了一些变量。例如,映射待售代币、映射每个待售代币的价格以及只能由名为 ownerVault 的合约所有者访问的受保护变量。

在资源上定义变量时,我们需要初始化变量。它在 init 函数中完成,并使用所有者的保管库资源和空值进行初始化。

然后,定义函数来控制 NFT 市场的操作很重要。功能是:

  • 待售清单
  • 更改价格
  • 提取
  • 价格
  • 购买
  • 破坏
  • 获取ID

如上所述,其中三个功能是公开可用的,这意味着 listForSale、撤销、销毁和 changePrice 仅对被列出的 NFT 所有者可用。例如,changePrice 不公开,因为我们不希望任何人改变 NFT 的价格。市场合约的最后一部分是 CreateSaleCollection 函数。

它允许将集合作为资源添加到帐户。编写该合约后,我们将使用模拟器帐户部署它。从项目的根目录运行以下命令:

流项目部署< font >< /font >

<字体>< /字体>

它将最终部署市场合约并允许我们在前端应用程序中使用它。

完成后,我们需要在前端工作并使用 Flow CLI 工具连接这些合约。

猜你喜欢

转载自blog.csdn.net/awf133/article/details/121564588