Skip to content

Instantly share code, notes, and snippets.

@nasdf
Created August 14, 2021 21:48
Show Gist options
  • Save nasdf/df4467399786a41bc52b323d966ac02a to your computer and use it in GitHub Desktop.
Save nasdf/df4467399786a41bc52b323d966ac02a to your computer and use it in GitHub Desktop.
Versioned NFT
//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