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 = 0x80ac58cdERC721Metadata = 0x5b5e139fERC721Enumerable = 0x780e9d63ERC721TokenReceiver = 0x150b7a02

🎯 什麼情境該想到我

當你「要實作或串接 NFT,需要知道轉移的三條路徑與安全轉移的確切規則」的時候。
概念層(NFT 是什麼、跟同質化差在哪)在 工具-代幣標準與DApp——但那張是 2018 提案版(用 deeddeedUri),介面已經改了,實作照這張。

⚙️ 怎麼用(步驟 / 公式)

必須同時實作 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 雜湊都能直接轉過來。

兩個選配擴充

  • Metadata0x5b5e139f):name()symbol()tokenURI(tokenId)tokenURI 指向符合 ERC721 Metadata JSON Schema 的 JSON(namedescriptionimage),URI 可以是會變的(規格舉例:NFT 代表一間房子,房子的照片與住戶本來就會變)。
  • Enumeration0x780e9d63):totalSupply()tokenByIndex(i)tokenOfOwnerByIndex(owner, i)排序未定義

鑄造與銷毀不在規格內,自己實作,但事件規則要遵守。

🧪 我實際套用的紀錄

  • 2026-08-27:(待填)

⚠️ 注意 / 什麼時候不適用

1. 不安全的轉移函式還在,坑只是變成「你選的」

我的補註:ERC-20 那個「代幣轉進不懂代幣的合約就永久卡死」的問題,721 給了 safeTransferFrom 當正面回應——但沒有廢掉 transferFrom。規格自己用全大寫警告永久遺失。整合時預設一律用 safe 版本,用不安全版本要有明確理由。

2. 靠 name/symbol 辨識真偽是不行的

規格明說兩件事:空字串是 namesymbol 的合法回應任何合約都能用跟你一樣的 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 的重入面向要照那張的清單掃