2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > ERC721标准合约接口事件和方法分析

ERC721标准合约接口事件和方法分析

时间:2021-01-11 16:05:59

相关推荐

ERC721标准合约接口事件和方法分析

目录

ERC721简介

ERC721标准

IERC721接口定义

IERC721接口函数分析

safeTransferFrom与transferFrom的核心异同点

必需实现ERC165标准

可选实现标准

ERC721Metadata

ERC721Enumerable

NTF TokenId生成

ERC721实现案例

参考

ERC721简介

ERC721 是由Dieter Shirley 在9月提出。Dieter Shirley 正是谜恋猫CryptoKitties背后的公司Axiom Zen的技术总监。因此谜恋猫也是第一个实现了ERC721 标准的去中心化应用。ERC721号提议已经被以太坊作为标准接受,但该标准仍处于草稿阶段。本文介绍的ERC721标准基于最新(/03/23官方提议。

ERC721是一个代币标准,ERC721官方定义为Non-Fungible Tokens,简写为NFTs,即非同质代币。

其显示意义在于非同质性其实广泛存在于我们的生活中,如图书馆的每一本,宠物商店的每一只宠物,歌手所演唱的歌曲,花店里不同的花等等,因此ERC721合约必定有广泛的应用场景。通过这样一个标准,也可建立跨功能的NFTs管理和销售平台(就像有支持ERC20的交易所和钱包一样),使生态更加强大。

ERC721标准

IERC721接口定义

ERC721做为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721及ERC165接口,接口定义如下:

pragma solidity ^0.8.0;import "../../utils/introspection/IERC165.sol";/*** @dev Required interface of an ERC721 compliant contract.*/interface IERC721 is IERC165 {/*** @dev Emitted when `tokenId` token is transferred from `from` to `to`.*/event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);/*** @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.*/event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);/*** @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.*/event ApprovalForAll(address indexed owner, address indexed operator, bool approved);/*** @dev Returns the number of tokens in ``owner``'s account.*/function balanceOf(address owner) external view returns (uint256 balance);/*** @dev Returns the owner of the `tokenId` token.** Requirements:** - `tokenId` must exist.*/function ownerOf(uint256 tokenId) external view returns (address owner);/*** @dev Safely transfers `tokenId` token from `from` to `to`.** Requirements:** - `from` cannot be the zero address.* - `to` cannot be the zero address.* - `tokenId` token must exist and be owned by `from`.* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.** Emits a {Transfer} event.*/function safeTransferFrom(address from,address to,uint256 tokenId,bytes calldata data) external;/*** @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients* are aware of the ERC721 protocol to prevent tokens from being forever locked.** Requirements:** - `from` cannot be the zero address.* - `to` cannot be the zero address.* - `tokenId` token must exist and be owned by `from`.* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.** Emits a {Transfer} event.*/function safeTransferFrom(address from,address to,uint256 tokenId) external;/*** @dev Transfers `tokenId` token from `from` to `to`.** WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.** Requirements:** - `from` cannot be the zero address.* - `to` cannot be the zero address.* - `tokenId` token must be owned by `from`.* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.** Emits a {Transfer} event.*/function transferFrom(address from,address to,uint256 tokenId) external;/*** @dev Gives permission to `to` to transfer `tokenId` token to another account.* The approval is cleared when the token is transferred.** Only a single account can be approved at a time, so approving the zero address clears previous approvals.** Requirements:** - The caller must own the token or be an approved operator.* - `tokenId` must exist.** Emits an {Approval} event.*/function approve(address to, uint256 tokenId) external;/*** @dev Approve or remove `operator` as an operator for the caller.* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.** Requirements:** - The `operator` cannot be the caller.** Emits an {ApprovalForAll} event.*/function setApprovalForAll(address operator, bool _approved) external;/*** @dev Returns the account approved for `tokenId` token.** Requirements:** - `tokenId` must exist.*/function getApproved(uint256 tokenId) external view returns (address operator);/*** @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.** See {setApprovalForAll}*/function isApprovedForAll(address owner, address operator) external view returns (bool);}

IERC721接口函数分析

1. Event事件

event Transfer(address indexed from, address indexed to, uint256 indexed tokenId) 当任何NFT的所有权更改时(不管哪种方式,最终都会调用该事件),就会触发此事件。

event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 当更改或确认NFT的授权地址时触发。approved参数表示被授权可以转移NFT的地址。approved为零地址表示取消授权的地址;发生 Transfer 事件时,该NFT的授权地址(如果有)被重置为“无”(零地址)。需要指出的是Approval需要花费Gas。

event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 所有者启用或禁用操作员时触发。即指定或取消可管理整个NFTs的operator操作员。(操作员可管理所有者所持有的NFTs)

function balanceOf(address owner) external view returns (uint256 balance); 统计owner 地址所持有的NFTs数量

function ownerOf(uint256 tokenId) external view returns (address owner); 返回指定NFT的所有者

function safeTransferFrom(address from,address to,uint256 tokenId,bytes calldata data) external; 将NFT的所有权从一个地址转移到另一个地址,data : 附加额外的参数(没有指定格式),传递给接收者。

function safeTransferFrom(address from,address to,uint256 tokenId) external; 将NFT的所有权从一个地址转移到另一个地址,功能同上,不带data参数。

function transferFrom(address from,address to,uint256 tokenId) external; 转移所有权 – 调用者负责确认_to是否有能力接收NFTs,否则可能永久丢失。一般不建议使用该方法转移NFT

function approve(address to, uint256 tokenId) external; 更改或确认NFT的授权地址

function setApprovalForAll(address operator, bool _approved) external; 启用或禁用第三方(操作员)管理 msg.sender 所有资产,触发 ApprovalForAll 事件。

function getApproved(uint256 tokenId) external view returns (address operator); 获取单个NFT的授权地址,也反映出一个tokenId只能同时授权给一个operator。

function isApprovedForAll(address owner, address operator) external view returns (bool); 查询一个地址是否是另一个地址的授权操作员

safeTransferFrom与transferFrom的核心异同点

safeTransferFrom函数的实现需要做一下几种检查:

调用者msg.sender应该是当前tokenId的所有者或被授权的地址_from 必须是 _tokenId的所有者_tokenId 应该是当前合约正在的NFTs 中的任何一个_to 地址不应该为 0如果_to 是一个合约应该调用其onERC721Received方法, 并且检查其返回值,如果返回值不为bytes4(keccak256("onERC721Received(address,uint256,bytes)"))抛出异常。

一个可接收NFT的合约必须实现ERC721TokenReceiver接口:

interface ERC721TokenReceiver {/// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);}

transferFrom需调用者自己确认_to地址能正常接收NFT,否则将丢失此NFT。此函数实现时需要检查上面条件的前4条。

必需实现ERC165标准

ERC721标准同时要求必须符合ERC165标准 ,其接口如下:

pragma solidity ^0.8.0;/*** @dev Interface of the ERC165 standard, as defined in the* /EIPS/eip-165[EIP].** Implementers can declare support of contract interfaces, which can then be* queried by others ({ERC165Checker}).** For an implementation, see {ERC165}.*/interface IERC165 {/*** @dev Returns true if this contract implements the interface defined by* `interfaceId`. See the corresponding* /EIPS/eip-165#how-interfaces-are-identified[EIP section]* to learn more about how these ids are created.** This function call must use less than 30 000 gas.*/function supportsInterface(bytes4 interfaceId) external view returns (bool);}

这个标准要求合约提供其实现了哪些接口,这样再与合约进行交互的时候可以先调用此接口进行查询。

interfaceID为函数选择器,计算方式有两种,如:bytes4(keccak256('supportsInterface(bytes4)'));ERC165.supportsInterface.selector,多个函数的接口ID为函数选择器的异或值。

可选实现标准

ERC721Metadata

ERC721Metadata 接口用于提供合约的元数据:name , symbol 及 URI(NFT所对应的资源)。

其接口定义如下:

interface ERC721Metadata /* is ERC721 */ {function name() external pure returns (string _name);function symbol() external pure returns (string _symbol);function tokenURI(uint256 _tokenId) external view returns (string);}

接口说明:

name(): 返回合约名字,尽管是可选,但强烈建议实现,即便是返回空字符串。symbol(): 返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。tokenURI(): 返回_tokenId所对应的外部资源文件的URI(通常是IPFS或HTTP(S)路径)。外部资源文件需要包含名字、描述、图片,其格式的要求如下:

{"title": "Asset Metadata","type": "object","properties": {"name": {"type": "string","description": "Identifies the asset to which this NFT represents",},"description": {"type": "string","description": "Describes the asset to which this NFT represents",},"image": {"type": "string","description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.",}}}

tokenURI通常是被web3调用,以便在应用层做相应的查询和展示。

ERC721Enumerable

ERC721Enumerable的主要目的是提高合约中NTF的可访问性,其接口定义如下:

interface ERC721Enumerable /* is ERC721 */ {function totalSupply() external view returns (uint256);function tokenByIndex(uint256 _index) external view returns (uint256);function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);}

接口说明:

totalSupply(): 返回NFT总量tokenByIndex(): 通过索引返回对应的tokenId。tokenOfOwnerByIndex(): 所有者可以一次拥有多个的NFT, 此函数返回_owner拥有的NFT列表中对应索引的tokenId。

NTF TokenId生成

tokenId,在合约中用唯一的uint265进行标识,每个NFT的ID在智能合约的生命周期内不允许改变。推荐的实现方式有:

从0开始,每新加一个NFT,NTF ID加1使用sha3后uuid 转换为 NTF ID

ERC721实现案例

openzeppelin智能合约库来写一个ERC721合约,来实现NFT的铸造、销毁和URL设定等功能。

// SPDX-License-Identifier: MIT;pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC721/ERC721.sol";import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";import "@openzeppelin/contracts/utils/Counters.sol";contract TestNFT is ERC721,ERC721Enumerable, ERC721URIStorage {using Counters for Counters.Counter;Counters.Counter private _tokenId;constructor() ERC721("The First NFT","FFT") {}function mint(address _recipient, string memory _tokenUrl) public returns(uint _mintTokenId){require(bytes(_tokenUrl).length > 0,"The _tokenUrl must be have");_tokenId.increment();uint newTokenId = _tokenId.current();_mint(_recipient, newTokenId);_setTokenURI(newTokenId, _tokenUrl);return newTokenId;}function _beforeTokenTransfer(address from, address to, uint256 tokenId)internaloverride(ERC721, ERC721Enumerable){super._beforeTokenTransfer(from, to, tokenId);}function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {super._burn(tokenId);}function tokenURI(uint256 tokenId)publicviewoverride(ERC721, ERC721URIStorage)returns (string memory){return super.tokenURI(tokenId);}function supportsInterface(bytes4 interfaceId)publicviewoverride(ERC721, ERC721Enumerable)returns (bool){return super.supportsInterface(interfaceId);}}

合约和方法解析:

@openzeppelin/contracts/token/ERC721/ERC721.sol 该合约实现了我们上面列出的IERC721接口中的方法

@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol 该合约为一个枚举合约,包含了按索引获取到对应的代币,可以提供NFTs的完整列表,以便NFT可被发现。

@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol 该合约帮我们实现了TOKEN的URI的设定和读取方法

@openzeppelin/contracts/utils/Counters.sol 该合约帮我们实现了合约的自增ID

constructor() 我们在构造方法中实现了设定该合约生成的NFT的名称的设定(The First NFT)、简写的设定(FFT)

mint() 为_recipient地址铸造一个NFT,并将其元数据URL设置为_tokenUrl

_beforeTokenTransfer() 重写_beforeTokenTransfer方法

_burn() 销毁某个NFT

tokenURI() 获取指定NFT的URL信息

supportsInterface() 重写supportsInterface方法

参考

EIPs/eip-165.md at master · ethereum/EIPs · GitHub

EIPs/eip-721.md at master · ethereum/EIPs · GitHub

科普 | ERC协议终极解读:ERC-721

剖析非同质化代币ERC721-全面解析ERC721标准 - 走看看

solidity实现智能合约教程(2)-ERC721合约_后端常规开发人员的博客-CSDN博客_erc721智能合约

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。