NFT教程:用Flow和IPFS創建NFT

2021-03-16 18:03:28

非同質化代幣(NFT)市場正在進入狂熱[4],回顧 NFT 早期的發展歷程,回憶CryptoKitties[5]所暴露出挑战是很有意思的。CryptoKitties 由Dapper Labs[6]的團隊打造,是讓以太坊第一次出現“大規模”使用的案例。

從那之後,NFT 就开始成長之路,Rarible[7]、OpenSea[8]、Foundation[9]、Sorare[10]等平臺紛紛湧現。這些平臺每月都有數百萬元的流量。盡管磕磕碰碰,但大部分依舊在以太坊區塊鏈上發生着。但 Dapper Labs 的團隊在經歷了 CryptoKitties 之後,着手建立一個新的通用的,很適合 NFT 使用場景區塊鏈[11]。他們這樣做的目標是想解決在以太坊上看到的許多 NFT 的問題,同時為該領域的开發者和收藏者提供更好的體驗。他們的新區塊鏈Flow[12],已經證明了自己能夠落地,並吸引一些大牌。,如 NBA[13]、UFC、甚至 Dr. Seuss 都在使用 Flow。

我們最近寫了使用 IPFS 上保存標的資產來創建 NFT[14],並且討論 NFT 領域的責任問題[15],以及 IPFS 如何提供幫助。現在,這篇文章談談如何在 Flow 上創建 IPFS 支持的 NFT。Flow 區塊鏈早期的主要應用之一是NBA 巔峯對決 (NBA Top Shot)[16]。我們要重新建立一個非常基本的 NFT 鑄幣過程,然後在 IPFS 上回溯 NFT 元數據和標的資產。

由於我們喜歡 piñatas,所以我們的 NFT 將不再是 NBA 精彩的視頻,而是一個可交易的 piñatas 視頻。

本教程有 3 篇文章

  1. 創建合約和鑄造代幣(本文是第一篇)

  2. 創建一個應用程序,以查看通過該合約創建的 NFT。

  3. 創建一個市場,將 NFT 轉讓給他人,同時也轉移在 IPFS 上的標的資產。

環境設置

我們需要安裝 Flow CLI。在Flow 的文檔[17]中有一些很好的安裝說明:

macOS

brew install flow-cli

Linux

sh -ci “$(curl -fsSL https://storage.googleapis.com/flow-cli/install.sh)"

Windows

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

我們將在 IPFS 上存儲資產文件。我們使用Pinata[18] 來簡化操作, 可以在這裏注冊一個免費账戶[19],獲取一個API 密鑰[20]。在本教程的第二篇文章中會使用 Pinata API,但在本篇文章中我們使用 Pinata 網站。

我們還需要安裝 NodeJS 和一個文本編輯器,它可以幫助高亮顯示 Flow 智能合約(這是用一種叫做Cadence[21]的語言編寫)代碼的語法。Visual Studio Code 有一個支持 Cadence 語法的插件[22]

讓我們為項目創建一個目錄:

mkdir pinata-party

進入該目錄,並初始化一個新的 flow 項目:

cd pinata-party
flow project init

現在,使用你最喜歡的代碼編輯器中打开項目(如果你使用 Visual Studio Code,可以安裝下 Cadence 插件),讓我們开始工作。

你會看到一個flow.json文件,我們很快就會用到它。首先,創建一個名為 cadence的文件夾。在該文件夾內,再添加一個名為 contracts的文件夾。最後,在 contracts文件夾中創建一個名為 PinataPartyContract.cdc的文件。

說明一下,我們現在所做的一切關於 Flow 區塊鏈的工作都將在模擬器上完成。但是,將一個項目部署到測試網或主網,只需要更新flow.json文件中的配置這樣簡單。我們現在就把這個文件設置成模擬器環境,然後就可以开始寫我們的合約了。

更新flow.json中的合約對象,代碼如下:

"contracts": {
     "PinataPartyContract": "./cadence/contracts/PinataPartyContract.cdc"
}

然後,更新該文件中的 deployments對象,代碼如下:

"deployments": {
     "emulator": {
          "emulator-account": ["PinataPartyContract"]
     }
}

這是在告訴 Flow CLI 使用模擬器來部署我們的合約,它也在引用(在模擬器上)我們即將寫的合約 ...

合約

Flow 有一個關於創建 NFT 合約的出色教程。他是一個很好的參考,但是正如 Flow 自己指出的[23],他們還沒有解決 NFT 元數據的問題。他們希望在鏈上存儲元數據。這是個好主意,他們一定會想出一個合理的辦法來。然而,我們現在想要鑄造一些帶有元數據的代幣,並且我們想要關聯上對應的媒體文件(標的)。元數據只是其中一個組成部分。我們還需要指出代幣最終代表的媒體文件。

如果你熟悉以太坊區塊鏈上的 NFT,你可能會知道,許多代幣的標的資產都存儲在傳統的雲服務器上,這樣做是可以的,但又弊端。我們曾寫過關於IPFS[24]內容可尋址,以及在傳統雲平臺上存儲區塊鏈數據的弊端[25],歸結起來主要有兩點:

  • 資產應可核查

  • 應該很容易轉移維護責任

IPFS[26]解決了這兩點。而 Pinata 則以一種簡單的方式將該內容長期保存在 IPFS 上。這正是我們的 NFT 關聯的資料所需要的?我們要確保能夠證明擁有 NFT 的所有權,並確保我們能控制對標的資產(IPFS)--媒體文件或其他內容,確保不是復制品。

考慮到這一點,讓我們寫一份合約,它可以鑄造 NFT,將元數據關聯到 NFT,並確保元數據指向存儲在 IPFS 上的標的資產。

打开PinataPartyContract.cdc,編寫一下代碼:

pub contract PinataPartyContract {
  pub resource NFT {
    pub let id: UInt64
    init(initID: UInt64) {
      self.id = initID
    }
  }
}

第一步是定義合約,後面會添加更多的內容,但我們首先定義PinataPartyContract,並在其中創建一個resource。資源是存儲在用戶账戶中並通過訪問控制措施進行訪問。在這裏,NFT資源最終用來代表 NFT 所擁有的東西。NFT 必須是唯一的, id屬性允許我們標識代幣。

接下來,我們需要創建一個資源接口,我們將用它來定義哪些能力可以提供給其他人(即不是合約所有者)。

pub resource interface NFTReceiver {
  pub fun deposit(token: @NFT, metadata: {String : String})
  pub fun getIDs(): [UInt64]
  pub fun idExists(id: UInt64): Bool
  pub fun getMetadata(id: UInt64) : {String : String}
}

把這個代碼放在 NFT resource 代碼的下面。這個NFTReceiver資源接口用來定義對資源有訪問權的人,就可以調用以下方法:

  • deposit

  • getIDs

  • idExists

  • getMetadata

接下來,我們需要定義代幣收藏品( Colletion )接口。把它看成是存放用戶所有 NFT 的錢包。

pub resource Collection: NFTReceiver {
    pub var ownedNFT: @{UInt64: NFT}
    pub var metadataObjs: {UInt64: { String : String }}
    init () {
        self.ownedNFT <- {}
        self.metadataObjs = {}
    }
    pub fun withdraw(withdrawID: UInt64): @NFT {
        let token <- self.ownedNFT.remove(key: withdrawID)!
        return <-token
    }
    pub fun deposit(token: @NFT, metadata: {String : String}) {
        self.ownedNFT[token.id] <-! token
    }
    pub fun idExists(id: UInt64): Bool {
        return self.ownedNFT[id] != nil
    }
    pub fun getIDs(): [UInt64] {
        return self.ownedNFT.keys
    }
    pub fun updateMetadata(id: UInt64, metadata: {String: String}) {
        self.metadataObjs[id] = metadata
    }
    pub fun getMetadata(id: UInt64): {String : String} {
        return self.metadataObjs[id]!
    }
    destroy() {
        destroy self.ownedNFT
    }
}

這個資源裏有很多東西,說明一下。首先,有一個變量叫ownedNFT。這個是很直接的,它可以跟蹤用戶在這個合約中所有擁有的 NFT。

接下來,有一個變量叫metadataObjs。這個有點特殊,因為我們擴展了 Flow NFT 合約功能,為每個 NFT 存儲元數據的映射。這個變量將代幣 id 映射到其相關的元數據上,這意味着我們需要在設置代幣 id 之前,將其設置為元數據。

然後我們初始化變量。定義在 Flow 中的資源中的變量必需初始化。

最後,我們擁有了 NFT Collection 資源的所有可用函數。需要注意的是,並不是所有這些函數大家都可以調用。你還記得在前面,NFTReceiver資源接口中定義了任何人都可以訪問的函數。

我尤其想指出 deposit函數。正如我們擴展了默認的 Flow NFT 合約以包含 metadataObjs映射一樣,我們正在擴展默認的 deposit函數,以接受額外的 metadata參數。為什么要在這裏做這個?因為需要確保只有 token 的 minter 可以將該元數據添加到 token 中。為了保持這種私密性,將元數據的初始添加限制在鑄幣執行中。

合約代碼就快完成了。因此,在 Collection資源的下面,添加以下內容:

pub fun createEmptyCollection(): @Collection {
    return <- create Collection()
}
pub resource NFTMinter {
    pub var idCount: UInt64
    init() {
        self.idCount = 1
    }
    pub fun mintNFT(): @NFT {
        var newNFT <- create NFT(initID: self.idCount)
        self.idCount = self.idCount + 1 as UInt64
        return <-newNFT
    }
}

首先,我們有一個函數,在調用時創建一個空的 NFT Collection。這就是第一次與合約進行交互的用戶如何創建一個存儲位置,該位置映射到定義好的 Collection資源。

之後,我們再創建一個資源(resource)。它很重要的,因為沒有它,我們就無法鑄造代幣。NFTMinter資源包括一個idCount,它是遞增的,以確保我們的 NFT 不會有重復的 id。它還有一個功能,用來創造 NFT。

NFTMinter資源的下方,添加主合約初始化函數;

init() {
      self.account.save(<-self.createEmptyCollection(), to: /storage/NFTCollection)
      self.account.link<&{NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection)
      self.account.save(<-create NFTMinter(), to: /storage/NFTMinter)
}

這個初始化函數只有在合約部署時才會被調用。它有三個作用。

  1. 為收藏品(Collection)的部署者創建一個空的收藏品,這樣合約的所有者就可以從該合約中鑄造和擁有 NFT。

  2. Collection資源發布在一個公共位置,並引用在一开始創建的NFTReceiver接口。通過這個方式告訴合約,在NFTReceiver上定義的函數可以被任何人調用。

  3. NFTMinter資源被保存在账戶存儲中,供合約的創建者使用。這意味着只有合約的創造者才能鑄造代幣。

合約全部代碼可在這裏找到[27]

現在合約已經准備好了,讓我們來部署它,對嗎?我們也許應該在Flow Playground[28]上測試一下。到那裏,點擊左側側欄的第一個账號。將示例合約中的所有代碼替換為我們的合約代碼,然後點擊部署。如果一切順利,你應該在屏幕底部的日志窗口中看到這樣的日志。

16:48:55 Deployment Deployed Contract To: 0x01

現在我們已經准備好將合約部署到本地運行的模擬器上。在命令行中,運行:

flow project start-emulator

現在,如果模擬器的運行正確和flow.json文件的正確配置,我們可以部署合約。只需運行這個命令:

flow project deploy

如果一切順利,你應該看到這樣的輸出:

Deploying 1 contracts for accounts: emulator-accountPinataPartyContract -> 0xf8d6e0586b0a20c7

現在已經在 Flow 模擬器上上线了一個合約,但我們想鑄造一個 NFT 代幣。

鑄造 NFT

在教程的第二篇文章中,我們將通過一個應用程序和用戶界面使鑄幣過程更加友好。為了看到所鑄造的內容,並展示元數據如何在 Flow 上與 NFT 一起工作,我們將使用 Cadence 腳本和命令行。

pinata-party項目的根目錄下創建一個新的目錄,我們把它叫做 transactions。創建好文件夾,在裏面創建一個名為MintPinataParty.cdc 的新文件。

為了編寫出交易,先需要提供給 NFT 的元數據一個引用文件。為此,我們將通過 Pinata 上傳一個文件到 IPFS。這個教程中,我將上傳一個孩子在生日派對上砸 pinata 的視頻。你可以上傳任何你想要的視頻文件。你真的可以上傳任何你喜歡的資產文件,並將其與你的 NFT 關聯起來,在本教程系列的第二篇文章將期待視頻內容。一旦你准備好你的視頻文件,在這裏上傳[29]

當你上傳文件後,你會得到一個 IPFS 哈希(通常被稱為內容標識符或 CID)。復制這個哈希值,因為我們將在鑄幣過程中使用它。

現在,在你的MintPinataParty.cdc文件中,添加以下內容:

import PinataPartyContract from 0xf8d6e0586b0a20c7
transaction {
  let receiverRef: &{PinataPartyContract.NFTReceiver}
  let minterRef: &PinataPartyContract.NFTMinter
  prepare(acct: AuthAccount) {
      self.receiverRef = acct.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver)
          .borrow()
          ?? panic("Could not borrow receiver reference")
      self.minterRef = acct.borrow<&PinataPartyContract.NFTMinter>(from: /storage/NFTMinter)
          ?? panic("could not borrow minter reference")
  }
  execute {
      let metadata : {String : String} = {
          "name": "The Big Swing",
          "swing_velocity": "29",
          "swing_angle": "45",
          "rating": "5",
          "uri": "ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"
      }
      let newNFT <- self.minterRef.mintNFT()
      self.receiverRef.deposit(token: <-newNFT, metadata: metadata)
      log("NFT Minted and deposited to Account 2's Collection")
  }
}

這是一個非常簡單的交易代碼,這在很大程度上要歸功於 Flow 所做的工作,但讓我們來看看它。首先,你會注意到頂部的導入語句。如果你還記得,在部署合約時,我們收到了一個账戶地址。它就是這裏引用的內容。因此,將0xf8d6e0586b0a20c7替換為你部署的账戶地址。

接下來我們對交易進行定義。在我們的交易中,我們首先要做的是定義兩個參考變量,receiverRefminterRef。在這種情況下,我們既是 NFT 的接收者,又是 NFT 的挖掘者。這兩個變量是引用我們在合約中創建的資源。如果執行交易的人對資源沒有訪問權,交易將失敗。

接下來,我們有一個prepare函數。該函數獲取試圖執行交易的人的账戶信息並進行一些驗證。它會嘗試 借用兩個資源 NFTMinterNFTReceiver上的可用能力。如果執行交易的人沒有訪問這些資源的權限,驗證無法通過,這就是交易會失敗的原因。

最後是execute函數。這個函數是為我們的 NFT 建立元數據,鑄造 NFT,然後在將 NFT 存入账戶之前關聯元數據。如果你注意到,我創建了一個元數據變量。在這個變量中,添加了一些關於 token 的信息。由於我們的代幣代表的是一個事件,即一個 piñata 在派對上被打碎,並且因為我們試圖復制你在 NBA Top Shot 中看到的大部分內容,所以我在元數據中定義了一些統計數據。孩子揮棒打 piñata 的速度,揮棒的角度和等級。我只是覺得這些統計數字有意思。你可以用類似的方式為你的代幣定義任何有意義的信息。

你會注意到,我還在元數據中定義了一個uri屬性。這將指向 IPFS 哈希,它承載着我們與 NFT 相關的標的資產文件。在這種情況下,它是 piñata 被擊中的真實視頻。你可以用你之前上傳文件後收到的哈希值來替換。

我們用ipfs://作為哈希的前綴,有幾個原因。這是 IPFS 上文件的標識符,可以使用 IPFS 的桌面客戶端和瀏覽器擴展。也可以直接粘貼到 Brave 瀏覽器中(Brave 瀏覽器現在提供了對 IPFS 內容的原生支持[30])。

調用 mintNFT函數來創建代幣。然後調用deposit函數將其存入我們的账戶。這也是我們傳遞元數據的地方。如果你還記得,我們在 deposit函數中定義了一個關聯變量,將元數據添加到關聯的 token id 中。

最後,我們只需要日志記錄代幣已被鑄造和存入账戶的信息。

現在我們差不多可以執行代碼發送交易鑄造 NFT 了。但首先,我們需要准備好我們的账戶。在項目根目錄下的命令行中,創建一個新的籤名私鑰。

運行以下命令。

flow keys generate

這將返回你一個公鑰和一個私鑰, 請始終保護好你的私鑰

我們將需要私鑰來籤署交易,所以我們可以把它粘貼到flow.json文件中。我們還需要指定籤名算法。下面是flow.json文件中的accounts 的內容:

"accounts": {
  "emulator-account": {
     "address": "YOUR ACCOUNT ADDRESS",
     "privateKey": "YOUR PRIVATE KEY",
     "chain": "flow-emulator",
     "sigAlgorithm": "ECDSA_P256",
     "hashAlgorithm": "SHA3_256"
  }
},

如果你打算在 github 或任何遠程 git 倉庫上存儲這個項目的任何內容,請確保你不包含私鑰。你可能想.gitignore你的整個flow.json。盡管我們只是使用本地模擬器,但保護你的密鑰是個好做法。

現在可以發送交易,簡單的運行這個命令:

flow transactions send --code ./transactions/MintPinataParty.cdc --signer emulator-account

flow.json中引用編寫的交易代碼文件和籤名账戶。如果一切順利,你應該看到這樣的輸出:

Getting information for account with address 0xf8d6e0586b0a20c7 ...
Submitting transaction with ID 4a79102747a450f65b6aab06a77161af196c3f7151b2400b3b3d09ade3b69823 ...
Successfully submitted transaction with ID 4a79102747a450f65b6aab06a77161af196c3f7151b2400b3b3d09ade3b69823

最後,驗證 token 是否在我們的账戶中,並獲取元數據。做到這一點,我們要寫一個非常簡單的腳本,並從命令行調用它。

在項目根目錄,創建一個名為 scripts的新文件夾。在裏面,創建一個名為CheckTokenMetadata.cdc的文件。在該文件中,添加以下內容:

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

這個腳本可以被認為是類似於以太坊智能合約上調用只讀方法。它們是免費的,只返回合約中的數據。

在腳本中,導入部署的合約地址。然後定義一個 main函數(這是腳本運行所需的函數名)。在這個函數裏面,我們定義了三個變量:

  • nftOwner:擁有 NFT 的账戶。由於使用部署了合約的账戶中鑄造了 NFT,所以在我們的例子中,這兩個地址是一樣的。這一點不一定,要看你將來的合約設計。

  • capability:需要從部署的合約中 借用的能力(或功能)。請記住,這些能力是受訪問控制的,所以如果一個能力對試圖借用它的地址不可用,腳本就會失敗。我們正在從NFTReceiver資源中借用能力。

  • receiverRef:這個變量只是簡單地記錄我們的能力。

現在,我們可以調用(可用的)函數。在這種情況下,我們要確保相關地址確實已經收到了我們鑄造的 NFT,然後我們要查看與代幣相關的元數據。

讓我們運行的腳本,看看得到了什么。在命令行中運行以下內容:

flow scripts execute ./scripts/CheckTokenMetadata.cdc

你應該會看到元數據輸出的類似這樣的輸出。

{"name": "The Big Swing", "swing_velocity": "29", "swing_angle": "45", "rating": "5", "uri": "ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"}

恭喜你!你成功創建了一個 Flow 智能合約,鑄造了一個代幣,並將元數據關聯到該代幣,並將該代幣的底層數字資產存儲在 IPFS 上。作為教程的第一部分,還算不錯。

接下來,我們有一個關於構建前端 React 應用的教程,通過獲取元數據和解析元數據,讓你顯示你的 NFT。

本翻譯由 Cell Network[31] 贊助支持。

來源:

https://medium.com/pinata/how-to-create-nfts-like-nba-top-shot-with-flow-and-ipfs-701296944bf

參考資料

[1]

登鏈翻譯計劃: https://github.com/lbc-team/Pioneer

[2]

翻譯小組: https://learnblockchain.cn/people/412

[3]

Tiny 熊: https://learnblockchain.cn/people/15

[4]

正在進入狂熱: https://www.cnbc.com/2021/02/25/nfts-why-digital-art-and-sports-collectibles-are-suddenly-so-popular.html

[5]

CryptoKitties: https://www.cryptokitties.co/

[6]

Dapper Labs: https://www.dapperlabs.com/

[7]

Rarible: https://rarible.com/

[8]

OpenSea: https://opensea.io/

[9]

Foundation: https://foundation.app/

[10]

Sorare: https://sorare.com/

[11]

着手建立一個新的通用的,很適合NFT使用場景區塊鏈: https://medium.com/dapperlabs/introducing-flow-a-new-blockchain-from-the-creators-of-cryptokitties-d291282732f5

[12]

Flow: https://www.onflow.org/

[13]

NBA: https://www.nbatopshot.com/

[14]

寫了使用IPFS上保存標的資產來創建NFT: https://learnblockchain.cn/article/2247

[15]

責任問題: https://medium.com/pinata/who-is-responsible-for-nft-289"    src="https://img.jinse.com/3638511_image3.png"   >

譯文出自:登鏈翻譯計劃[1]

譯者:翻譯小組[2]

校對:Tiny 熊[3]

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。

推薦文章

btc日內再次下跌 短线應當如何處理?

盡管以太坊現貨ETF獲批是個好消息,但市場反應卻不如預期。在消息公布後,以太坊價格出現了小幅下跌,...

加密蓮
134 3個月前

7月23日、BTC(合約)ETH(合約)行情分析及操作策略

昨日收益還是不錯的,日內給出的現價空單分別止盈我們目標點位,恭喜跟上的朋友喫肉。時間一晃到月底了,...

倪老師
134 3個月前

幣圈院士:血與淚的教訓!交易者為何總是撞死在同一棵樹上?

幣圈院士談。交易市場中的幾種“死法” 在幣圈市場鱗次櫛比的海洋,風起雲湧,時常讓人感到驚手不及。在...

幣圈院士
139 3個月前

7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC

7月23:Mt. Gox 比特幣錢包在市場緊縮的情況下轉移了價值 28.2 億美元的 BTC一個引...

168超神
131 3個月前

悅盈:比特幣68000的空完美落地反彈繼續看跌 以太坊破前高看回撤

一個人的自律中,藏着無限的可能性,你自律的程度,決定着你人生的高度。 人生沒有近路可走,但你走的每...

我是周悅盈
111 3個月前

btc完美盈利 晚間波動較大注意

昨日btc空單完美給到,最大化走出一千七百點空間~ btc: 日內开盤下跌繼續測試66000一线,...

加密蓮
121 3個月前