# NFT & ERC721 & ERC1155(考虑一下) ## Outline - [x] NFT Cointains what? - [ ] ERC721 & ERC1155 diffrence? - [x] NFT Data Storage(Link to ever posts) - [x] :flashlight: Inside ERC721 - [ ] :thinking_face: THINK: NFT? ## Content ### NFT 的组成 首先我们来看看一个 NFT 里面究竟有什么东西: 找个NFT,截图+ 数据, 我们可以看到,一个 NFT 是由以下三部分组成: - name 名字 - symbol 代币符号 - URI NFT元数据 其中,元数据是 NFT 最重要的部分,它决定了 NFT 里面纠结有什么。对于一些图片类的 NFT 来说,元数据通常都会存放图片的链接,这个链接可能是中心化网站的链接,或者是 ipfs 链接 如 https://ipfs.io/ipfs/bafkreiemnftbduxffrv62icdtwyr6fdylgdxteapjl76rhtoikh6x6bavm 其实大部分 NFT 都只放了一个链接....没那么多东西,下面是官方的元数据示范,写的是 JSON,大家可以看看 (官方的截图 关于 NFT 元数据的存储问题,在之前的周报 #几 #几 已经解释过了,大家有兴趣可以自行阅读。至于最近热门的 NFT 是怎么存储的,可以看这篇文章 https://www.chainnews.com/articles/363603486355.htm?utm_source=telegra CryptoPunks、加密猫、小企鹅都没有在链上存储元数据 ### 在 ERC721 畅游 EIP-721 给了关于 ERC721 的 3 个接口,分别是: - IERC721: 核心 ERC721 - IERC721Metadata: ERC721 元数据 - IERC721Enumerable: 关于 ERC721 的供应量、序号之类的数字接口 其中,IERC721 是唯一必须要实现的借口,但这是在是太简单了,与主流 NFT 项目的标准还有一定的距离。因此,我们将和大家分享多一些关于 ERC721 能用到的功能和接口。 除了上述 3 个接口之外,还有以下功能是可以添加上去的: - ERC721Pausable:用于管理 ERC721 的代币转账 - ERC721Burnable:摧毁代币! - AccessControl:合约权限管理 我们以 OpenZepplin 的 ERC721PresetMinterPauserAutoId 范例合约为例来看,这个范例可以实现以下功能: - 允许用户摧毁掉自己的代币 - 一个铸造者(minter)角色来负责铸造代币 - 一个交易管理员角色(pauser),有权暂停代币转账 - 自动生成 tokenID 和 元数据 ### ERC721 我们主要关注的是元数据的写入以及代币的铸造过程 首先,元数据以 mapping 的形式存储 ```solidity= // Optional mapping for token URIs mapping (uint256 => string) private _tokenURIs; ``` 通过 _setTokenURI 函数,我们可以设置 URI,这里需要传入 tokenID 和 URI 元数据 ```solidity= function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token"); _tokenURIs[tokenId] = _tokenURI; } ``` ERC721 对 _mint 函数和 ERC20 很相似,本质就是将代币分发给用户,由于 TokenID 代表了 NFT 代币,因此调用 mint 函数的时候只需要指定用户和 tokenID 即可 ```solidity= function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _beforeTokenTransfer(address(0), to, tokenId); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(address(0), to, tokenId); } ``` 值得一提的是,OpenZepplin 并不推荐我们直接使用 _mint,而是推荐使用 _safeMint ```solidity= /** * @dev Safely mints `tokenId` and transfers it to `to`. * * Requirements: d* * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal virtual { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual { _mint(to, tokenId); require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } ``` _safeMint 会在 mint 之前进行检查,要求如下: - tokenID 不存在,意思就是这个 tokenID 对应的 NFT 还没有被 mint - 收款地址需要有接受 ERC721 代币的能力,这个检查主要是为了合约收款而设置的 ### 额外功能 ### Pausable Pausable 合约在 OpenZepplin 的 utils 工具库中。它的原理比较简单,就是控制一个叫 _paused 的 bool 类型变量,在调用交易函数前可以对这个变量进行检查,假如 _paused 为 True,就无法进行交易。 修改 _paused 的值会触发对应的事件。 https://docs.openzeppelin.com/contracts/3.x/api/utils#Pausable-paused-- ### ERC721Burnable 摧毁代币的真的很简单 🤣,直接调用 `burn(uint256 tokenId)`,传入 tokenID 即可。 https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/token/ERC721/ERC721.sol ---- ## Draft ### NFT Cointains what? -> **ERC721Metadata** Functions: - name() - symbol() - tokenURI(tokenId) URI 数据是什么 https://en.wikipedia.org/wiki/Uniform_Resource_Identifier A Uniform Resource Identifier (URI) is a unique sequence of characters that identifies a logical or physical resource used by web technologies. URIs may be used to identify anything, including real-world objects, such as people and places, concepts, or information resources such as web pages and books. ## Ref ### Codes~~~ EIP721: https://eips.ethereum.org/EIPS/eip-721 Ethereum Org: https://ethereum.org/en/developers/docs/standards/tokens/erc-721/ OPZL: https://docs.openzeppelin.com/contracts/3.x/api/presets#ERC721PresetMinterPauserAutoId