IERC165接口合约只聲明supportsInterface函數: 檢查該合約是否實現interface id
interface IERC165 {
/**
* @dev 如果合约实现了查询的`interfaceId`,则返回true
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
function supportsInterface(bytes4 interfaceId) external pure override returns (bool)
{
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
若interfaceId為IERC721或IERC165的inteface id,則返回true, 反之則返回false
- Transfer: from, to, tokenId => 轉帳時被釋放
- Approval: owner(授權地址), approved(被授權地址), tokenId => 授權時釋放
- ApprovalForAll => 批量授權時釋放
- balanceOf => NFT持有量
- ownerOf => 返回某tokenId的owner
- transferFrom => 普通轉帳
- safeTransferFrom => 安全轉帳 (若接收方是合約地址,會要求有ERC721Receiver接口)
- approve。授權其他地址使用NFT
- getApproved: 查詢tokenId被批准給哪個地址
- setApprovalForAll: 將自己持有的NFT批量授權給其他地址
- isApprovedForAll: 查詢某地址的NFT是否批量授權給其他地址
// ERC721接收者接口:合约必须实现这个接口来通过安全转账接收ERC721
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external returns (bytes4);
}
ERC721利用_checkOnERC721Received實現onERC721Received()
function _checkOnERC721Received(
address operator,
address from,
address to,
uint256 tokenId,
bytes memory data
) internal {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
// Token rejected
revert IERC721Errors.ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-IERC721Receiver implementer
revert IERC721Errors.ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
- name => 代幣名稱
- symbol => 代幣代號
- tokenURI => 透過tokenId查詢小圖片url
變數
// Token名称
string public override name;
// Token代号
string public override symbol;
// tokenId 到 owner address 的持有人映射
mapping(uint => address) private _owners;
// address 到 持仓数量 的持仓量映射
mapping(address => uint) private _balances;
// tokenID 到 授权地址 的授权映射
mapping(uint => address) private _tokenApprovals;
// owner地址。到operator地址 的批量授权映射
mapping(address => mapping(address => bool)) private _operatorApprovals;
檢查owner的NFT是否將所持有的NFT都授權給operator
function isApprovedForAll(address owner, address operator)
external
view
override
returns (bool)
{
return _operatorApprovals[owner][operator];
}
將自己所持有的NFT都授權給operator
function setApprovalForAll(address operator, bool approved) external override {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
查詢此tokenId對應到的NFT授權給哪個地址
function getApproved(uint tokenId) external view override returns (address) {
require(_owners[tokenId] != address(0), "token doesn't exist");
return _tokenApprovals[tokenId];
}
將該tokenId對應到的NFT授權給to地址
function _approve(
address owner,
address to,
uint tokenId
) private {
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
將該tokenId對應到的NFT授權給to地址 且to不能是owner且msg.sender是owner或授權地址
function approve(address to, uint tokenId) external override {
address owner = _owners[tokenId];
require(
msg.sender == owner || _operatorApprovals[owner][msg.sender],
"not owner nor approved for all"
);
_approve(owner, to, tokenId);
}
檢查spender地址是否可以使用tokenId 需要是owner或被授權地址
function _isApprovedOrOwner(
address owner,
address spender,
uint tokenId
) private view returns (bool) {
return (spender == owner ||
_tokenApprovals[tokenId] == spender ||
_operatorApprovals[owner][spender]);
}
通過調整_balances和_owner變量 將tokenId從from轉給to 同時釋放Transfer事件
條件限制:
- tokenId要被from擁有
- to不是0地址
function _transfer(
address owner,
address from,
address to,
uint tokenId
) private {
require(from == owner, "not owner");
require(to != address(0), "transfer to the zero address");
_approve(owner, address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
將tokenId從from轉給to 會檢查合約接受者是否支持ERC721協議(IERC721Receiver-onERC721Received) 條件
- from與to不能是0地址
- tokenId代幣需存在,且被from擁有
function _safeTransfer(
address owner,
address from,
address to,
uint tokenId,
bytes memory _data
) private {
_transfer(owner, from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, _data);
}
安全轉帳,調用_safeTransfer函數
function safeTransferFrom(
address from,
address to,
uint tokenId,
bytes memory _data
) public override {
address owner = ownerOf(tokenId);
require(
_isApprovedOrOwner(owner, msg.sender, tokenId),
"not owner nor approved"
);
_safeTransfer(owner, from, to, tokenId, _data);
}
若要轉給合約,會調用IERC721Receiver-onERC721Received,以防止tokenId被不小心轉入黑洞
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
鑄造tokenId給to 釋放transfer事件 條件
- tokenId尚不存在
- to不是0 address
function _mint(address to, uint tokenId) internal virtual {
require(to != address(0), "mint to zero address");
require(_owners[tokenId] == address(0), "token already minted");
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
銷毀tokenId,同時釋放Transfer事件 條件
- tokenId存在
function _burn(uint tokenId) internal virtual {
address owner = ownerOf(tokenId);
require(msg.sender == owner, "not owner of token");
_approve(owner, address(0), tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
小圖片url
function _baseURI() internal view virtual returns (string memory) {
return "";
}
每張小圖片的url
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_owners[tokenId] != address(0), "Token Not Exist");
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}