Created
August 14, 2021 21:48
-
-
Save nasdf/df4467399786a41bc52b323d966ac02a to your computer and use it in GitHub Desktop.
Versioned NFT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: Unlicense | |
pragma solidity ^0.8.0; | |
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; | |
import "@openzeppelin/contracts/utils/Counters.sol"; | |
contract Versioned is ERC721URIStorage { | |
using Counters for Counters.Counter; | |
using Strings for uint256; | |
Counters.Counter private _tokenIds; | |
// mapping of keccak256(tokenId, tokenVersion) to version URIs. | |
mapping(bytes32 => string) private _tokenVersionURIs; | |
// mapping of tokenId to array of version names. | |
mapping(uint256 => string[]) private _tokenVersionNames; | |
// Published event is emitted when a new token version is published. | |
event Published(address indexed _from, uint256 indexed _tokenId, string _tokenVersion, string _tokenURI); | |
constructor() ERC721("Versioned", "V") {} | |
/** | |
* Returns the total number of versions for the token with the given tokenId. | |
*/ | |
function versionCount(uint256 tokenId) public view returns (uint256) { | |
require(_exists(tokenId), "token does not exist"); | |
return _tokenVersionNames[tokenId].length; | |
} | |
/** | |
* Returns the version name of the token with the given tokenId at the given index. | |
*/ | |
function versionNameByIndex(uint256 tokenId, uint256 index) public view returns (string memory) { | |
require(index < _tokenVersionNames[tokenId].length, "index out of bounds"); | |
return _tokenVersionNames[tokenId][index]; | |
} | |
/** | |
* Returns the URI of the token with the given tokenId at the given version. | |
*/ | |
function versionURI(uint256 tokenId, string memory tokenVersion) public view returns (string memory) { | |
require(_exists(tokenId), "token does not exist"); | |
bytes32 selector = keccak256(abi.encodePacked(tokenId, tokenVersion)); | |
return _tokenVersionURIs[selector]; | |
} | |
/** | |
* Create a new versioned token with an initial version and URI. | |
*/ | |
function create(string memory tokenVersion, string memory tokenURI) public returns (uint256) { | |
require(bytes(tokenVersion).length > 0, "invalid token version"); | |
require(bytes(tokenURI).length > 0, "invalid token URI"); | |
_tokenIds.increment(); | |
uint256 tokenId = _tokenIds.current(); | |
_mint(msg.sender, tokenId); | |
publish(tokenId, tokenVersion, tokenURI); | |
return tokenId; | |
} | |
/** | |
* Publish a new version of an existing token. | |
*/ | |
function publish(uint256 tokenId, string memory tokenVersion, string memory tokenURI) public { | |
require(_isApprovedOrOwner(msg.sender, tokenId), "publish caller is not owner nor approved"); | |
require(bytes(tokenVersion).length > 0, "invalid token version"); | |
require(bytes(tokenURI).length > 0, "invalid token URI"); | |
bytes32 selector = keccak256(abi.encodePacked(tokenId, tokenVersion)); | |
require(bytes(_tokenVersionURIs[selector]).length == 0, "version already exists"); | |
_tokenVersionURIs[selector] = tokenURI; | |
_tokenVersionNames[tokenId].push(tokenVersion); | |
_setTokenURI(tokenId, tokenURI); | |
emit Published(msg.sender, tokenId, tokenVersion, tokenURI); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment