EIP-721|Standards Track: ERC|狀態 Final|建立 2018-01-24|Requires: EIP-165
作者:William Entriken、Dieter Shirley、Jacob Evans、Nastassia Sachs|授權 CC0
規格原文:https://eips.ethereum.org/EIPS/eip-721
介面 ID:ERC721=0x80ac58cd/ERC721Metadata=0x5b5e139f/ERC721Enumerable=0x780e9d63/ERC721TokenReceiver=0x150b7a02
🎯 什麼情境該想到我
當你「要實作或串接 NFT,需要知道轉移的三條路徑與安全轉移的確切規則」的時候。
概念層(NFT 是什麼、跟同質化差在哪)在 工具-代幣標準與DApp——但那張是 2018 提案版(用 deed/deedUri),介面已經改了,實作照這張。
⚙️ 怎麼用(步驟 / 公式)
必須同時實作 ERC721 與 ERC165
supportsInterface(bytes4) 是查詢入口,規格要求它用少於 30,000 gas。
所有權查詢
| 函式 | 語義 |
|---|---|
balanceOf(address _owner) | 該地址擁有幾個 NFT。查零地址必須 throw |
ownerOf(uint256 _tokenId) | 誰擁有這個 NFT。無效 NFT 必須 throw |
指派給零地址的 NFT 一律視為無效。
授權有兩層(這是與 ERC-20 最大的結構差異)
| 層級 | 函式 | 範圍 |
|---|---|---|
| 單顆 | approve(address _approved, uint256 _tokenId) | 授權某地址控制這一顆 |
| 全部 | setApprovalForAll(address _operator, bool _approved) | 授權 operator 管理你全部的 NFT。合約 MUST 允許一個 owner 有多個 operator |
查詢用 getApproved(tokenId)/isApprovedForAll(owner, operator)。
規格明說為什麼沒有 allowance:ERC-20 的額度可被改動所衍生的問題(OpenZeppelin issue #438)在這裡不存在——每個 NFT 的數量非 0 即 1,所以「拿到 ERC-20 原始設計的好處,卻沒有後來才發現的問題」。
三種人可以發動轉移
NFT 的擁有者、該 NFT 的 approved address、擁有者的 authorized operator。
轉移完成時,該 NFT 的 approved address 自動重置為無。
safeTransferFrom vs transferFrom——這是本規格最重要的一組對照
| 行為 | |
|---|---|
safeTransferFrom | 轉完後檢查 _to 是否為合約(code size > 0)。是的話呼叫 onERC721Received,回傳值不等於 magic value 就 throw |
transferFrom | 不檢查。規格用大寫警告: “THE CALLER IS RESPONSIBLE TO CONFIRM THAT _to IS CAPABLE OF RECEIVING NFTS OR ELSE THEY MAY BE PERMANENTLY LOST” |
兩者共同的 throw 條件:msg.sender 非擁有者/operator/approved、_from 非現任擁有者、_to 是零地址、_tokenId 無效。
接收方合約要收 safe transfer 就 MUST 實作 ERC721TokenReceiver,回傳
bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))。
事件
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);- 鑄造
_from == 0、銷毀_to == 0。 - 例外:合約建立期間可以鑄造任意數量的 NFT 而不發
Transfer。(見下方注意)
tokenId 的規則
uint256,終生不變。(合約地址, tokenId) 才是鏈上全域唯一識別。
呼叫端 MUST 把 ID 當黑盒——規格明文禁止假設它從 0 開始遞增或有任何規律。選 uint256 是因為 UUID 與 sha3 雜湊都能直接轉過來。
兩個選配擴充
- Metadata(
0x5b5e139f):name()/symbol()/tokenURI(tokenId)。tokenURI指向符合 ERC721 Metadata JSON Schema 的 JSON(name/description/image),URI 可以是會變的(規格舉例:NFT 代表一間房子,房子的照片與住戶本來就會變)。 - Enumeration(
0x780e9d63):totalSupply()/tokenByIndex(i)/tokenOfOwnerByIndex(owner, i)。排序未定義。
鑄造與銷毀不在規格內,自己實作,但事件規則要遵守。
🧪 我實際套用的紀錄
- 2026-08-27:(待填)
⚠️ 注意 / 什麼時候不適用
1. 不安全的轉移函式還在,坑只是變成「你選的」
我的補註:ERC-20 那個「代幣轉進不懂代幣的合約就永久卡死」的問題,721 給了 safeTransferFrom 當正面回應——但沒有廢掉 transferFrom。規格自己用全大寫警告永久遺失。整合時預設一律用 safe 版本,用不安全版本要有明確理由。
2. 靠 name/symbol 辨識真偽是不行的
規格明說兩件事:空字串是 name 與 symbol 的合法回應;任何合約都能用跟你一樣的 name 與 symbol。而且「客戶端如何判斷哪個 ERC-721 合約是正牌的,不在本標準範圍內」。
唯一可靠的識別是合約地址。
3. 建構期可以不發 Transfer 事件
這代表你不能保證「光靠事件重建完整所有權歷史」——建構期鑄造的 NFT 可能沒有對應事件。寫索引器時要另外處理初始狀態。(對照:ERC-1155 多代幣標準 反而把「事件足以重建餘額」寫成硬性保證。)
4. 不實作 enumeration 換不到隱私
規格明白指出:攻擊者可以直接對每一個可能的 tokenId 呼叫 ownerOf,所以**「私有的所有權登記」在這個標準下做不到**。
5. 會擴張的實作避開 for/while 迴圈
規格點名這是規模化的警訊——gas 成本會無上限地隨時間上升。
6. payable 的可變性保證
介面裡標 payable 的函式,你的實作可以更嚴格(改成 nonpayable),但不能更寬鬆。
🔗 相關工具
- ERC-20 代幣標準 —— 同質化的對照組。721 的
Transfer(from=0)鑄造慣例、safeTransferFrom的接收檢查,都是針對 20 的已知問題而設計 - ERC-1155 多代幣標準 —— 一個合約裝多種代幣型別;只有 safe 轉移、且保證事件可重建餘額
- 工具-代幣標準與DApp —— 概念與決策層(《精通以太坊》2018)。該卡自己標明其 ERC-721 介面是當時的提案版,實作前必須查現行規格——就是這張
- 工具-智能合約安全反模式 —— 接收方 hook 的重入面向要照那張的清單掃