Day 22
NFT技术标准:ERC721与ERC1155深度对比
深入理解NFT技术标准ERC721和ERC1155的设计原理、应用场景、优劣对比,掌握NFT产品设计基础
2025-01-31
NFTERC721ERC1155智能合约Week4
Day 22: NFT 标准 - ERC721 与 ERC1155
Week 4 学习路径
Week 4: NFT与新范式
├── Day 22: ERC721/1155标准对比 ✅ ← 今天
├── Day 23: NFT市场机制(版税/稀有度)
├── Day 24: 账户抽象(AA) ERC4337
├── Day 25: 社交恢复、Gas代付
├── Day 26: The Graph Subgraph原理
├── Day 27: 集成Subgraph到项目
└── Day 28: Uniswap产品分析文章核心概念
什么是 NFT?
> 类比理解:如果 ERC20 代币像钞票(每张100元都一样),那 NFT 就像艺术品或房产证——每一个都是独一无二的,有自己的编号和属性。NFT = Non-Fungible Token = 非同质化代币。
Token 类型对比
═══════════════════════════════════════════════════════════
同质化代币 (Fungible Token) - ERC20
├── 每个代币完全相同
├── 可以分割(0.5 ETH)
├── 互相可替换
└── 例:ETH, USDC, UNI
非同质化代币 (Non-Fungible Token) - ERC721
├── 每个代币独一无二
├── 不可分割(只能整个转移)
├── 不可互换(每个都不同)
└── 例:CryptoPunks, BAYC, 域名
半同质化代币 (Semi-Fungible Token) - ERC1155
├── 同类型代币相同,不同类型不同
├── 支持批量操作
├── 更灵活
└── 例:游戏道具、门票
═══════════════════════════════════════════════════════════ERC721 标准详解
核心接口
// ERC721 核心接口
interface IERC721 {
// 查询余额
function balanceOf(address owner) external view returns (uint256);
// 查询所有者
function ownerOf(uint256 tokenId) external view returns (address);
// 安全转移
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
// 普通转移
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
// 授权
function approve(address to, uint256 tokenId) external;
// 查询授权
function getApproved(uint256 tokenId) external view returns (address);
// 批量授权
function setApprovalForAll(address operator, bool approved) external;
// 查询批量授权
function isApprovedForAll(
address owner,
address operator
) external view returns (bool);
// 事件
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
}ERC721 数据结构
ERC721 存储结构
═══════════════════════════════════════════════════════════
核心映射:
┌─────────────────────────────────────────────────────────┐
│ mapping(uint256 => address) private _owners; │
│ // tokenId => 所有者地址 │
│ │
│ mapping(address => uint256) private _balances; │
│ // 地址 => 持有数量 │
│ │
│ mapping(uint256 => address) private _tokenApprovals; │
│ // tokenId => 授权地址 │
│ │
│ mapping(address => mapping(address => bool)) │
│ private _operatorApprovals; │
│ // 所有者 => 操作者 => 是否授权 │
└─────────────────────────────────────────────────────────┘
每个 Token 独立存储:
Token #1 → Owner: 0xAlice
Token #2 → Owner: 0xBob
Token #3 → Owner: 0xAlice
...
查询某人持有的所有 NFT 需要遍历(Gas 高)
═══════════════════════════════════════════════════════════ERC721 Metadata 扩展
// 元数据接口 - 让 NFT 有名字和图片
interface IERC721Metadata {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
// tokenURI 返回 JSON 地址,格式如:
// ipfs://QmXxx.../1.json
// 或 https://api.project.com/token/1
// JSON 内容标准格式:
{
"name": "Cool Cat #1234",
"description": "A very cool cat",
"image": "ipfs://QmXxx.../1.png",
"attributes": [
{"trait_type": "Background", "value": "Blue"},
{"trait_type": "Fur", "value": "Golden"},
{"trait_type": "Eyes", "value": "Laser"}
]
}ERC1155 标准详解
为什么需要 ERC1155?
ERC721 的局限性
═══════════════════════════════════════════════════════════
问题1:每个 Token 独立合约调用
├── 批量转移 10 个 NFT = 10 次交易
├── Gas 成本高
└── 用户体验差
问题2:不支持同质化场景
├── 游戏里 1000 把相同的剑怎么办?
├── 每把剑都要独立 tokenId?
└── 浪费存储空间
问题3:一个合约只能一种资产
├── 想发行多种 NFT 需要多个合约
└── 管理复杂
ERC1155 解决方案:
├── 支持批量操作
├── 同一合约支持多种资产(FT + NFT)
└── 更省 Gas
═══════════════════════════════════════════════════════════ERC1155 核心接口
interface IERC1155 {
// 查询余额(某地址某类型代币的数量)
function balanceOf(
address account,
uint256 id
) external view returns (uint256);
// 批量查询余额
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
// 授权
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
// 单个转移
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) external;
// 批量转移 - 核心优势!
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
// 事件
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 value
);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
}ERC1155 数据结构
ERC1155 存储结构
═══════════════════════════════════════════════════════════
核心映射:
┌─────────────────────────────────────────────────────────┐
│ mapping(uint256 => mapping(address => uint256)) │
│ private _balances; │
│ // tokenId => 地址 => 数量 │
│ │
│ 例: │
│ _balances[1][Alice] = 100 // Alice 有 100 个 Token#1 │
│ _balances[1][Bob] = 50 // Bob 有 50 个 Token#1 │
│ _balances[2][Alice] = 1 // Alice 有 1 个 Token#2 │
└─────────────────────────────────────────────────────────┘
灵活性:
├── Token#1 可以是同质化的(供应量 10000)
├── Token#2 可以是非同质化的(供应量 1)
└── 同一个合约,不同类型共存
游戏场景示例:
├── Token#1: 金币(同质化,每人可有多个)
├── Token#2: 普通剑(半同质化,同款可有多把)
├── Token#1001: 传说武器(NFT,全服唯一)
└── 一个合约搞定所有道具!
═══════════════════════════════════════════════════════════ERC721 vs ERC1155 对比
功能对比
核心差异对比
═══════════════════════════════════════════════════════════
│ 特性 │ ERC721 │ ERC1155 │
├────────────────────────────────────────────────────────────┤
│ 代币类型 │ 只支持 NFT │ FT + NFT 都支持 │
│ 批量转移 │ ❌ 不支持 │ ✅ 原生支持 │
│ 批量查询 │ ❌ 不支持 │ ✅ 原生支持 │
│ Gas 效率 │ 较高 │ 更低(批量时) │
│ 合约复杂度 │ 简单 │ 较复杂 │
│ 单独授权 │ ✅ 支持 │ ❌ 只有全局授权 │
│ ownerOf │ ✅ 有 │ ❌ 没有 │
│ 生态支持 │ 更广泛 │ 逐渐增加 │
│ OpenSea 支持 │ ✅ 完全 │ ✅ 完全 │
└────────────────────────────────────────────────────────────┘
═══════════════════════════════════════════════════════════场景选择
如何选择标准?
═══════════════════════════════════════════════════════════
选择 ERC721:
├── 每个代币必须完全独特
├── 头像 PFP 项目(BAYC、Azuki)
├── 艺术品 1/1
├── 域名(ENS)
├── 需要单独授权功能
└── 生态兼容性要求高
选择 ERC1155:
├── 游戏道具(多种类型 + 可堆叠)
├── 门票/会员卡(同类型多份)
├── 需要批量操作节省 Gas
├── 一个合约管理多种资产
├── 音乐/版权(多版次发行)
└── SFT 半同质化场景
实际案例:
├── CryptoPunks: 早期,非标准实现
├── BAYC: ERC721
├── Uniswap V3 LP: ERC721(每个仓位独特)
├── OpenSea Storefront: ERC1155
├── Enjin 游戏道具: ERC1155
└── Aavegotchi: ERC721 + ERC1155 混合
═══════════════════════════════════════════════════════════Gas 对比
Gas 成本对比(估算)
═══════════════════════════════════════════════════════════
单次转移:
├── ERC721 transferFrom: ~65,000 gas
└── ERC1155 safeTransferFrom: ~52,000 gas
节省约 20%
批量转移 10 个:
├── ERC721: 10 × 65,000 = 650,000 gas
└── ERC1155 safeBatchTransferFrom: ~120,000 gas
节省约 80%!
Mint 操作:
├── ERC721 单个 mint: ~95,000 gas
├── ERC721 批量 mint 10 个: ~500,000 gas
└── ERC1155 批量 mint 10 个: ~150,000 gas
结论:
├── 单次操作:差异不大
└── 批量操作:ERC1155 显著更省
═══════════════════════════════════════════════════════════链上实操:Mint 一个免费 NFT
方法 1:使用测试网 Mint
在测试网体验 NFT Mint
═══════════════════════════════════════════════════════════
1. 准备工作
├── MetaMask 切换到 Sepolia 测试网
├── 确保有测试网 ETH(从 faucet 领取)
└── sepoliafaucet.com 或 Google "Sepolia faucet"
2. 找一个测试网 NFT 项目
├── 访问 testnets.opensea.io
└── 搜索免费 mint 的 NFT
3. Mint 操作
├── 连接钱包
├── 点击 Mint
├── 确认交易
└── 等待确认
4. 查看你的 NFT
├── 在 OpenSea 个人页面查看
└── 或在 Etherscan 查看交易详情
═══════════════════════════════════════════════════════════方法 2:阅读 NFT 合约
在 Etherscan 阅读 NFT 合约
═══════════════════════════════════════════════════════════
1. 打开知名 NFT 合约
BAYC: 0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D
2. 点击 Contract → Read Contract
3. 尝试查询:
├── name(): 返回 "BoredApeYachtClub"
├── symbol(): 返回 "BAYC"
├── totalSupply(): 返回 10000
├── ownerOf(1): 返回 Token#1 的所有者
└── tokenURI(1): 返回元数据地址
4. 点击 tokenURI 返回的链接
└── 查看 JSON 格式的 NFT 属性
5. 思考问题:
├── 图片存在哪里?(IPFS/中心化服务器)
├── 如果服务器关闭会怎样?
└── 如何保证 NFT 的永久性?
═══════════════════════════════════════════════════════════今日思考
PM 视角的 NFT 产品设计
1. 存储问题
- 图片存 IPFS 还是中心化服务器?
- 元数据可以修改吗?
- 用户能接受图片加载慢吗?
2. Gas 优化
- 如何降低用户 Mint 成本?
- 批量操作用 ERC1155?
- Lazy Minting(延迟铸造)?
3. 用户体验
- 非 Crypto 用户如何理解 NFT?
- 如何解释 Gas 费用?
- 钱包连接流程如何简化?
面试题准备
问题:ERC721 和 ERC1155 有什么区别?如何选择?
30秒版本:
═══════════════════════════════════════════════════════════
ERC721 是 NFT 标准,每个代币独一无二;ERC1155 是多代币标准,
支持同质化和非同质化代币共存,并且支持批量操作更省 Gas。
选择建议:独特的 PFP/艺术品用 721,游戏道具/门票等需要批量
操作的用 1155。
═══════════════════════════════════════════════════════════
2分钟版本:
═══════════════════════════════════════════════════════════
核心区别:
ERC721:
- 每个 tokenId 对应唯一的所有者
- 有 ownerOf() 函数查询所有者
- 不支持批量转移
- 适合独特资产:头像、艺术品、域名
ERC1155:
- 每个 tokenId 可以有多个持有者和数量
- 用 balanceOf(address, id) 查询
- 支持批量转移,批量时节省 80% Gas
- 适合游戏道具、门票、版权等
实际案例:
- BAYC、CryptoPunks:ERC721(每只猴子独特)
- Uniswap V3 LP:ERC721(每个仓位独特)
- 游戏道具:ERC1155(同款剑可以有多把)
- OpenSea Storefront:ERC1155
产品选择考量:
1. 资产是否必须完全独特?
2. 是否需要批量操作?
3. 是否需要同一合约管理多种资产?
4. 生态兼容性要求?
大多数 PFP 项目选 ERC721,因为每个头像的唯一性是核心价值。
游戏类项目选 ERC1155,因为需要多种道具和批量操作。
═══════════════════════════════════════════════════════════学习资源
| 资源 | 链接 | 说明 |
|---|---|---|
| EIP-721 | eips.ethereum.org/EIPS/eip-721 | 官方标准 |
| EIP-1155 | eips.ethereum.org/EIPS/eip-1155 | 官方标准 |
| OpenZeppelin | docs.openzeppelin.com | 标准实现库 |
| OpenSea 文档 | docs.opensea.io | NFT 市场标准 |
明日预告
Day 23: NFT 市场机制
├── 版税机制 (EIP-2981)
├── 稀有度计算方法
├── OpenSea vs Blur 对比
├── NFT 市场产品分析
└── 体验 OpenSea/Blur