@@ -15,6 +15,109 @@ timezone: UTC+8
1515## Notes
1616
1717<!-- Content_START -->
18+ # 2025-08-20
19+
20+ 下面以区块链与合约开发者的视角,系统阐述 ERC‑721 的定义、技术细节、实现要点与在合约/DeFi 中的典型用法,并配上若干可直接复用的代码示例,帮助你从标准到工程落地一把过。
21+
22+ 一、定义与背景
23+ - 定义:ERC‑721 是以太坊最早、最广泛使用的“非同质化代币”(NFT)标准,核心特性是“唯一性 + 可确权归属”,每个 tokenId 代表一个不可互换的资产单元。
24+ - 标准目标:
25+ - 标准化拥有者查询、转移、授权与元数据访问的接口;
26+ - 为合约间 NFT 交互提供强约束(如 safeTransfer 的合约接收回调);
27+ - 与钱包、市场、浏览器等基础设施的互操作性。
28+ - 对比 ERC‑20/EIP‑1155:
29+ - ERC‑20 可替代;ERC‑721 完全非同质;
30+ - ERC‑1155 为多代币/半同质模型,适合大规模铸造与批量转移;ERC‑721 更强调 “每个 tokenId 是独立实体”。
31+
32+ 二、核心接口与事件
33+ - IERC165:接口检测
34+ - 合约需实现 supportsInterface(bytes4 interfaceId)
35+ - IERC721:核心
36+ - balanceOf(owner)、ownerOf(tokenId)
37+ - approve(to, tokenId)、getApproved(tokenId)、setApprovalForAll(operator, approved)、isApprovedForAll(owner, operator)
38+ - transferFrom(from, to, tokenId)、safeTransferFrom(from, to, tokenId[ , data] )
39+ - 事件:Transfer、Approval、ApprovalForAll
40+ - IERC721Receiver:合约安全接收回调
41+ - onERC721Received(operator, from, tokenId, data) -> bytes4
42+ - safeTransferFrom 若目标是合约,必须返回正确的 selector,否则 revert
43+ - IERC721Metadata(可选但行业事实标准)
44+ - name()、symbol()、tokenURI(tokenId)
45+ - IERC721Enumerable(可选,不再推荐用于大规模集合)
46+ - tokenOfOwnerByIndex、totalSupply、tokenByIndex(链上枚举成本高)
47+ - 常见扩展
48+ - EIP‑2981: 版税标准(royaltyInfo)
49+ - EIP‑2309: 批量铸造事件(ConsecutiveTransfer)用于部署期大批量 mint 记账
50+ - EIP‑4494: NFT Permit(签名授权,无需先 on-chain approve)
51+ - EIP‑4907: 可租用角色(user/expiry)
52+
53+ 三、实现要点与存储结构
54+ - 典型存储
55+ - mapping(uint256 => address) _ ownerOf
56+ - mapping(address => uint256) _ balanceOf
57+ - mapping(uint256 => address) _ tokenApprovals
58+ - mapping(address => mapping(address => bool)) _ operatorApprovals
59+ - 必备不变量
60+ - ownerOf(tokenId) 非零地址代表已存在
61+ - balanceOf 与 Transfer 事件保持一致性
62+ - 转移后应清空 token 级别的 single-approval
63+ - 转移安全
64+ - 对未知地址优先使用 safeTransferFrom,避免 NFT 被转进不支持的合约导致“黑洞”
65+ - checks-effects-interactions 原则:在对外调用前更新状态,必要时加 ReentrancyGuard
66+ - 元数据
67+ - tokenURI 返回 JSON 元数据 URL(可 HTTP/IPFS/Arweave/data URI)
68+ - 大集合建议 baseURI + tokenId 拼接,或 on-chain SVG/JSON(需注意 gas)
69+ - 构造 tokenId
70+ - 常用自增计数器(如 OpenZeppelin Counters)
71+ - 若需随机/稀有度分布,慎用链上随机(需 VRF/预承诺等抗操纵机制)
72+ - Gas/可扩展性
73+ - 尽量避免 IERC721Enumerable;链上枚举昂贵
74+ - 大规模集合:考虑 EIP‑2309、稀疏存储压缩、或使用优化实现(如批量 mint 的 gas 模式、稀疏 ownership 记录)
75+ - 升级性:使用代理(UUPS/Transparent),注意构造函数逻辑迁移到 initializer
76+
77+ 四、可用 ERC‑721(基于 OpenZeppelin)
78+ 包含:安全转移、BaseURI、仅拥有者铸造
79+
80+ ``` solidity:contracts/MyNFT.sol
81+ // SPDX-License-Identifier: MIT
82+ pragma solidity ^0.8.20;
83+
84+ import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
85+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
86+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
87+
88+ contract MyNFT is ERC721, Ownable {
89+ using Strings for uint256;
90+
91+ string private _baseTokenURI;
92+ uint256 private _nextId;
93+
94+ constructor(string memory name_, string memory symbol_, string memory baseURI_) ERC721(name_, symbol_) Ownable(msg.sender) {
95+ _baseTokenURI = baseURI_;
96+ _nextId = 1;
97+ }
98+
99+ function setBaseURI(string calldata baseURI_) external onlyOwner {
100+ _baseTokenURI = baseURI_;
101+ }
102+
103+ function mint(address to) external onlyOwner returns (uint256 tokenId) {
104+ tokenId = _nextId++;
105+ _safeMint(to, tokenId); // 使用 safeMint,自动调用 onERC721Received
106+ }
107+
108+ function _baseURI() internal view override returns (string memory) {
109+ return _baseTokenURI;
110+ }
111+
112+ // 如果需要自定义 tokenURI,可覆盖如下
113+ function tokenURI(uint256 tokenId) public view override returns (string memory) {
114+ _requireOwned(tokenId);
115+ string memory base = _baseURI();
116+ return bytes(base).length > 0 ? string(abi.encodePacked(base, tokenId.toString(), ".json")) : "";
117+ }
118+ }
119+ ```
120+
18121# 2025-08-19
19122
20123# 1. EIP-20
0 commit comments