NFT(非同质化代币)不是"数字图片"的同义词,而是链上唯一性证明的通用标准。从游戏道具到房地产凭证,从学位证书到供应链溯源,ERC-721 和 ERC-1155 提供了一套在无需中心机构的情况下,证明"某物的唯一所有权与该所有权转移历史"的基础设施。
9.4.1 非同质化vs同质化:底层数学
同质化代币(ERC-20)的集合论本质:
非同质化代币(ERC-721)的本质:
关键差异:ERC-20 的余额是一个标量(uint256 balance),ERC-721 的"余额"是一个从 tokenId 到所有者的映射(mapping(uint256 => address) ownerOf)。
9.4.2 ERC-721:唯一性映射的标准
核心状态结构:
ts
// 概念性 ERC-721 状态映射
interface ERC721State {
tokenId: number;
owner: string; // 当前所有者地址
approved: string; // 单地址授权(一次性转移)
metadataUri: string; // 指向 JSON 元数据的 URI
}标准接口:
balanceOf(address):查询某地址持有的 NFT 数量;ownerOf(tokenId):查询特定 NFT 的当前所有者;transferFrom(from, to, tokenId):转移(需经授权或是所有者);safeTransferFrom:转移 + 检查接收方是否为可接收合约。
sequenceDiagram
participant M as 铸造者
participant C as ERC-721 合约
participant A as Alice
participant B as Bob
M->>C: mint(tokenId=42, to=Alice, uri="ipfs://...")
C->>C: ownerOf[42] = Alice
A->>C: approve(Bob, 42)
C->>C: approved[42] = Bob
B->>C: transferFrom(Alice, Bob, 42)
C->>C: require(msg.sender==Bob||approved[42]==Bob)
C->>C: ownerOf[42] = Bob
C->>C: approved[42] = 0x0
9.4.3 ERC-1155:半同质化与批量管理
ERC-1155 解决了 ERC-20 + ERC-721 并存时的冗余问题:
一个游戏玩家可能同时拥有 1000 个金币(同质)和 1 把传奇剑(非同质)。在 ERC-20/721 时代,这需要两个不同的合约;在 ERC-1155 中,一个合约用 tokenId 区分类型,用 amount 区分数量。
核心映射:
tokenId = 1:金币,amount = 1000;tokenId = 42:传奇剑,amount = 1(也可 >1 表示有一定供应限制的半同质资产)。
关键优势:
- 批量转移:一次性转移多种资产到多个地址,Gas 效率提升 10-100 倍;
- 单合约多资产:降低管理复杂度;
- 混合型资产:同时支持同质与半同质。
graph LR
A[ERC-20
仅同质] --> B[ERC-721
仅非同质]
A --> C[ERC-1155
混合]
B --> C
style A fill:#ffffcc
style B fill:#ccffcc
style C fill:#99ddff
9.4.4 NFT 的链上确权与链下元数据
NFT 本身不存储图片或数据——它只存储一个指向链下资源的 URI。这使得:
- 可扩展性:链上数据量恒定(仅需 32 字节的
tokenURI),图片/视频存储在 IPFS/Arweave; - 灵活性:元数据可以更新(如果合约设计允许);
- 风险:若 IPFS 节点全部下线,或中心化服务器关闭,
tokenURI变为"死链接"。
可验证的元数据机制:许多项目使用链上哈希验证:
ts
// nft-ownership-verification.ts
// 纯内置:模拟 ERC-721 所有权链证明与元数据哈希验证
interface NftRecord {
tokenId: number;
owner: string;
metadataHash: bigint; // keccak256 的链上存储
history: { from: string; to: string; block: number }[];
}
class NftRegistry {
tokens = new Map<number, NftRecord>();
mint(tokenId: number, to: string, metadataHash: bigint): void {
this.tokens.set(tokenId, { tokenId, owner: to, metadataHash, history: [{ from: '0x0', to, block: 1 }] });
}
transfer(tokenId: number, from: string, to: string, block: number): boolean {
const rec = this.tokens.get(tokenId);
if (!rec || rec.owner !== from) return false;
rec.owner = to;
rec.history.push({ from, to, block });
return true;
}
verifyMetadata(tokenId: number, content: string): boolean {
// 纯内置哈希: DJB2 模拟 keccak256 的链上验证行为
let hash = 5381n;
for (let i = 0; i < content.length; i++) {
hash = ((hash << 5n) + hash) + BigInt(content.charCodeAt(i));
}
const rec = this.tokens.get(tokenId);
return rec ? rec.metadataHash === hash : false;
}
provenance(tokenId: number): NftRecord | null { return this.tokens.get(tokenId) || null; }
}
const registry = new NftRegistry();
registry.mint(42, '0xAlice', 12345n);
registry.transfer(42, '0xAlice', '0xBob', 100);
registry.transfer(42, '0xBob', '0xCarol', 200);
console.log('所有权链:', registry.provenance(42)?.history.map(h => `{h.to}@#${h.block}`).join(', '));
console.log('元数据验证:', registry.verifyMetadata(42, 'fake') ? '正确' : '不匹配');
// 输出: 完整的所有权转移历史和元数据链上验证9.4.5 超出"数字图片":NFT 的扩展用例
| 用例 | 机制 | 代表 |
|---|---|---|
| 灵魂绑定代币(SBT) | 不可转让的 NFT,绑定到身份 | 学术证书、资质证明 |
| RWA 代币化 | 链上 NFT 代表链下实物资产 | 房地产、发票、碳信用 |
| 动态 NFT | 元数据/图像随链下事件变化 | 游戏角色、体育赛事 |
| 版税自动分配 | 每次转售时向创作者输送费用 | EIP-2981 标准 |
| 碎片化 | 将高价值 NFT 拆分为 ERC-20 | 允许小额共同拥有 |
关键认知四:NFT 的"所有权"不是拥有 JPEG 文件,而是链上社会对"tokenId X 归地址 Y 所有"这一映射的共识。URI 指向的媒体文件可能被复制一万次,但只有持有对应 tokenId 的私钥才能在市场交易它或在游戏中使用它。
← 9.3 稳定币 | 前往 → 9.5 去中心化身份
评论
0评论加载中…