ERC721Metadata.sol 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. pragma solidity ^0.6.0;
  2. import "../../GSN/Context.sol";
  3. import "./ERC721.sol";
  4. import "./IERC721Metadata.sol";
  5. import "../../introspection/ERC165.sol";
  6. contract ERC721Metadata is Context, ERC165, ERC721, IERC721Metadata {
  7. // Token name
  8. string private _name;
  9. // Token symbol
  10. string private _symbol;
  11. // Optional mapping for token URIs
  12. mapping(uint256 => string) private _tokenURIs;
  13. // Base URI
  14. string private _baseURI;
  15. /*
  16. * bytes4(keccak256('name()')) == 0x06fdde03
  17. * bytes4(keccak256('symbol()')) == 0x95d89b41
  18. * bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
  19. *
  20. * => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
  21. */
  22. bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
  23. /**
  24. * @dev Constructor function
  25. */
  26. constructor (string memory name, string memory symbol) public {
  27. _name = name;
  28. _symbol = symbol;
  29. // register the supported interfaces to conform to ERC721 via ERC165
  30. _registerInterface(_INTERFACE_ID_ERC721_METADATA);
  31. }
  32. /**
  33. * @dev Gets the token name.
  34. * @return string representing the token name
  35. */
  36. function name() external view override returns (string memory) {
  37. return _name;
  38. }
  39. /**
  40. * @dev Gets the token symbol.
  41. * @return string representing the token symbol
  42. */
  43. function symbol() external view override returns (string memory) {
  44. return _symbol;
  45. }
  46. /**
  47. * @dev Returns the URI for a given token ID. May return an empty string.
  48. *
  49. * If the token's URI is non-empty and a base URI was set (via
  50. * {_setBaseURI}), it will be added to the token ID's URI as a prefix.
  51. *
  52. * Reverts if the token ID does not exist.
  53. */
  54. function tokenURI(uint256 tokenId) external view override returns (string memory) {
  55. require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
  56. string memory _tokenURI = _tokenURIs[tokenId];
  57. // Even if there is a base URI, it is only appended to non-empty token-specific URIs
  58. if (bytes(_tokenURI).length == 0) {
  59. return "";
  60. } else {
  61. // abi.encodePacked is being used to concatenate strings
  62. return string(abi.encodePacked(_baseURI, _tokenURI));
  63. }
  64. }
  65. /**
  66. * @dev Internal function to set the token URI for a given token.
  67. *
  68. * Reverts if the token ID does not exist.
  69. *
  70. * TIP: if all token IDs share a prefix (e.g. if your URIs look like
  71. * `http://api.myproject.com/token/<id>`), use {_setBaseURI} to store
  72. * it and save gas.
  73. */
  74. function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
  75. require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
  76. _tokenURIs[tokenId] = _tokenURI;
  77. }
  78. /**
  79. * @dev Internal function to set the base URI for all token IDs. It is
  80. * automatically added as a prefix to the value returned in {tokenURI}.
  81. *
  82. * _Available since v2.5.0._
  83. */
  84. function _setBaseURI(string memory baseURI) internal virtual {
  85. _baseURI = baseURI;
  86. }
  87. /**
  88. * @dev Returns the base URI set via {_setBaseURI}. This will be
  89. * automatically added as a preffix in {tokenURI} to each token's URI, when
  90. * they are non-empty.
  91. *
  92. * _Available since v2.5.0._
  93. */
  94. function baseURI() external view returns (string memory) {
  95. return _baseURI;
  96. }
  97. function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override {
  98. super._beforeTokenTransfer(from, to, tokenId);
  99. if (to == address(0)) { // When burning tokens
  100. // Clear metadata (if any)
  101. if (bytes(_tokenURIs[tokenId]).length != 0) {
  102. delete _tokenURIs[tokenId];
  103. }
  104. }
  105. }
  106. }