1. ERC721代幣標(biāo)準(zhǔn)
BTC和ETH這類代幣都屬于同質(zhì)化代幣,礦工挖出的第1枚BTC與第10000枚BTC并沒有不同,是等價(jià)的。但世界中很多物品是不同質(zhì)的,其中包括房產(chǎn)、古董、虛擬藝術(shù)品等等,這類物品無法用同質(zhì)化代幣抽象。因此,以太坊EIP721提出了ERC721標(biāo)準(zhǔn),來抽象非同質(zhì)化的物品。
EIP與ERC
這里有一個(gè)點(diǎn)需要理解,本節(jié)標(biāo)題是ERC721,這里又提到了EIP721,這兩個(gè)是什么關(guān)系呢?
EIP全稱 Ethereum Imporvement Proposals(以太坊改進(jìn)建議), 是以太坊開發(fā)者社區(qū)提出的改進(jìn)建議, 是一系列以編號(hào)排定的文件, 類似互聯(lián)網(wǎng)上IETF的RFC。
EIP可以是 Ethereum 生態(tài)中任意領(lǐng)域的改進(jìn), 比如新特性、ERC、協(xié)議改進(jìn)、編程工具等等。
ERC全稱 Ethereum Request For Comment (以太坊意見征求稿), 用以記錄以太坊上應(yīng)用級(jí)的各種開發(fā)標(biāo)準(zhǔn)和協(xié)議。如典型的Token標(biāo)準(zhǔn)(ERC20, ERC721)、名字注冊(cè)(ERC26, ERC13), URI范式(ERC67), Library/Package格式(EIP82), 錢包格式(EIP75,EIP85)。
ERC協(xié)議標(biāo)準(zhǔn)是影響以太坊發(fā)展的重要因素, 像ERC20, ERC223, ERC721, ERC777等, 都是對(duì)以太坊生態(tài)產(chǎn)生了很大影響。
所以最終結(jié)論:EIP包含ERC。
2. IERC721接口
OpenZepplin 為我們提供了相應(yīng)的庫,方便開發(fā)者導(dǎo)入即用。
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/interfaces/IERC165.sol";
interface IERC721 is IERC165 {
// *************************** 事件 ***************************
// 1. 轉(zhuǎn)賬事件
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
// 2. 授權(quán)事件
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
// 3. 授權(quán)所有資產(chǎn)事件
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
// *************************** 函數(shù) ***************************
// 1. 查詢賬戶余額
function balanceOf(address owner) external view returns (uint256 balance);
// 2. 根據(jù)tokenId獲取NFT所有者
function ownerOf(uint256 tokenId) external view returns (address owner);
// 3. 安全轉(zhuǎn)賬1:
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
// 4. 安全轉(zhuǎn)賬2:
function safeTransferFrom(address from, address to, uint256 tokenId) external;
// 5. 轉(zhuǎn)賬
function transferFrom(address from, address to, uint256 tokenId) external;
// 6. 授權(quán)
function approve(address to, uint256 tokenId) external;
// 7. 授權(quán)所有資產(chǎn)
function setApprovalForAll(address operator, bool approved) external;
// 8. 獲取單一NFT授權(quán)情況
function getApproved(uint256 tokenId) external view returns (address operator);
// 9. 是否授權(quán)了所有資產(chǎn)
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
- 查詢地址擁有的NFT數(shù)量
function balanceOf(address owner) external view returns (uint256 balance);
- 給定一個(gè)地址
owner,返回該地址擁有的NFT數(shù)量;- 使用
mapping ( address => uint256) _balance存儲(chǔ)。
- 查詢NFT的持有者
function ownerOf(uint256 tokenId) external view returns (address owner);
- 給定一個(gè)
tokenId,返回該NFT的持有者地址;- 使用
mapping ( uint256 => address) _owner存儲(chǔ)。
- 安全轉(zhuǎn)移函數(shù)(帶data)
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
- 調(diào)用者
msg.sender將編號(hào)為tokenId的NFT從持有者from轉(zhuǎn)移到接受者to;- 若接受者為合約,則會(huì)嘗試調(diào)用
onERC721Received來進(jìn)行檢查:
- 檢查的目的:轉(zhuǎn)移NFT需要調(diào)用NFT合約對(duì)應(yīng)的轉(zhuǎn)賬接口,如果被轉(zhuǎn)入的合約沒有實(shí)現(xiàn)相關(guān)功能,則NFT會(huì)被永久鎖定在沒有預(yù)期收到NFT的合約中。
- 檢查過程:NFT721的
safeTransferFrom要求接受者為合約時(shí),該合約需要實(shí)現(xiàn)IERC721Receiver接口的相關(guān)函數(shù),同時(shí)NFT合約會(huì)將data作為參數(shù)調(diào)用接收合約的onERC721Received函數(shù),最后檢查回傳值是否正確。- 由于權(quán)限問題,調(diào)用者必須為:NFT持有者 / 被授權(quán)的第三方。
- 安全轉(zhuǎn)移函數(shù)(不帶data)
function safeTransferFrom(address from, address to, uint256 tokenId) external;
- 與安全轉(zhuǎn)移函數(shù)(帶data)的差異只在于沒有
data,因此通常直接采用以下方式實(shí)現(xiàn):
safeTransferFrom(from, to, tokenId, "");
- 轉(zhuǎn)移函數(shù)
function transferFrom(address from, address to, uint256 tokenId) external;
- 調(diào)用者
msg.sender將編號(hào)為tokenId的NFT從持有者from轉(zhuǎn)移到接受者to;- 由于權(quán)限問題,調(diào)用者必須為:NFT持有者 / 被授權(quán)的第三方;
- 不帶安全檢查,如果被轉(zhuǎn)入是合約且沒有實(shí)現(xiàn)相關(guān)功能,則NFT會(huì)被永久鎖定在沒有預(yù)期收到NFT的合約中。
- 授權(quán)單個(gè)NFT
function approve(address to, uint256 tokenId) external;
- 調(diào)用者
msg.sender將所擁有的的編號(hào)為tokenId的NFT授權(quán)給第三方to;- 使用
mapping (uint256 => address) _tokenApprovals;存儲(chǔ);
- 授權(quán)/撤銷所有NFT
function setApprovalForAll(address operator, bool approved) external;
調(diào)用者msg.sender授權(quán)或撤銷所持有的全部NFT給操作者operator。
- 查詢單個(gè)NFT的授權(quán)情況
function getApproved(uint256 tokenId) external view returns (address operator);
- 給定一個(gè)NFT的
tokenId,返回該NFT的被授權(quán)地址address。- 使用
mapping( address => (mapping ( address => bool))) _operatorApprovals來存儲(chǔ)。
- 查詢是否授權(quán)了所有NFT
function isApprovedForAll(address owner, address operator) external view returns (bool);
- 給定持有者
owner和第三方operator,查詢是否授權(quán)了所有NFT的管理權(quán)。