BTC 和 ETH 這類代幣都屬於同質化代幣,礦工挖出的第 1 個 BTC 與第 10000 個 BTC 是等價的,但世界上許多物品是不同質的,其中包括房產、古董、虛擬藝術品等等,這類物品無法用同質化代幣抽象化。因此,以太坊 EIP721 提出了 ERC721 標準,來抽象化非同質化的物品。今天要來理解 ERC721 標準,並利用它發行一款 NFT。
EIP 全名為 Ethereum Improvement Proposals(以太坊改進建議), 是以太坊開發者社區提出的改進建議,是一系列以編號排定的文件,類似網路上 IETF 的 RFC。
而 EIP 可以是乙太坊生態中任意領域的改進,例如新特性、ERC、協定改進、程式設計工具等等。
ERC 全名為 Ethereum Request For Comment (以太坊意見徵求稿),用來記錄以太坊上應用級的各種開發標準和協議。如典型的 Token 標準(ERC20, ERC721)、名字註冊(ERC26, ERC13)、URI範式(ERC67)、Library/Package 格式(EIP82)、包格式(EIP75,EIP85)。ERC 協議標準也是影響以太坊發展的重要因素,像 ERC20、ERC223、 ERC721、ERC777 等,都是對以太坊生態產生了很大影響。而 EIP 包含了 ERC。
透過 ERC165 標準,智能合約可以宣告它支援的介面,供其他合約檢查。簡單的說,ERC165 就是檢查一個智能合約是不是支援了 ERC721、ERC1155 的介面。
IERC165 介面合約只聲明了一個 supportsInterface
函數,輸入要查詢的interfaceId
介面id,若合約實作了該介面 id,則傳回 true
:
interface IERC165 {
/**
* @dev 如果合約實作了查詢的 `interfaceId`,則傳回 true
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
然後 ERC721 實作 supportsInterface()
函數:
function supportsInterface(bytes4 interfaceId) external pure override returns (bool)
{
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
當查詢的是 IERC721 或 IERC165 的介面 id 時,回傳 true,反之回傳 false。
IERC721 是 ERC721標準的介面合約,規定了 ERC721 要實現的基本函數。它利用 tokenId
來表示特定的非同質化代幣,授權或轉帳都要明確 tokenId
;而 ERC20 只需要明確轉帳的金額即可。
/**
* @dev ERC721 標準介面
*/
interface IERC721 is IERC165 {
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);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
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 setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
IERC721 包含 3 個事件,其中 Transfer 和 Approval 事件在 ERC20 中也有。
如果一個合約沒有實現 ERC721 的相關函數,轉入的 NFT 就進了黑洞,永遠轉不出來了。為了防止誤轉賬,ERC721 實作了 safeTransferFrom() 安全轉帳函數,目標合約必須實作了 IERC721Receiver 介面才能接收 ERC721 代幣,不然會 revert。 IERC721Receiver 介面只包含一個 onERC721Received() 函數。
// ERC721接收者介面:合約必須實作這個介面來透過安全轉帳接收ERC721
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external returns (bytes4);
}