From 67d8f8ea18e73fb05bd4a3d09f064573754eb707 Mon Sep 17 00:00:00 2001 From: neokry Date: Mon, 24 Jul 2023 13:38:28 +0800 Subject: [PATCH] Add new type of metadata renderer --- script/DeployContracts.s.sol | 7 +- script/DeployMetadataUpgrade.s.sol | 7 +- script/DeployTokenUpgrade.s.sol | 5 +- script/DeployVersion1_1.s.sol | 7 +- src/manager/IManager.sol | 7 + src/manager/Manager.sol | 22 +- .../metadata/interfaces/IBaseMetadata.sol | 2 +- src/metadata/media/MediaMetadata.sol | 313 ++++++++++++++++++ .../media/interfaces/IMediaMetadata.sol | 57 ++++ .../media/storage/MediaMetadataStorageV1.sol | 24 ++ .../media/types/MediaMetadataTypesV1.sol | 27 ++ .../property/PropertyMetadata.sol} | 90 ++--- .../interfaces/IPropertyMetadata.sol} | 18 +- .../storage/PropertyMetadataStorageV1.sol} | 6 +- .../storage/PropertyMetadataStorageV2.sol} | 6 +- .../types/PropertyMetadataTypesV1.sol} | 4 +- .../types/PropertyMetadataTypesV2.sol} | 4 +- src/token/IToken.sol | 9 + src/token/Token.sol | 13 +- src/token/types/TokenTypesV1.sol | 2 +- test/MetadataRenderer.t.sol | 32 +- test/forking/TestUpdateMinters.t.sol | 4 +- test/utils/NounsBuilderTest.sol | 18 +- 23 files changed, 577 insertions(+), 107 deletions(-) rename src/{token => }/metadata/interfaces/IBaseMetadata.sol (97%) create mode 100644 src/metadata/media/MediaMetadata.sol create mode 100644 src/metadata/media/interfaces/IMediaMetadata.sol create mode 100644 src/metadata/media/storage/MediaMetadataStorageV1.sol create mode 100644 src/metadata/media/types/MediaMetadataTypesV1.sol rename src/{token/metadata/MetadataRenderer.sol => metadata/property/PropertyMetadata.sol} (94%) rename src/{token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol => metadata/property/interfaces/IPropertyMetadata.sol} (87%) rename src/{token/metadata/storage/MetadataRendererStorageV1.sol => metadata/property/storage/PropertyMetadataStorageV1.sol} (79%) rename src/{token/metadata/storage/MetadataRendererStorageV2.sol => metadata/property/storage/PropertyMetadataStorageV2.sol} (66%) rename src/{token/metadata/types/MetadataRendererTypesV1.sol => metadata/property/types/PropertyMetadataTypesV1.sol} (90%) rename src/{token/metadata/types/MetadataRendererTypesV2.sol => metadata/property/types/PropertyMetadataTypesV2.sol} (78%) diff --git a/script/DeployContracts.s.sol b/script/DeployContracts.s.sol index 224501d..d3f0289 100644 --- a/script/DeployContracts.s.sol +++ b/script/DeployContracts.s.sol @@ -6,12 +6,11 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IManager, Manager } from "../src/manager/Manager.sol"; import { IToken, Token } from "../src/token/Token.sol"; -import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; import { IAuction, Auction } from "../src/auction/Auction.sol"; import { IGovernor, Governor } from "../src/governance/governor/Governor.sol"; import { ITreasury, Treasury } from "../src/governance/treasury/Treasury.sol"; -import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; -import { MetadataRendererTypesV1 } from "../src/token/metadata/types/MetadataRendererTypesV1.sol"; +import { PropertyMetadata } from "../src/metadata/property/PropertyMetadata.sol"; +import { PropertyMetadataTypesV1 } from "../src/metadata/property/types/PropertyMetadataTypesV1.sol"; import { ERC1967Proxy } from "../src/lib/proxy/ERC1967Proxy.sol"; contract DeployContracts is Script { @@ -46,7 +45,7 @@ contract DeployContracts is Script { address tokenImpl = address(new Token(address(manager))); // Deploy metadata renderer implementation - address metadataRendererImpl = address(new MetadataRenderer(address(manager))); + address metadataRendererImpl = address(new PropertyMetadata(address(manager))); // Deploy auction house implementation address auctionImpl = address(new Auction(address(manager), weth)); diff --git a/script/DeployMetadataUpgrade.s.sol b/script/DeployMetadataUpgrade.s.sol index aa48421..cc25b02 100644 --- a/script/DeployMetadataUpgrade.s.sol +++ b/script/DeployMetadataUpgrade.s.sol @@ -6,12 +6,11 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IManager, Manager } from "../src/manager/Manager.sol"; import { IToken, Token } from "../src/token/Token.sol"; -import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; import { IAuction, Auction } from "../src/auction/Auction.sol"; import { IGovernor, Governor } from "../src/governance/governor/Governor.sol"; import { ITreasury, Treasury } from "../src/governance/treasury/Treasury.sol"; -import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; -import { MetadataRendererTypesV1 } from "../src/token/metadata/types/MetadataRendererTypesV1.sol"; +import { PropertyMetadata } from "../src/metadata/property/PropertyMetadata.sol"; +import { PropertyMetadataTypesV1 } from "../src/metadata/property/types/PropertyMetadataTypesV1.sol"; import { ERC1967Proxy } from "../src/lib/proxy/ERC1967Proxy.sol"; contract DeployMetadataUpgrade is Script { @@ -52,7 +51,7 @@ contract DeployMetadataUpgrade is Script { Manager manager = Manager(managerProxy); // Deploy metadata renderer implementation - address metadataRendererImpl = address(new MetadataRenderer(managerProxy)); + address metadataRendererImpl = address(new PropertyMetadata(managerProxy)); address managerImpl = address(new Manager()); diff --git a/script/DeployTokenUpgrade.s.sol b/script/DeployTokenUpgrade.s.sol index c89f99c..3c79e63 100644 --- a/script/DeployTokenUpgrade.s.sol +++ b/script/DeployTokenUpgrade.s.sol @@ -7,12 +7,11 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IManager, Manager } from "../src/manager/Manager.sol"; import { IToken, Token } from "../src/token/Token.sol"; -import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; import { IAuction, Auction } from "../src/auction/Auction.sol"; import { IGovernor, Governor } from "../src/governance/governor/Governor.sol"; import { ITreasury, Treasury } from "../src/governance/treasury/Treasury.sol"; -import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; -import { MetadataRendererTypesV1 } from "../src/token/metadata/types/MetadataRendererTypesV1.sol"; +import { PropertyMetadata } from "../src/metadata/property/PropertyMetadata.sol"; +import { PropertyMetadataTypesV1 } from "../src/metadata/property/types/PropertyMetadataTypesV1.sol"; import { ERC1967Proxy } from "../src/lib/proxy/ERC1967Proxy.sol"; contract DeployTokenUpgrade is Script { diff --git a/script/DeployVersion1_1.s.sol b/script/DeployVersion1_1.s.sol index 115d19b..ba3f52d 100644 --- a/script/DeployVersion1_1.s.sol +++ b/script/DeployVersion1_1.s.sol @@ -7,12 +7,11 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IManager, Manager } from "../src/manager/Manager.sol"; import { IToken, Token } from "../src/token/Token.sol"; -import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; import { IAuction, Auction } from "../src/auction/Auction.sol"; import { IGovernor, Governor } from "../src/governance/governor/Governor.sol"; import { ITreasury, Treasury } from "../src/governance/treasury/Treasury.sol"; -import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; -import { MetadataRendererTypesV1 } from "../src/token/metadata/types/MetadataRendererTypesV1.sol"; +import { PropertyMetadata } from "../src/metadata/property/PropertyMetadata.sol"; +import { PropertyMetadataTypesV1 } from "../src/metadata/property/types/PropertyMetadataTypesV1.sol"; import { ERC1967Proxy } from "../src/lib/proxy/ERC1967Proxy.sol"; contract DeployVersion1_1 is Script { @@ -58,7 +57,7 @@ contract DeployVersion1_1 is Script { // Deploy token upgrade implementation address tokenUpgradeImpl = address(new Token(managerProxy)); // Deploy metadata upgrade implementation - address metadataUpgradeImpl = address(new MetadataRenderer(managerProxy)); + address metadataUpgradeImpl = address(new PropertyMetadata(managerProxy)); address managerImpl = address(new Manager()); diff --git a/src/manager/IManager.sol b/src/manager/IManager.sol index 19e535a..297d2a3 100644 --- a/src/manager/IManager.sol +++ b/src/manager/IManager.sol @@ -40,6 +40,11 @@ interface IManager is IUUPS, IOwnable { /// @param upgradeImpl The upgrade implementation address event UpgradeRemoved(address baseImpl, address upgradeImpl); + /// @notice Event emitted when metadata renderer is updated. + /// @param sender address of the updater + /// @param renderer new metadata renderer address + event MetadataRendererUpdated(address sender, address renderer); + /// /// /// ERRORS /// /// /// @@ -56,6 +61,8 @@ interface IManager is IUUPS, IOwnable { /// @dev Reverts if an implementation type is not valid on registration error INVALID_IMPLEMENTATION_TYPE(); + error ONLY_TOKEN_OWNER(); + /// /// /// STRUCTS /// /// /// diff --git a/src/manager/Manager.sol b/src/manager/Manager.sol index f5c82bb..4e4602b 100644 --- a/src/manager/Manager.sol +++ b/src/manager/Manager.sol @@ -9,10 +9,11 @@ import { ManagerStorageV1 } from "./storage/ManagerStorageV1.sol"; import { ManagerStorageV2 } from "./storage/ManagerStorageV2.sol"; import { IManager } from "./IManager.sol"; import { IToken } from "../token/IToken.sol"; -import { IBaseMetadata } from "../token/metadata/interfaces/IBaseMetadata.sol"; +import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol"; import { IAuction } from "../auction/IAuction.sol"; import { ITreasury } from "../governance/treasury/ITreasury.sol"; import { IGovernor } from "../governance/governor/IGovernor.sol"; +import { IOwnable } from "../lib/interfaces/IOwnable.sol"; import { VersionedContract } from "../VersionedContract.sol"; import { IVersionedContract } from "../lib/interfaces/IVersionedContract.sol"; @@ -79,6 +80,7 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 // Ensure all implementations are registered unchecked { for (uint256 i; i < implAddressesLength; ++i) { + if (i == IMPLEMENTATION_TYPE_METADATA) continue; // metadata registration is optional if (!isImplementation[uint8(i)][_implAddresses[i]]) revert IMPLEMENTATION_NOT_REGISTERED(); } } @@ -113,6 +115,24 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 emit DAODeployed({ token: token, metadata: metadata, auction: auction, treasury: treasury, governor: governor }); } + /// @notice Set a new metadata renderer + /// @param _newRendererImpl new renderer address to use + /// @param _setupRenderer data to setup new renderer with + function setMetadataRenderer(address _token, address _newRendererImpl, bytes memory _setupRenderer) external { + if (msg.sender != IOwnable(_token).owner()) revert ONLY_TOKEN_OWNER(); + + address metadata = address(new ERC1967Proxy(_newRendererImpl, "")); + daoAddressesByToken[_token].metadata = metadata; + + if (_setupRenderer.length > 0) { + IBaseMetadata(metadata).initialize(_setupRenderer, _token); + } + + IToken(_token).setMetadataRenderer(IBaseMetadata(metadata)); + + emit MetadataRendererUpdated({ sender: msg.sender, renderer: metadata }); + } + /// /// /// DAO ADDRESSES /// /// /// diff --git a/src/token/metadata/interfaces/IBaseMetadata.sol b/src/metadata/interfaces/IBaseMetadata.sol similarity index 97% rename from src/token/metadata/interfaces/IBaseMetadata.sol rename to src/metadata/interfaces/IBaseMetadata.sol index a1ca513..15994a1 100644 --- a/src/token/metadata/interfaces/IBaseMetadata.sol +++ b/src/metadata/interfaces/IBaseMetadata.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { IUUPS } from "../../../lib/interfaces/IUUPS.sol"; +import { IUUPS } from "../../lib/interfaces/IUUPS.sol"; /// @title IBaseMetadata /// @author Rohan Kulkarni diff --git a/src/metadata/media/MediaMetadata.sol b/src/metadata/media/MediaMetadata.sol new file mode 100644 index 0000000..f1716d9 --- /dev/null +++ b/src/metadata/media/MediaMetadata.sol @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { UriEncode } from "sol-uriencode/src/UriEncode.sol"; +import { MetadataBuilder } from "micro-onchain-metadata-utils/MetadataBuilder.sol"; +import { MetadataJSONKeys } from "micro-onchain-metadata-utils/MetadataJSONKeys.sol"; + +import { UUPS } from "../../lib/proxy/UUPS.sol"; +import { Initializable } from "../../lib/utils/Initializable.sol"; +import { IOwnable } from "../../lib/interfaces/IOwnable.sol"; +import { ERC721 } from "../../lib/token/ERC721.sol"; + +import { MediaMetadataStorageV1 } from "./storage/MediaMetadataStorageV1.sol"; +import { IToken } from "../../token/IToken.sol"; +import { IMediaMetadata } from "./interfaces/IMediaMetadata.sol"; +import { IManager } from "../../manager/IManager.sol"; +import { IBaseMetadata } from "../interfaces/IBaseMetadata.sol"; +import { VersionedContract } from "../../VersionedContract.sol"; + +/// @title Media Metadata Renderer +/// @author Neokry +/// @notice A DAO's artwork generator and renderer +/// @custom:repo github.com/ourzora/nouns-protocol +contract MediaMetadata is IMediaMetadata, VersionedContract, Initializable, UUPS, MediaMetadataStorageV1 { + /// /// + /// CONSTANTS /// + /// /// + uint8 public constant SELECTION_TYPE_SEQUENTIAL = 0; + + uint8 public constant SELECTION_TYPE_RANDOM = 1; + + uint8 public constant SELECTION_TYPE_LOOP = 2; + + /// /// + /// IMMUTABLES /// + /// /// + + /// @notice The contract upgrade manager + IManager private immutable manager; + + /// /// + /// MODIFIERS /// + /// /// + + /// @notice Checks the token owner if the current action is allowed + modifier onlyOwner() { + if (owner() != msg.sender) { + revert IOwnable.ONLY_OWNER(); + } + + _; + } + + /// /// + /// CONSTRUCTOR /// + /// /// + + /// @param _manager The contract upgrade manager address + constructor(address _manager) payable initializer { + manager = IManager(_manager); + } + + /// /// + /// INITIALIZER /// + /// /// + + /// @notice Initializes a DAO's token metadata renderer + /// @param _data The encoded metadata initialization parameters + /// @param _token The ERC-721 token address + function initialize(bytes calldata _data, address _token) external initializer { + // Ensure the caller is the contract manager + if (msg.sender != address(manager)) { + revert ONLY_MANAGER(); + } + + // Decode the token initialization strings + IBaseMetadata.MetadataParams memory params = abi.decode(_data, (IBaseMetadata.MetadataParams)); + + // Store the renderer settings + settings.projectURI = params.projectURI; + settings.description = params.description; + settings.contractImage = params.contractImage; + settings.rendererBase = params.rendererBase; + settings.projectURI = params.projectURI; + settings.token = _token; + } + + /// /// + /// PROPERTIES & ITEMS /// + /// /// + + /// @notice The number of items in a property + /// @param _propertyId The property id + /// @return items array length + function mediaItemsCount(uint256 _propertyId) external view returns (uint256) { + return mediaItems.length; + } + + /// @notice Updates the additional token properties associated with the metadata. + /// @dev Be careful to not conflict with already used keys such as "name", "description", "properties", + function setAdditionalTokenProperties(AdditionalTokenProperty[] memory _additionalTokenProperties) external onlyOwner { + delete additionalTokenProperties; + for (uint256 i = 0; i < _additionalTokenProperties.length; i++) { + additionalTokenProperties.push(_additionalTokenProperties[i]); + } + + emit AdditionalTokenPropertiesSet(_additionalTokenProperties); + } + + /// @notice Deletes existing properties and/or items to be pseudo-randomly chosen from during token minting, replacing them with provided properties. WARNING: This function can alter or break existing token metadata if the number of properties for this renderer change before/after the upsert. If the properties selected in any tokens do not exist in the new version those token will not render + /// @dev We do not require the number of properties for an reset to match the existing property length, to allow multi-stage property additions (for e.g. when there are more properties than can fit in a single transaction) + /// @param _items The items to add to each property + function deleteAndRecreateMediaItems(MediaItem[] calldata _items) external onlyOwner { + delete mediaItems; + _addMediaItems(_items); + } + + function _addMediaItems(MediaItem[] calldata _items) internal { + // Cache the number of media items + uint256 numStoredMediaItems = mediaItems.length; + + // Cache the number of new properties + uint256 numNewMediaItems = _items.length; + + if (numNewMediaItems == 0) { + revert ONE_MEDIA_ITEM_REQUIRED(); + } + + unchecked { + for (uint256 i = 0; i < numNewMediaItems; ++i) { + // Append storage space + mediaItems.push(); + + // Get the new media item id + uint256 mediaItemId = numStoredMediaItems + i; + + // Store the media item + mediaItems[mediaItemId].imageURI = _items[i].imageURI; + mediaItems[mediaItemId].animationURI = _items[i].animationURI; + } + } + } + + /// /// + /// ATTRIBUTE GENERATION /// + /// /// + + /// @notice Generates attributes for a token upon mint + /// @param _tokenId The ERC-721 token id + function onMinted(uint256 _tokenId) external override returns (bool) { + // Ensure the caller is the token contract + if (msg.sender != settings.token) revert ONLY_TOKEN(); + + _generateMetadata(_tokenId); + } + + /// @notice Generates attributes for a requested set of tokens + /// @param startId The ERC-721 token id + /// @param endId The ERC-721 token id + function generateMetadataForTokenIds(uint256 startId, uint256 endId) external onlyOwner returns (bool[] memory results) { + uint256 tokensLen = endId + 1 - startId; + unchecked { + for (uint256 i = startId; i < tokensLen; ++i) { + results[i] = _generateMetadata(i); + } + } + } + + function _generateMetadata(uint256 _tokenId) internal returns (bool) { + // Compute some randomness for the token id + uint256 seed = _generateSeed(_tokenId); + + // Cache the total number of properties available + uint256 numMediaItems = mediaItems.length; + uint8 selectionType = settings.selectionType; + + if (numMediaItems == 0 || selectionType > 2 || (selectionType == SELECTION_TYPE_SEQUENTIAL && numMediaItems - 1 < _tokenId)) { + return false; + } + + if (selectionType == SELECTION_TYPE_SEQUENTIAL) tokenIdToSelectedMediaItem[_tokenId] = _tokenId; + if (selectionType == SELECTION_TYPE_RANDOM) tokenIdToSelectedMediaItem[_tokenId] = seed % numMediaItems; + if (selectionType == SELECTION_TYPE_LOOP) tokenIdToSelectedMediaItem[_tokenId] = _tokenId % numMediaItems; + + return true; + } + + /// @dev Generates a psuedo-random seed for a token id + function _generateSeed(uint256 _tokenId) private view returns (uint256) { + return uint256(keccak256(abi.encode(_tokenId, blockhash(block.number), block.coinbase, block.timestamp))); + } + + /// /// + /// URIs /// + /// /// + + /// @notice Internal getter function for token name + function _name() internal view returns (string memory) { + return ERC721(settings.token).name(); + } + + /// @notice The contract URI + function contractURI() external view override returns (string memory) { + MetadataBuilder.JSONItem[] memory items = new MetadataBuilder.JSONItem[](4); + + items[0] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyName, value: _name(), quote: true }); + items[1] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyDescription, value: settings.description, quote: true }); + items[2] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyImage, value: settings.contractImage, quote: true }); + items[3] = MetadataBuilder.JSONItem({ key: "external_url", value: settings.projectURI, quote: true }); + + return MetadataBuilder.generateEncodedJSON(items); + } + + /// @notice The token URI + /// @param _tokenId The ERC-721 token id + function tokenURI(uint256 _tokenId) external view returns (string memory) { + MediaItem storage mediaItem = mediaItems[tokenIdToSelectedMediaItem[_tokenId]]; + + MetadataBuilder.JSONItem[] memory items = new MetadataBuilder.JSONItem[](4); + + items[0] = MetadataBuilder.JSONItem({ + key: MetadataJSONKeys.keyName, + value: string.concat(_name(), " #", Strings.toString(_tokenId)), + quote: true + }); + items[1] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyDescription, value: settings.description, quote: true }); + items[2] = MetadataBuilder.JSONItem({ + key: MetadataJSONKeys.keyImage, + value: string.concat(settings.rendererBase, mediaItem.imageURI), + quote: true + }); + items[3] = MetadataBuilder.JSONItem({ + key: MetadataJSONKeys.keyAnimationURL, + value: string.concat(settings.rendererBase, mediaItem.animationURI), + quote: true + }); + + return MetadataBuilder.generateEncodedJSON(items); + } + + /// /// + /// METADATA SETTINGS /// + /// /// + + /// @notice The associated ERC-721 token + function token() external view returns (address) { + return settings.token; + } + + /// @notice The contract image + function contractImage() external view returns (string memory) { + return settings.contractImage; + } + + /// @notice The renderer base + function rendererBase() external view returns (string memory) { + return settings.rendererBase; + } + + /// @notice The collection description + function description() external view returns (string memory) { + return settings.description; + } + + /// @notice The collection description + function projectURI() external view returns (string memory) { + return settings.projectURI; + } + + /// @notice Get the owner of the metadata (here delegated to the token owner) + function owner() public view returns (address) { + return IOwnable(settings.token).owner(); + } + + /// /// + /// UPDATE SETTINGS /// + /// /// + + /// @notice Updates the contract image + /// @param _newContractImage The new contract image + function updateContractImage(string memory _newContractImage) external onlyOwner { + emit ContractImageUpdated(settings.contractImage, _newContractImage); + + settings.contractImage = _newContractImage; + } + + /// @notice Updates the collection description + /// @param _newDescription The new description + function updateDescription(string memory _newDescription) external onlyOwner { + emit DescriptionUpdated(settings.description, _newDescription); + + settings.description = _newDescription; + } + + function updateProjectURI(string memory _newProjectURI) external onlyOwner { + emit WebsiteURIUpdated(settings.projectURI, _newProjectURI); + + settings.projectURI = _newProjectURI; + } + + /// /// + /// METADATA UPGRADE /// + /// /// + + /// @notice Ensures the caller is authorized to upgrade the contract to a valid implementation + /// @dev This function is called in UUPS `upgradeTo` & `upgradeToAndCall` + /// @param _impl The address of the new implementation + function _authorizeUpgrade(address _impl) internal view override onlyOwner { + if (!manager.isRegisteredUpgrade(_getImplementation(), _impl)) revert INVALID_UPGRADE(_impl); + } +} diff --git a/src/metadata/media/interfaces/IMediaMetadata.sol b/src/metadata/media/interfaces/IMediaMetadata.sol new file mode 100644 index 0000000..5dc9b77 --- /dev/null +++ b/src/metadata/media/interfaces/IMediaMetadata.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +import { MediaMetadataTypesV1 } from "../types/MediaMetadataTypesV1.sol"; +import { IBaseMetadata } from "../../interfaces/IBaseMetadata.sol"; + +/// @title IMediaMetadataRenderer +/// @author Neokry +/// @notice The external Metadata Renderer events, errors, and functions +interface IMediaMetadata is IBaseMetadata, MediaMetadataTypesV1 { + /// /// + /// EVENTS /// + /// /// + + /// @notice Additional token properties have been set + event AdditionalTokenPropertiesSet(AdditionalTokenProperty[] _additionalJsonProperties); + + /// @notice Emitted when the contract image is updated + event ContractImageUpdated(string prevImage, string newImage); + + /// @notice Emitted when the collection description is updated + event DescriptionUpdated(string prevDescription, string newDescription); + + /// @notice Emitted when the collection uri is updated + event WebsiteURIUpdated(string lastURI, string newURI); + + /// /// + /// ERRORS /// + /// /// + + /// @dev Reverts if the caller isn't the token contract + error ONLY_TOKEN(); + + /// @dev Reverts if the caller does not include a media item during an artwork upload + error ONE_MEDIA_ITEM_REQUIRED(); + + /// @dev Reverts if the selection type is invalid + error INVALID_SELECTION_TYPE(); + + /// /// + /// FUNCTIONS /// + /// /// + + /// @notice The contract image + function contractImage() external view returns (string memory); + + /// @notice The collection description + function description() external view returns (string memory); + + /// @notice Updates the contract image + /// @param newContractImage The new contract image + function updateContractImage(string memory newContractImage) external; + + /// @notice Updates the collection description + /// @param newDescription The new description + function updateDescription(string memory newDescription) external; +} diff --git a/src/metadata/media/storage/MediaMetadataStorageV1.sol b/src/metadata/media/storage/MediaMetadataStorageV1.sol new file mode 100644 index 0000000..488aba0 --- /dev/null +++ b/src/metadata/media/storage/MediaMetadataStorageV1.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +import { MediaMetadataTypesV1 } from "../types/MediaMetadataTypesV1.sol"; + +/// @title MediaMetadataTypesV1 +/// @author Neokry +/// @notice The Metadata Renderer storage contract +contract MediaMetadataStorageV1 is MediaMetadataTypesV1 { + /// @notice The metadata renderer settings + Settings public settings; + + /// @notice The media items chosen from upon generation + MediaItem[] public mediaItems; + + /// @notice The attributes generated for a token - mapping of tokenID uint16 array + /// @dev Array of size 16 1st element [0] used for number of attributes chosen, next N elements for those selections + /// @dev token ID + mapping(uint256 => uint256) public tokenIdToSelectedMediaItem; + + /// @notice Additional JSON key/value properties for each token. + /// @dev While strings are quoted, JSON needs to be escaped. + AdditionalTokenProperty[] internal additionalTokenProperties; +} diff --git a/src/metadata/media/types/MediaMetadataTypesV1.sol b/src/metadata/media/types/MediaMetadataTypesV1.sol new file mode 100644 index 0000000..b2b50da --- /dev/null +++ b/src/metadata/media/types/MediaMetadataTypesV1.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +/// @title MediaMetadataTypesV1 +/// @author Neokry +/// @notice The Metadata Renderer custom data types +interface MediaMetadataTypesV1 { + struct MediaItem { + string imageURI; + string animationURI; + } + + struct Settings { + address token; + string projectURI; + string description; + string contractImage; + string rendererBase; + uint8 selectionType; + } + + struct AdditionalTokenProperty { + string key; + string value; + bool quote; + } +} diff --git a/src/token/metadata/MetadataRenderer.sol b/src/metadata/property/PropertyMetadata.sol similarity index 94% rename from src/token/metadata/MetadataRenderer.sol rename to src/metadata/property/PropertyMetadata.sol index 6c28481..299535f 100644 --- a/src/token/metadata/MetadataRenderer.sol +++ b/src/metadata/property/PropertyMetadata.sol @@ -12,26 +12,19 @@ import { Initializable } from "../../lib/utils/Initializable.sol"; import { IOwnable } from "../../lib/interfaces/IOwnable.sol"; import { ERC721 } from "../../lib/token/ERC721.sol"; -import { MetadataRendererStorageV1 } from "./storage/MetadataRendererStorageV1.sol"; -import { MetadataRendererStorageV2 } from "./storage/MetadataRendererStorageV2.sol"; +import { PropertyMetadataStorageV1 } from "./storage/PropertyMetadataStorageV1.sol"; +import { PropertyMetadataStorageV2 } from "./storage/PropertyMetadataStorageV2.sol"; import { IToken } from "../../token/IToken.sol"; -import { IPropertyIPFSMetadataRenderer } from "./interfaces/IPropertyIPFSMetadataRenderer.sol"; +import { IPropertyMetadata } from "./interfaces/IPropertyMetadata.sol"; import { IManager } from "../../manager/IManager.sol"; -import { IBaseMetadata } from "./interfaces/IBaseMetadata.sol"; +import { IBaseMetadata } from "../interfaces/IBaseMetadata.sol"; import { VersionedContract } from "../../VersionedContract.sol"; -/// @title Metadata Renderer +/// @title Property IPFS Metadata Renderer /// @author Iain Nash & Rohan Kulkarni /// @notice A DAO's artwork generator and renderer /// @custom:repo github.com/ourzora/nouns-protocol -contract MetadataRenderer is - IPropertyIPFSMetadataRenderer, - VersionedContract, - Initializable, - UUPS, - MetadataRendererStorageV1, - MetadataRendererStorageV2 -{ +contract PropertyMetadata is IPropertyMetadata, VersionedContract, Initializable, UUPS, PropertyMetadataStorageV1, PropertyMetadataStorageV2 { /// /// /// IMMUTABLES /// /// /// @@ -228,38 +221,19 @@ contract MetadataRenderer is function onMinted(uint256 _tokenId) external override returns (bool) { // Ensure the caller is the token contract if (msg.sender != settings.token) revert ONLY_TOKEN(); + return _generateMetadata(_tokenId); + } - // Compute some randomness for the token id - uint256 seed = _generateSeed(_tokenId); - - // Get the pointer to store generated attributes - uint16[16] storage tokenAttributes = attributes[_tokenId]; - - // Cache the total number of properties available - uint256 numProperties = properties.length; - - if (numProperties == 0) { - return false; - } - - // Store the total as reference in the first slot of the token's array of attributes - tokenAttributes[0] = uint16(numProperties); - + /// @notice Generates attributes for a requested set of tokens + /// @param startId The ERC-721 token id + /// @param endId The ERC-721 token id + function generateMetadataForTokenIds(uint256 startId, uint256 endId) external onlyOwner returns (bool[] memory results) { + uint256 tokensLen = endId + 1 - startId; unchecked { - // For each property: - for (uint256 i = 0; i < numProperties; ++i) { - // Get the number of items to choose from - uint256 numItems = properties[i].items.length; - - // Use the token's seed to select an item - tokenAttributes[i + 1] = uint16(seed % numItems); - - // Adjust the randomness - seed >>= 16; + for (uint256 i = startId; i < tokensLen; ++i) { + results[i] = _generateMetadata(i); } } - - return true; } /// @notice The properties and query string for a generated token @@ -311,6 +285,40 @@ contract MetadataRenderer is } } + function _generateMetadata(uint256 _tokenId) private returns (bool) { + // Compute some randomness for the token id + uint256 seed = _generateSeed(_tokenId); + + // Get the pointer to store generated attributes + uint16[16] storage tokenAttributes = attributes[_tokenId]; + + // Cache the total number of properties available + uint256 numProperties = properties.length; + + if (numProperties == 0) { + return false; + } + + // Store the total as reference in the first slot of the token's array of attributes + tokenAttributes[0] = uint16(numProperties); + + unchecked { + // For each property: + for (uint256 i = 0; i < numProperties; ++i) { + // Get the number of items to choose from + uint256 numItems = properties[i].items.length; + + // Use the token's seed to select an item + tokenAttributes[i + 1] = uint16(seed % numItems); + + // Adjust the randomness + seed >>= 16; + } + } + + return true; + } + /// @dev Generates a psuedo-random seed for a token id function _generateSeed(uint256 _tokenId) private view returns (uint256) { return uint256(keccak256(abi.encode(_tokenId, blockhash(block.number), block.coinbase, block.timestamp))); diff --git a/src/token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol b/src/metadata/property/interfaces/IPropertyMetadata.sol similarity index 87% rename from src/token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol rename to src/metadata/property/interfaces/IPropertyMetadata.sol index 1a8df9a..614c54d 100644 --- a/src/token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol +++ b/src/metadata/property/interfaces/IPropertyMetadata.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { MetadataRendererTypesV1 } from "../types/MetadataRendererTypesV1.sol"; -import { MetadataRendererTypesV2 } from "../types/MetadataRendererTypesV2.sol"; -import { IBaseMetadata } from "./IBaseMetadata.sol"; +import { PropertyMetadataTypesV1 } from "../types/PropertyMetadataTypesV1.sol"; +import { PropertyMetadataTypesV2 } from "../types/PropertyMetadataTypesV2.sol"; +import { IBaseMetadata } from "../../interfaces/IBaseMetadata.sol"; -/// @title IPropertyIPFSMetadataRenderer +/// @title IPropertyMetadata /// @author Iain Nash & Rohan Kulkarni /// @notice The external Metadata Renderer events, errors, and functions -interface IPropertyIPFSMetadataRenderer is IBaseMetadata, MetadataRendererTypesV1, MetadataRendererTypesV2 { +interface IPropertyMetadata is IBaseMetadata, PropertyMetadataTypesV1, PropertyMetadataTypesV2 { /// /// /// EVENTS /// /// /// @@ -50,6 +50,8 @@ interface IPropertyIPFSMetadataRenderer is IBaseMetadata, MetadataRendererTypesV /// error TOO_MANY_PROPERTIES(); + error TOKEN_ALREADY_GENERATED(uint256 tokenId); + /// /// /// FUNCTIONS /// /// /// @@ -58,11 +60,7 @@ interface IPropertyIPFSMetadataRenderer is IBaseMetadata, MetadataRendererTypesV /// @param names The names of the properties to add /// @param items The items to add to each property /// @param ipfsGroup The IPFS base URI and extension - function addProperties( - string[] calldata names, - ItemParam[] calldata items, - IPFSGroup calldata ipfsGroup - ) external; + function addProperties(string[] calldata names, ItemParam[] calldata items, IPFSGroup calldata ipfsGroup) external; /// @notice The number of properties function propertiesCount() external view returns (uint256); diff --git a/src/token/metadata/storage/MetadataRendererStorageV1.sol b/src/metadata/property/storage/PropertyMetadataStorageV1.sol similarity index 79% rename from src/token/metadata/storage/MetadataRendererStorageV1.sol rename to src/metadata/property/storage/PropertyMetadataStorageV1.sol index be0f856..474b426 100644 --- a/src/token/metadata/storage/MetadataRendererStorageV1.sol +++ b/src/metadata/property/storage/PropertyMetadataStorageV1.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { MetadataRendererTypesV1 } from "../types/MetadataRendererTypesV1.sol"; +import { PropertyMetadataTypesV1 } from "../types/PropertyMetadataTypesV1.sol"; -/// @title MetadataRendererTypesV1 +/// @title PropertyMetadataTypesV1 /// @author Iain Nash & Rohan Kulkarni /// @notice The Metadata Renderer storage contract -contract MetadataRendererStorageV1 is MetadataRendererTypesV1 { +contract PropertyMetadataStorageV1 is PropertyMetadataTypesV1 { /// @notice The metadata renderer settings Settings public settings; diff --git a/src/token/metadata/storage/MetadataRendererStorageV2.sol b/src/metadata/property/storage/PropertyMetadataStorageV2.sol similarity index 66% rename from src/token/metadata/storage/MetadataRendererStorageV2.sol rename to src/metadata/property/storage/PropertyMetadataStorageV2.sol index 3b28adc..2c5ecbf 100644 --- a/src/token/metadata/storage/MetadataRendererStorageV2.sol +++ b/src/metadata/property/storage/PropertyMetadataStorageV2.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { MetadataRendererTypesV2 } from "../types/MetadataRendererTypesV2.sol"; +import { PropertyMetadataTypesV2 } from "../types/PropertyMetadataTypesV2.sol"; -/// @title MetadataRendererTypesV1 +/// @title PropertyMetadataTypesV1 /// @author Iain Nash & Rohan Kulkarni /// @notice The Metadata Renderer storage contract -contract MetadataRendererStorageV2 is MetadataRendererTypesV2 { +contract PropertyMetadataStorageV2 is PropertyMetadataTypesV2 { /// @notice Additional JSON key/value properties for each token. /// @dev While strings are quoted, JSON needs to be escaped. AdditionalTokenProperty[] internal additionalTokenProperties; diff --git a/src/token/metadata/types/MetadataRendererTypesV1.sol b/src/metadata/property/types/PropertyMetadataTypesV1.sol similarity index 90% rename from src/token/metadata/types/MetadataRendererTypesV1.sol rename to src/metadata/property/types/PropertyMetadataTypesV1.sol index 062e7b7..ca88604 100644 --- a/src/token/metadata/types/MetadataRendererTypesV1.sol +++ b/src/metadata/property/types/PropertyMetadataTypesV1.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -/// @title MetadataRendererTypesV1 +/// @title PropertyMetadataTypesV1 /// @author Iain Nash & Rohan Kulkarni /// @notice The Metadata Renderer custom data types -interface MetadataRendererTypesV1 { +interface PropertyMetadataTypesV1 { struct ItemParam { uint256 propertyId; string name; diff --git a/src/token/metadata/types/MetadataRendererTypesV2.sol b/src/metadata/property/types/PropertyMetadataTypesV2.sol similarity index 78% rename from src/token/metadata/types/MetadataRendererTypesV2.sol rename to src/metadata/property/types/PropertyMetadataTypesV2.sol index 9321780..fce8856 100644 --- a/src/token/metadata/types/MetadataRendererTypesV2.sol +++ b/src/metadata/property/types/PropertyMetadataTypesV2.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -/// @title MetadataRendererTypesV2 +/// @title PropertyMetadataTypesV2 /// @author Iain Nash & Rohan Kulkarni /// @notice The Metadata Renderer custom data types -interface MetadataRendererTypesV2 { +interface PropertyMetadataTypesV2 { struct AdditionalTokenProperty { string key; string value; diff --git a/src/token/IToken.sol b/src/token/IToken.sol index de5465e..76382a9 100644 --- a/src/token/IToken.sol +++ b/src/token/IToken.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.16; import { IUUPS } from "../lib/interfaces/IUUPS.sol"; import { IERC721Votes } from "../lib/interfaces/IERC721Votes.sol"; import { IManager } from "../manager/IManager.sol"; +import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol"; import { TokenTypesV1 } from "./types/TokenTypesV1.sol"; import { TokenTypesV2 } from "./types/TokenTypesV2.sol"; @@ -36,6 +37,10 @@ interface IToken is IUUPS, IERC721Votes, TokenTypesV1, TokenTypesV2 { /// @param allowed Whether address is allowed to mint event MinterUpdated(address minter, bool allowed); + /// @notice Event emitted when metadata renderer is updated. + /// @param renderer new metadata renderer address + event MetadataRendererUpdated(address renderer); + /// /// /// ERRORS /// /// /// @@ -146,6 +151,10 @@ interface IToken is IUUPS, IERC721Votes, TokenTypesV1, TokenTypesV2 { /// @param _minter Address to check function isMinter(address _minter) external view returns (bool); + /// @notice Set a new metadata renderer + /// @param newRenderer new renderer address to use + function setMetadataRenderer(IBaseMetadata newRenderer) external; + /// @notice Callback called by auction on first auction started to transfer ownership to treasury from founder function onFirstAuctionStarted() external; } diff --git a/src/token/Token.sol b/src/token/Token.sol index 3da5c98..b1a0e12 100644 --- a/src/token/Token.sol +++ b/src/token/Token.sol @@ -8,7 +8,7 @@ import { ERC721 } from "../lib/token/ERC721.sol"; import { Ownable } from "../lib/utils/Ownable.sol"; import { TokenStorageV1 } from "./storage/TokenStorageV1.sol"; import { TokenStorageV2 } from "./storage/TokenStorageV2.sol"; -import { IBaseMetadata } from "./metadata/interfaces/IBaseMetadata.sol"; +import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol"; import { IManager } from "../manager/IManager.sol"; import { IAuction } from "../auction/IAuction.sol"; import { IToken } from "./IToken.sol"; @@ -454,6 +454,17 @@ contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC return minter[_minter]; } + /// @notice Set a new metadata renderer + /// @param newRenderer new renderer address to use + function setMetadataRenderer(IBaseMetadata newRenderer) external { + // Ensure the caller is the contract manager + if (msg.sender != address(manager)) { + revert ONLY_MANAGER(); + } + + settings.metadataRenderer = newRenderer; + } + /// /// /// TOKEN UPGRADE /// /// /// diff --git a/src/token/types/TokenTypesV1.sol b/src/token/types/TokenTypesV1.sol index e6fb4be..72162ea 100644 --- a/src/token/types/TokenTypesV1.sol +++ b/src/token/types/TokenTypesV1.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol"; +import { IBaseMetadata } from "../../metadata/interfaces/IBaseMetadata.sol"; /// @title TokenTypesV1 /// @author Rohan Kulkarni diff --git a/test/MetadataRenderer.t.sol b/test/MetadataRenderer.t.sol index f77c7db..7b463c8 100644 --- a/test/MetadataRenderer.t.sol +++ b/test/MetadataRenderer.t.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.16; import { NounsBuilderTest } from "./utils/NounsBuilderTest.sol"; -import { MetadataRendererTypesV1 } from "../src/token/metadata/types/MetadataRendererTypesV1.sol"; -import { MetadataRendererTypesV2 } from "../src/token/metadata/types/MetadataRendererTypesV2.sol"; +import { PropertyMetadataTypesV1 } from "../src/metadata/property/types/PropertyMetadataTypesV1.sol"; +import { PropertyMetadataTypesV2 } from "../src/metadata/property/types/PropertyMetadataTypesV2.sol"; import { Base64URIDecoder } from "./utils/Base64URIDecoder.sol"; import "forge-std/console2.sol"; -contract MetadataRendererTest is NounsBuilderTest, MetadataRendererTypesV1 { +contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { function setUp() public virtual override { super.setUp(); @@ -94,7 +94,7 @@ contract MetadataRendererTest is NounsBuilderTest, MetadataRendererTypesV1 { function testRevert_CannotExceedMaxProperties() public { string[] memory names = new string[](16); - MetadataRendererTypesV1.ItemParam[] memory items = new MetadataRendererTypesV1.ItemParam[](16); + PropertyMetadataTypesV1.ItemParam[] memory items = new PropertyMetadataTypesV1.ItemParam[](16); for (uint256 j; j < 16; j++) { names[j] = "aaa"; // Add random properties @@ -104,7 +104,7 @@ contract MetadataRendererTest is NounsBuilderTest, MetadataRendererTypesV1 { items[j].isNewProperty = true; } - MetadataRendererTypesV1.IPFSGroup memory group = MetadataRendererTypesV1.IPFSGroup("aaa", "aaa"); + PropertyMetadataTypesV1.IPFSGroup memory group = PropertyMetadataTypesV1.IPFSGroup("aaa", "aaa"); vm.prank(founder); vm.expectRevert(abi.encodeWithSignature("TOO_MANY_PROPERTIES()")); @@ -187,9 +187,9 @@ contract MetadataRendererTest is NounsBuilderTest, MetadataRendererTypesV1 { vm.prank(address(auction)); token.mint(); - MetadataRendererTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](2); - additionalTokenProperties[0] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); - additionalTokenProperties[1] = MetadataRendererTypesV2.AdditionalTokenProperty({ + PropertyMetadataTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](2); + additionalTokenProperties[0] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); + additionalTokenProperties[1] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "participationAgreement", value: "This is a JSON quoted participation agreement.", quote: true @@ -238,9 +238,9 @@ contract MetadataRendererTest is NounsBuilderTest, MetadataRendererTypesV1 { vm.prank(address(auction)); token.mint(); - MetadataRendererTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](2); - additionalTokenProperties[0] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); - additionalTokenProperties[1] = MetadataRendererTypesV2.AdditionalTokenProperty({ + PropertyMetadataTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](2); + additionalTokenProperties[0] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); + additionalTokenProperties[1] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "participationAgreement", value: "This is a JSON quoted participation agreement.", quote: true @@ -250,7 +250,7 @@ contract MetadataRendererTest is NounsBuilderTest, MetadataRendererTypesV1 { string memory withAdditionalTokenProperties = token.tokenURI(0); - MetadataRendererTypesV2.AdditionalTokenProperty[] memory clearedTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](0); + PropertyMetadataTypesV2.AdditionalTokenProperty[] memory clearedTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](0); vm.prank(founder); metadataRenderer.setAdditionalTokenProperties(clearedTokenProperties); @@ -282,9 +282,9 @@ contract MetadataRendererTest is NounsBuilderTest, MetadataRendererTypesV1 { vm.prank(address(auction)); token.mint(); - MetadataRendererTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](2); - additionalTokenProperties[0] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); - additionalTokenProperties[1] = MetadataRendererTypesV2.AdditionalTokenProperty({ + PropertyMetadataTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](2); + additionalTokenProperties[0] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); + additionalTokenProperties[1] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "participationAgreement", value: "This is a JSON quoted participation agreement.", quote: true @@ -294,7 +294,7 @@ contract MetadataRendererTest is NounsBuilderTest, MetadataRendererTypesV1 { string memory withAdditionalTokenProperties = token.tokenURI(0); - MetadataRendererTypesV2.AdditionalTokenProperty[] memory clearedTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](0); + PropertyMetadataTypesV2.AdditionalTokenProperty[] memory clearedTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](0); vm.prank(founder); metadataRenderer.setAdditionalTokenProperties(clearedTokenProperties); diff --git a/test/forking/TestUpdateMinters.t.sol b/test/forking/TestUpdateMinters.t.sol index 05e7868..4a0318a 100644 --- a/test/forking/TestUpdateMinters.t.sol +++ b/test/forking/TestUpdateMinters.t.sol @@ -6,7 +6,7 @@ import { Treasury } from "../../src/governance/treasury/Treasury.sol"; import { Auction } from "../../src/auction/Auction.sol"; import { IAuction } from "../../src/auction/IAuction.sol"; import { Token } from "../../src/token/Token.sol"; -import { MetadataRenderer } from "../../src/token/metadata/MetadataRenderer.sol"; +import { PropertyMetadata } from "../../src/metadata/property/PropertyMetadata.sol"; import { Governor } from "../../src/governance/governor/Governor.sol"; import { IManager } from "../../src/manager/IManager.sol"; import { Manager } from "../../src/manager/Manager.sol"; @@ -22,7 +22,7 @@ contract TestUpdateMinters is Test { Auction internal immutable auction = Auction(0x658D3A1B6DaBcfbaa8b75cc182Bf33efefDC200d); Governor internal immutable governor = Governor(0xe3F8d5488C69d18ABda42FCA10c177d7C19e8B1a); Treasury internal immutable treasury = Treasury(payable(0xDC9b96Ea4966d063Dd5c8dbaf08fe59062091B6D)); - MetadataRenderer internal immutable metadata = MetadataRenderer(0x963ac521C595D3D1BE72C1Eb057f24D4D42CB70b); + PropertyMetadata internal immutable metadata = PropertyMetadata(0x963ac521C595D3D1BE72C1Eb057f24D4D42CB70b); function setUp() public { uint256 mainnetFork = vm.createFork(vm.envString("ETH_RPC_MAINNET"), 16585958); diff --git a/test/utils/NounsBuilderTest.sol b/test/utils/NounsBuilderTest.sol index 548c468..cde31dc 100644 --- a/test/utils/NounsBuilderTest.sol +++ b/test/utils/NounsBuilderTest.sol @@ -5,11 +5,11 @@ import { Test } from "forge-std/Test.sol"; import { IManager, Manager } from "../../src/manager/Manager.sol"; import { IToken, Token } from "../../src/token/Token.sol"; -import { IBaseMetadata, MetadataRenderer } from "../../src/token/metadata/MetadataRenderer.sol"; +import { IBaseMetadata, PropertyMetadata } from "../../src/metadata/property/PropertyMetadata.sol"; import { IAuction, Auction } from "../../src/auction/Auction.sol"; import { IGovernor, Governor } from "../../src/governance/governor/Governor.sol"; import { ITreasury, Treasury } from "../../src/governance/treasury/Treasury.sol"; -import { MetadataRendererTypesV1 } from "../../src/token/metadata/types/MetadataRendererTypesV1.sol"; +import { PropertyMetadataTypesV1 } from "../../src/metadata/property/types/PropertyMetadataTypesV1.sol"; import { ERC1967Proxy } from "../../src/lib/proxy/ERC1967Proxy.sol"; import { MockERC721 } from "../utils/mocks/MockERC721.sol"; @@ -62,7 +62,7 @@ contract NounsBuilderTest is Test { manager = Manager(address(new ERC1967Proxy(managerImpl0, abi.encodeWithSignature("initialize(address)", zoraDAO)))); tokenImpl = address(new Token(address(manager))); - metadataRendererImpl = address(new MetadataRenderer(address(manager))); + metadataRendererImpl = address(new PropertyMetadata(address(manager))); auctionImpl = address(new Auction(address(manager), weth)); treasuryImpl = address(new Treasury(address(manager))); governorImpl = address(new Governor(address(manager))); @@ -199,11 +199,11 @@ contract NounsBuilderTest is Test { string[] memory names = new string[](1); names[0] = "testing"; - MetadataRendererTypesV1.ItemParam[] memory items = new MetadataRendererTypesV1.ItemParam[](2); - items[0] = MetadataRendererTypesV1.ItemParam({ propertyId: 0, name: "failure1", isNewProperty: true }); - items[1] = MetadataRendererTypesV1.ItemParam({ propertyId: 0, name: "failure2", isNewProperty: true }); + PropertyMetadataTypesV1.ItemParam[] memory items = new PropertyMetadataTypesV1.ItemParam[](2); + items[0] = PropertyMetadataTypesV1.ItemParam({ propertyId: 0, name: "failure1", isNewProperty: true }); + items[1] = PropertyMetadataTypesV1.ItemParam({ propertyId: 0, name: "failure2", isNewProperty: true }); - MetadataRendererTypesV1.IPFSGroup memory ipfsGroup = MetadataRendererTypesV1.IPFSGroup({ baseUri: "BASE_URI", extension: "EXTENSION" }); + PropertyMetadataTypesV1.IPFSGroup memory ipfsGroup = PropertyMetadataTypesV1.IPFSGroup({ baseUri: "BASE_URI", extension: "EXTENSION" }); vm.prank(metadataRenderer.owner()); metadataRenderer.addProperties(names, items, ipfsGroup); @@ -231,7 +231,7 @@ contract NounsBuilderTest is Test { /// /// Token internal token; - MetadataRenderer internal metadataRenderer; + PropertyMetadata internal metadataRenderer; Auction internal auction; Treasury internal treasury; Governor internal governor; @@ -313,7 +313,7 @@ contract NounsBuilderTest is Test { ); token = Token(_token); - metadataRenderer = MetadataRenderer(_metadata); + metadataRenderer = PropertyMetadata(_metadata); auction = Auction(_auction); treasury = Treasury(payable(_treasury)); governor = Governor(_governor);