NFT(Non-Fungible Token,非同质化代币)是一种基于区块链技术的数字资产,其核心特点是唯一性、不可分割性和可验证性。以下是详细解释:
1. 定义与核心特性
- 非同质化:与比特币、以太币等同质化代币(可互换)不同,每个NFT都是独一无二的,无法被其他NFT直接替代。
- 区块链技术:NFT通常基于以太坊等区块链平台发行,其所有权、交易记录等信息永久存储在区块链上,确保透明且不可篡改。
- 数字所有权证明:NFT代表对特定数字资产(如图片、音乐、视频、游戏道具等)的所有权,类似数字世界的"房产证"。
2. 常见应用场景
- 数字艺术品:艺术家将作品转化为NFT,通过拍卖或直接销售获得收益。例如,Beeple的数字画作《Everydays: The First 5000 Days》以6900万美元成交。
- 收藏品:如加密猫(CryptoKitties)、NBA Top Shot(NBA球星高光时刻NFT)等,用户可收集、交易虚拟物品。
- 游戏资产:游戏中的角色、装备、土地等可转化为NFT,玩家真正拥有并可跨游戏使用。
- 音乐与影视:音乐人发行限量版NFT专辑,粉丝购买后获得独家内容或权益。
- 虚拟世界:在Decentraland等元宇宙平台中,NFT代表虚拟土地、建筑或装饰品。
- 身份与认证:NFT可用于证明学历、会员资格、票务等,防止伪造。
3. 工作原理
- 创建(Mint):艺术家或开发者将数字文件上传至区块链平台,生成唯一的NFT,并设定规则(如版税比例)。
- 交易:NFT可在加密货币交易所或专用市场(如OpenSea)买卖,交易记录公开可查。
- 所有权转移:购买者通过加密钱包接收NFT,成为其唯一所有者,可转售或展示。
4. 优势与争议
- 优势 :
- 确权与溯源:解决数字内容复制难题,保护创作者权益。
- 增值潜力:稀缺性可能推动NFT价值上涨,吸引投资者。
- 创新商业模式:创作者可通过版税持续获得收益(如每次转售抽取一定比例)。
- 争议 :
- 环境问题:部分区块链(如以太坊)能耗高,引发环保质疑。
- 市场泡沫:部分NFT价格虚高,存在投机炒作风险。
- 版权纠纷:NFT化可能涉及未经授权使用他人作品。
5. 简单类比
想象你拥有一幅名画,但它是数字形式的(如一张JPEG图片)。通过NFT技术,你可以:
- 证明这是"原版"而非复制品;
- 在区块链上记录你拥有它;
- 自由出售或展示,甚至设置未来每次转售时你都能获得分成。
总结
NFT是数字世界中的"唯一凭证",它重新定义了数字资产的所有权与交易方式,为创作者、收藏家和投资者提供了新机遇,但同时也需警惕市场风险与伦理问题。
NFT 核心架构图解
在开始代码之前,我们需要建立正确的心理模型。NFT 的本质不仅仅是一张图片,它是一个 "指向数据的权属证明"。
- Token ID : 链上唯一的标识符(如
#1024)。 - Owner: 链上记录的持有者地址。
- Metadata (元数据): 包含图片、名称、属性的 JSON 文件,通常存储在 IPFS 上。
- TokenURI: 智能合约中存储的一个字符串,指向 Metadata 的位置。
阶段一:元数据 (Metadata) ------ NFT 的灵魂
在铸造(Mint)之前,你需要准备好 NFT 的内容。为了去中心化,我们通常不把图片放在中心化服务器(AWS/阿里云),而是放在 IPFS。
1. 标准 Metadata 格式 (JSON)
这是 OpenSea 和主流市场通用的标准:
json
{
"name": "CyberPunk #2077",
"description": "这是一个来自未来的赛博朋克战士。",
"image": "ipfs://QmYourImageHash...",
"attributes": [
{ "trait_type": "Background", "value": "Neon City" },
{ "trait_type": "Weapon", "value": "Katana" },
{ "trait_type": "Level", "value": 5 }
]
}
💡 关键点:合约里存的只是上面这个 JSON 文件的链接(TokenURI),而不是 JSON 内容本身。
阶段二:Mint (铸造) ------ 资产诞生
铸造是将数据写入区块链的过程。我们将使用 Solidity 和 OpenZeppelin 库(行业安全标准)。
Solidity 合约示例
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract ProNFT is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("ProfessionalNFT", "PNFT") {}
// 铸造函数
function mintNFT(address recipient, string memory tokenURI)
public
onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
// 1. 铸造给指定地址
_safeMint(recipient, newItemId);
// 2. 设置元数据链接
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}
阶段三:Approve (授权) ------ 交易的前提
这是新手最容易困惑的地方。
为什么卖 NFT 时需要先"授权"?
因为智能合约(如 OpenSea 的合约)不能直接拿走你钱包里的 NFT。你必须先执行 approve 操作,告诉 NFT 合约:"我允许 OpenSea 支配我的这个 Token"。
两种授权方式:
- 单个授权 (
approve): 只允许操作特定的 Token ID。 - 全员授权 (
setApprovalForAll): 允许操作该系列下你拥有的所有 NFT(省 Gas,OpenSea 常用此法)。
Solidity 接口定义
solidity
// 允许 operator 地址挪动你的 tokenId
function approve(address operator, uint256 tokenId) external;
// 允许 operator 挪动你所有的 NFT
function setApprovalForAll(address operator, bool _approved) external;
阶段四:Transfer (转移) ------ 所有权变更
转移有两种方式,但在开发中强烈建议 只使用 safeTransferFrom。
transferFrom: 暴力转账。如果接收方是一个无法处理 NFT 的合约,NFT 可能会永久丢失。safeTransferFrom: 安全转账。它会检查接收方是否实现了onERC721Received接口,防止资产锁死。
Python (Web3.py) 调用示例:
python
# 假设这是从 Alice 转给 Bob
tx = contract.functions.safeTransferFrom(
alice_address, # From
bob_address, # To
1 # Token ID
).transact({"from": alice_address})
阶段五:Marketplace (交易) ------ 买卖逻辑
NFT 本身没有"价格"属性。交易功能是由市场合约(Marketplace Contract)实现的。
简易市场交易逻辑(原子化交易):
- Listing (挂单): 卖家授权市场合约,并设定价格(存储在市场合约的数据结构中)。
- Buying (购买) : 买家调用市场合约的
buy函数并附带 ETH。 - Settlement (结算) : 市场合约同时做两件事:
- 把 ETH 转给卖家。
- 把 NFT 从卖家转给买家(利用之前的授权)。
核心 Solidity 代码片段
solidity
// 这是一个简化的市场购买函数
function buyNFT(address nftContract, uint256 tokenId) external payable {
// 1. 获取上架信息
Listing memory item = listings[nftContract][tokenId];
require(item.price > 0, "Not for sale");
require(msg.value >= item.price, "Insufficient funds");
// 2. 结算资金 (买家 -> 卖家)
payable(item.seller).transfer(item.price);
// 3. 转移 NFT (卖家 -> 买家)
// 注意:这里市场合约调用 NFT 合约,将 NFT 从 Seller 账户拉取并给 Buyer
IERC721(nftContract).safeTransferFrom(item.seller, msg.sender, tokenId);
// 4. 清除上架状态
delete listings[nftContract][tokenId];
}
阶段六:Swap (交换) ------ 以物易物
Swap 是指 NFT 换 NFT,或者 NFT 换 ERC20 Token。去中心化交换的核心是互信 。我们不需要互信,我们需要一个Swap 合约作为中介。
流程:
- User A 授权 Swap 合约操作 NFT A。
- User B 授权 Swap 合约操作 NFT B。
- 任意一方调用
swap函数,合约同时执行两个transferFrom。
solidity
function swap(
address nftA_Contract, uint256 idA, address ownerA,
address nftB_Contract, uint256 idB, address ownerB
) external {
// 这是一个原子操作,要么全成功,要么全失败
IERC721(nftA_Contract).transferFrom(ownerA, ownerB, idA);
IERC721(nftB_Contract).transferFrom(ownerB, ownerA, idB);
}
实战演练:完整的 Python 交互脚本
先部署一个 MyNFT 合约。使用 Python (web3.py) 走完铸造 -> 授权 -> 转移全流程的代码。
1. 环境准备
你需要安装 web3 库:
pip install web3
python
from web3 import Web3
# 1. 连接区块链 (这里使用本地test节点)
w3 = Web3(Web3.EthereumTesterProvider())
if not w3.is_connected():
raise Exception("无法连接到区块链节点")
# 模拟的三个用户地址 (使用测试节点的前三个账户)
admin = w3.eth.accounts[0] # 铸造者
alice = w3.eth.accounts[1] # 主要持有人
bob = w3.eth.accounts[2] # 市场/操作员
还需要一个NFT合约,代码如下:
solidity
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.5.0
pragma solidity ^0.8.27;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC721Burnable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract MyNFT is ERC721, ERC721URIStorage, ERC721Burnable, Ownable {
uint256 private _nextTokenId;
constructor(address initialOwner)
ERC721("MyNFT", "MT")
Ownable(initialOwner)
{}
function mint(address to, string memory uri)
public
onlyOwner
returns (uint256)
{
uint256 tokenId = _nextTokenId++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
return tokenId;
}
// The following functions are overrides required by Solidity.
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721URIStorage)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
python
# 先部署合约,然后获取合约地址和ABI
from solcx import install_solc, set_solc_version
from solcx import compile_files
install_solc(version='0.8.30')
set_solc_version('0.8.30')
compiled_sol = compile_files([
"contracts/MyNFT.sol"
], import_remappings=["@openzeppelin=openzeppelin-contracts"])
contract_id, contract_interface = compiled_sol.popitem()
# get bytecode / bin
bytecode = contract_interface['bin']
# get abi
abi = contract_interface['abi']
MyNFTContract = w3.eth.contract(abi=abi, bytecode=bytecode)
tx_hash = MyNFTContract.constructor(admin).transact({'from': admin})
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
nft_contract = w3.eth.contract(address=tx_receipt.contractAddress, abi=abi)
python
import base64
import json
print("\n--- NFT ERC721 全流程模拟开始 ---")
print(f"Admin (铸造者): {admin}")
print(f"Alice (持有人): {alice}")
print(f"Bob (操作员): {bob}\n")
# ----------------------------------------------------
# 辅助函数
# ----------------------------------------------------
def encode_metadata_json(metadata_dict):
"""将 NFT Metadata 转成 base64 data URI"""
json_str = json.dumps(metadata_dict)
encoded = base64.b64encode(json_str.encode()).decode()
return f"data:application/json;base64,{encoded}"
def transact_and_wait(func, from_addr):
"""发送交易并等待确认"""
tx_hash = func.transact({"from": from_addr})
return w3.eth.wait_for_transaction_receipt(tx_hash)
def check_status(step_name, token_id=None):
"""打印当前状态的辅助函数"""
print(f"\n--- {step_name} ---")
print(f"Alice Balance: {nft_contract.functions.balanceOf(alice).call()}")
print(f"Bob Balance: {nft_contract.functions.balanceOf(bob).call()}")
if token_id is not None:
try:
owner = nft_contract.functions.ownerOf(token_id).call()
uri = nft_contract.functions.tokenURI(token_id).call()
approved = nft_contract.functions.getApproved(token_id).call()
print(f"Token {token_id} Owner: {owner}")
print(f"Token {token_id} URI: {uri}")
print(f"Token {token_id} Approved: {approved}")
except:
print(f"Token {token_id} 不存在或已被销毁。")
print(f"Alice 授权 Bob: {nft_contract.functions.isApprovedForAll(alice, bob).call()}")
# ----------------------------------------------------
# 步骤 1: 检查 name / symbol
# ----------------------------------------------------
print("name:", nft_contract.functions.name().call())
print("symbol:", nft_contract.functions.symbol().call())
# ----------------------------------------------------
# 步骤 2: Admin 铸造 Token 1 给 Alice
# ----------------------------------------------------
nft_metadata = {
"name": "CyberPunk #2077",
"description": "来自未来的赛博朋克战士装备。",
"image": "ipfs://QmImageHash...",
"attributes": [
{"trait_type": "Background", "value": "Neon City"},
{"trait_type": "Weapon", "value": "Katana"},
{"trait_type": "Level", "value": 5}
]
}
token_uri = encode_metadata_json(nft_metadata)
transact_and_wait(
nft_contract.functions.mint(alice, token_uri),
admin
)
check_status("2. Admin 铸造 Token ID 1 给 Alice", 0) # tokenId = 0 (从 0 开始)
# ----------------------------------------------------
# 步骤 3: Alice 授权 Bob 操作 Token ID 0
# ----------------------------------------------------
transact_and_wait(
nft_contract.functions.approve(bob, 0),
alice
)
check_status("3. Alice 授权 Bob 操作 Token 0", 0)
# ----------------------------------------------------
# 步骤 4: Bob 转移 Token 0 给自己
# ----------------------------------------------------
transact_and_wait(
nft_contract.functions.transferFrom(alice, bob, 0),
bob
)
check_status("4. Bob 将 Token 0 转给自己", 0)
# ----------------------------------------------------
# 步骤 5: Bob 授权 Admin 为全局操作员
# ----------------------------------------------------
transact_and_wait(
nft_contract.functions.setApprovalForAll(admin, True),
bob
)
check_status("5. Bob 设置 Admin 为全局 Operator", 0)
# ----------------------------------------------------
# 步骤 6: Admin 再铸造 Token 1 给 Bob
# ----------------------------------------------------
transact_and_wait(
nft_contract.functions.mint(bob, token_uri),
admin
)
check_status("6. Admin 铸造 Token 1 给 Bob", 1)
# ----------------------------------------------------
# 步骤 7: Admin (operator) 安全转移 Token 1 给 Alice
# ----------------------------------------------------
transact_and_wait(
nft_contract.functions.safeTransferFrom(bob, alice, 1),
admin
)
check_status("7. Operator Admin 将 Token 1 转给 Alice", 1)
# ----------------------------------------------------
# 步骤 8: Alice 销毁 Token 1
# ----------------------------------------------------
transact_and_wait(
nft_contract.functions.burn(1),
alice
)
check_status("8. Alice 销毁 Token 1")
# ----------------------------------------------------
# 步骤 9: Bob 撤销 Admin 的操作员权限
# ----------------------------------------------------
transact_and_wait(
nft_contract.functions.setApprovalForAll(admin, False),
bob
)
check_status("9. Bob 撤销 Admin 的 Operator 权限", 0)
print("\n--- 完整 NFT 流程测试结束 ---")
--- NFT ERC721 全流程模拟开始 ---
Admin (铸造者): 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
Alice (持有人): 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
Bob (操作员): 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
name: MyNFT
symbol: MT
--- 2. Admin 铸造 Token ID 1 给 Alice ---
Alice Balance: 1
Bob Balance: 0
Token 0 Owner: 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
Token 0 URI: data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False
--- 3. Alice 授权 Bob 操作 Token 0 ---
Alice Balance: 1
Bob Balance: 0
Token 0 Owner: 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
Token 0 URI: data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Alice 授权 Bob: False
--- 4. Bob 将 Token 0 转给自己 ---
Alice Balance: 0
Bob Balance: 1
Token 0 Owner: 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Token 0 URI: data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False
--- 5. Bob 设置 Admin 为全局 Operator ---
Alice Balance: 0
Bob Balance: 1
Token 0 Owner: 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Token 0 URI: data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False
--- 6. Admin 铸造 Token 1 给 Bob ---
Alice Balance: 0
Bob Balance: 2
Token 1 Owner: 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Token 1 URI: data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 1 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False
--- 7. Operator Admin 将 Token 1 转给 Alice ---
Alice Balance: 1
Bob Balance: 1
Token 1 Owner: 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
Token 1 URI: data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 1 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False
--- 8. Alice 销毁 Token 1 ---
Alice Balance: 0
Bob Balance: 1
Alice 授权 Bob: False
--- 9. Bob 撤销 Admin 的 Operator 权限 ---
Alice Balance: 0
Bob Balance: 1
Token 0 Owner: 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Token 0 URI: data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False
--- 完整 NFT 流程测试结束 ---
你现在已经有了一个完整的 NFT 交易流程
包含:
| 步骤 | 功能 |
|---|---|
| 1 | 查看 name / symbol |
| 2 | mint(内置 JSON metadata → base64) |
| 3 | approve(单授权) |
| 4 | transferFrom(被授权者转移) |
| 5 | setApprovalForAll(市场授权) |
| 6 | 再次 mint |
| 7 | safeTransferFrom(操作员转移) |
| 8 | burn(销毁) |
| 9 | revoke operator(撤销授权) |
补充:meta data通常是用 IPFS(nft.storage / pinata)或去中心化方案进行存储的,而不是直接用 base64 存在链上,这里为了简化演示才这么做的。IPFS介绍可以参考另一篇博客