Bitaigen 编辑团队认为,本文系统梳理了在 Flow 与 IPFS 环境下编写、部署并铸造 NFT 的完整步骤。我们从工具安装到 Cadence 合约实现逐项说明,帮助开发者快速上手,后续实战案例更值得期待,建议细读。
用Flow和IPFS创建NFT的合约和铸造代币操作教程
在 Flow 区块链上配合 IPFS,可通过编写 Cadence 合约、部署至模拟器或测试网、使用 Pinata 上传资产获取 CID,随后在交易中铸造 NFT 并将元数据绑定到代币上,实现完整的创建与铸造流程。
环境设置
- 安装 Flow CLI
- macOS
```bash
brew install flow-cli
```
- Linux
```bash
sh -ci "$(curl -fsSL https://storage.googleapis.com/flow-cli/install.sh)"
```
- Windows
```powershell
iex "& { $(irm 'https://storage.googleapis.com/flow-cli/install.ps1') }"
```
- 准备 IPFS 资产存储
- 注册 Pinata 免费账户,获取 API Key。后续第二篇教程中会使用 Pinata API,本篇仅通过 Pinata 网站手动上传。
- 安装 Node.js 与代码编辑器
- 推荐使用 Visual Studio Code,并安装 Cadence 语法插件,便于高亮显示智能合约代码。
- 创建项目目录
```bash
mkdir pinata-party
cd pinata-party
flow project init
```
项目初始化后会生成 `flow.json`,稍后会对其进行配置。
- 组织代码结构
```
pinata-party/
├─ cadence/
│ └─ contracts/
│ └─ PinataPartyContract.cdc
├─ transactions/
└─ scripts/
```
所有 Flow 相关代码都放在 `cadence/contracts` 目录下,后续的交易脚本和查询脚本分别放在 `transactions` 与 `scripts` 中。
配置 flow.json 为模拟器
在 `flow.json` 中添加合约路径:
```json
"contracts": {
"PinataPartyContract": "./cadence/contracts/PinataPartyContract.cdc"
},
"deployments": {
"emulator": {
"emulator-account": ["PinataPartyContract"]
}
}
```
以上配置指示 Flow CLI 使用本地模拟器部署 `PinataPartyContract`。
编写 NFT 合约
合约核心结构
```cadence
pub contract PinataPartyContract {
// NFT 资源定义
pub resource NFT {
pub let id: UInt64
init(initID: UInt64) {
self.id = initID
}
}
// 接收 NFT 的资源接口
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 Collection 实现
pub resource Collection: NFTReceiver {
// 存放 NFT
pub var ownedNFT: @{UInt64: NFT}
// 对应的元数据映射
pub var metadataObjs: {UInt64: {String: String}}
init() {
self.ownedNFT <- {}
self.metadataObjs = {}
}
// 提取 NFT
pub fun withdraw(withdrawID: UInt64): @NFT {
let token <- self.ownedNFT.remove(key: withdrawID)!
return <-token
}
// 存入 NFT 并绑定元数据
pub fun deposit(token: @NFT, metadata: {String: String}) {
self.ownedNFT[token.id] <-! token
self.metadataObjs[token.id] = metadata
}
pub fun idExists(id: UInt64): Bool {
return self.ownedNFT[id] != nil
}
pub fun getIDs(): [UInt64] {
return self.ownedNFT.keys
}
pub fun getMetadata(id: UInt64): {String: String} {
return self.metadataObjs[id]!
}
destroy() {
destroy self.ownedNFT
}
}
// 创建空 Collection 的工厂函数
pub fun createEmptyCollection(): @Collection {
return <-create Collection()
}
// 铸造者资源
pub resource NFTMinter {
pub var idCount: UInt64
init() {
self.idCount = 1
}
pub fun mintNFT(): @NFT {
let newNFT <- create NFT(initID: self.idCount)
self.idCount = self.idCount + 1
return <-newNFT
}
}
// 合约初始化:部署 Collection、公开接口、保存铸造者
init() {
// 为部署者创建并保存空 Collection
self.account.save(<-self.createEmptyCollection(), to: /storage/NFTCollection)
// 将 Collection 公开为 NFTReceiver 接口
self.account.link<&Collection{NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection)
// 保存 NFTMinter 资源,仅合约创建者可访问
self.account.save(<-create NFTMinter(), to: /storage/NFTMinter)
}
}
```
关键点说明
- NFT 资源仅包含唯一标识 `id`。
- NFTReceiver 接口定义了外部可以调用的四个方法:`deposit`、`getIDs`、`idExists`、`getMetadata`。
- Collection 资源实现了该接口,并额外维护 `metadataObjs` 用于存储每个 NFT 的元数据。
- NFTMinter 负责递增 `idCount` 并生成新 NFT。
- `init()` 在合约部署时自动创建空 Collection、公开接口以及铸造者资源。
部署合约
- 在 Flow Playground 或本地模拟器中部署。
- 本地部署命令:
```bash
flow project start-emulator # 启动本地模拟器
flow project deploy # 部署合约
```
成功后日志类似:
```
Deploying 1 contracts for accounts: emulator-account.PinataPartyContract -> 0xf8d6e0586b0a20c7
```
铸造 NFT(交易脚本)
创建交易文件
在 `transactions` 目录下新建 `MintPinataParty.cdc`,内容如下:
```cadence
import PinataPartyContract from 0xf8d6e0586b0a20c7
transaction {
// 引用接收器和铸造者
let receiverRef: &{PinataPartyContract.NFTReceiver}
let minterRef: &PinataPartyContract.NFTMinter
prepare(acct: AuthAccount) {
// 获取公开的 NFTReceiver 能力
self.receiverRef = acct.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver)
.borrow()
?? panic("Could not borrow receiver reference")
// 获取私有的 NFTMinter 资源
self.minterRef = acct.borrow<&PinataPartyContract.NFTMinter>(from: /storage/NFTMinter)
?? panic("Could not borrow minter reference")
}
execute {
// 元数据示例(请自行替换 IPFS CID)
let metadata: {String: String} = {
"name": "The Big Swing",
"swing_velocity": "29",
"swing_angle": "45",
"rating": "5",
"uri": "ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"
}
// 铸造 NFT 并存入 Collection
let newNFT <- self.minterRef.mintNFT()
self.receiverRef.deposit(token: <-newNFT, metadata: metadata)
log("NFT minted and deposited")
}
}
```
说明
- `import` 行中的地址需替换为实际部署合约的地址。
- `metadata` 中的 `uri` 字段使用 `ipfs://` 前缀,指向 Pinata 上传后得到的 CID。
- `deposit` 同时将 NFT 与元数据写入 Collection。
生成账户密钥并配置 flow.json
```bash
flow keys generate
```
将返回的 `privateKey`、`publicKey` 填入 `flow.json`:
```json
"accounts": {
"emulator-account": {
"address": "0xf8d6e0586b0a20c7",
"privateKey": "YOUR_PRIVATE_KEY",
"chain": "flow-emulator",
"sigAlgorithm": "ECDSA_P256",
"hashAlgorithm": "SHA3_256"
}
}
```
安全提示:切勿将私钥提交至公共仓库,建议将 `flow.json` 加入 `.gitignore`。
发送交易
```bash
flow transactions send --code ./transactions/MintPinataParty.cdc --signer emulator-account
```
成功后会返回交易 ID,表示 NFT 已写入账户的 Collection。
查询 NFT 元数据(只读脚本)
在 `scripts` 目录新建 `CheckTokenMetadata.cdc`:
```cadence
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)
}
```
运行脚本:
```bash
flow scripts execute ./scripts/CheckTokenMetadata.cdc
```
预期输出类似:
```
{"name":"The Big Swing","swing_velocity":"29","swing_angle":"45","rating":"5","uri":"ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"}
```
至此,Flow 与 IPFS 的完整 NFT 创建、铸造以及元数据查询流程已全部实现。
---
小结
- 使用 Cadence 编写符合 Flow 标准的 NFT 合约。
- 通过 Pinata 将媒体文件上传至 IPFS,获得 CID。
- 在交易脚本中将 CID 写入元数据字段 `uri`,实现链上 NFT 与链下资产的绑定。
- 本教程覆盖了环境搭建、合约开发、部署、铸造以及查询四个关键环节,为进一步构建前端展示或二级市场奠定基础。

---
*本文翻译由 Cell Network 赞助。原文链接:https://medium.com/pinata/how-to-create-nfts-like-nba-top-shot-with-flow-and-ipfs-701296944bf*
关键要点
- 使用 Flow CLI 在本地模拟器部署 Cadence 合约
- 通过 Pinata 上传资产并获取 IPFS CID
- 项目结构划分为 contracts、transactions、scripts 目录
- 在交易中铸造 NFT 并将元数据绑定至代币
常见问题
如何安装 Flow CLI?
在 macOS 上使用 brew 安装:`brew install flow-cli`;Linux 通过 curl 脚本执行 `sh -ci "$(curl -fsSL https://storage.googleapis.com/flow-cli/install.sh)"`;Windows 使用 PowerShell 运行 `iex "& { $(irm 'https://storage.googleapis.com/flow-cli/install.ps1') }"` 即可完成 Flow CLI 的安装。
Pinata 在本教程中有什么作用?
Pinata 提供 IPFS 文件托管服务。教程中先在 Pinata 官网注册免费账户并获得 API Key,随后手动上传图片或 JSON 等资产,Pinata 会返回唯一的 CID。该 CID 在铸造 NFT 时作为元数据的指向,实现链上代币与链下内容的关联。
合约文件应放在哪个目录?
项目目录采用约定结构:根目录下新建 `cadence/contracts` 文件夹,所有 Cadence 合约文件放置其中,例如本教程的 NFT 合约文件命名为 `PinataPartyContract.cdc`。交易脚本放在 `transactions`,查询脚本放在 `scripts`,便于管理。
如何在 flow.json 中配置合约部署?
在 `flow.json` 中添加 `contracts` 节点,指定合约名称与相对路径,例如 `"PinataPartyContract": "./cadence/contracts/PinataPartyContract.cdc"`。随后在 `deployments` 的 `emulator` 部分声明部署账户,例如 `"emulator-account": ["PinataPartyContract"]`,即可让 Flow CLI 在本地模拟器上部署该合约。
NFT 合约中的资源结构是什么?
合约中定义的 NFT 资源只有一个公开字段 `id: UInt64`,在初始化时通过 `init(initID: UInt64)` 赋值。资源接口 `NFTReceiver` 进一步提供 `deposit(token: @NFT, metadata: {String: String})`、`getIDs()`、`idExists(id: UInt64)`、`getMetadata(id: UInt64)` 等方法,用于存放、查询和获取代币的元数据。
相关阅读
- TON FunC 合约安全指南:常见漏洞与最佳实践
- 深入解析 ERC-20:以太坊代币标准及其优势与局限
- Flow基金会与Dapper Labs联手诉韩法院阻止FLOW在韩三大交易所下架
- 2026 Solana开发者训练营:全栈区块链开发实战课程
💡 注册币安使用邀请码 B2345 享平台手续费折扣。详见 币安完整教程。