From 8f4625684e84fbe190b6915466cf1baec889796d Mon Sep 17 00:00:00 2001 From: neokry Date: Wed, 18 Oct 2023 16:29:43 +0700 Subject: [PATCH] refactor to keep manager types and reduce diff --- script/DeployMetadataUpgrade.s.sol | 92 ------ script/DeployNewDAO.s.sol | 4 +- script/DeployTokenUpgrade.s.sol | 103 ------- script/DeployV2Core.s.sol | 24 +- script/DeployV2New.s.sol | 10 - script/DeployVersion1_1.s.sol | 96 ------ script/GetInterfaceIds.s.sol | 6 +- script/MockTreasury.sol | 19 -- src/auction/Auction.sol | 32 +- src/auction/IAuction.sol | 28 +- src/auction/storage/AuctionStorageV1.sol | 4 +- src/governance/governor/Governor.sol | 50 ++-- src/governance/governor/IGovernor.sol | 30 +- .../governor/types/GovernorTypesV1.sol | 4 +- src/governance/treasury/ITreasury.sol | 14 +- src/governance/treasury/Treasury.sol | 10 +- src/manager/IManager.sol | 70 +++-- src/manager/Manager.sol | 237 ++++++++++----- src/manager/storage/ManagerStorageV2.sol | 11 - src/metadata/interfaces/IBaseMetadata.sol | 83 ------ src/metadata/media/MediaMetadata.sol | 281 ------------------ .../media/interfaces/IMediaMetadata.sol | 40 --- .../media/storage/MediaMetadataStorageV1.sol | 19 -- .../media/types/MediaMetadataTypesV1.sol | 31 -- src/minters/ERC721RedeemMinter.sol | 23 +- src/minters/MerkleReserveMinter.sol | 24 +- src/minters/interfaces/IMintStrategy.sol | 11 - src/token/default/IToken.sol | 60 ++-- src/token/default/Token.sol | 40 +-- src/token/default/types/TokenTypesV1.sol | 2 +- src/token/interfaces/IBaseToken.sol | 71 ----- .../metadata/MetadataRenderer.sol} | 129 ++++---- .../metadata/interfaces/IBaseMetadata.sol | 46 +++ .../IPropertyIPFSMetadataRenderer.sol} | 48 +-- .../storage/MetadataRendererStorageV1.sol} | 6 +- .../storage/MetadataRendererStorageV2.sol} | 6 +- .../types/MetadataRendererTypesV1.sol} | 4 +- .../types/MetadataRendererTypesV2.sol} | 4 +- .../partial-mirror/IPartialMirrorToken.sol | 35 +-- .../partial-mirror/PartialMirrorToken.sol | 41 +-- .../types/PartialMirrorTokenTypesV1.sol | 2 +- test/Auction.t.sol | 6 +- test/ERC721RedeemMinter.t.sol | 36 ++- test/Gov.t.sol | 16 +- test/Manager.t.sol | 73 +---- test/MediaMetadata.t.sol | 165 ---------- test/MerkleReserveMinter.t.sol | 22 +- test/MetadataRenderer.t.sol | 32 +- test/PartialMirrorToken.t.sol | 71 +---- test/Token.t.sol | 41 +-- test/forking/TestUpdateMinters.t.sol | 4 +- test/utils/NounsBuilderTest.sol | 197 ++++++------ test/utils/mocks/MockMinter.sol | 12 - 53 files changed, 664 insertions(+), 1861 deletions(-) delete mode 100644 script/DeployMetadataUpgrade.s.sol delete mode 100644 script/DeployTokenUpgrade.s.sol delete mode 100644 script/DeployVersion1_1.s.sol delete mode 100644 script/MockTreasury.sol delete mode 100644 src/manager/storage/ManagerStorageV2.sol delete mode 100644 src/metadata/interfaces/IBaseMetadata.sol delete mode 100644 src/metadata/media/MediaMetadata.sol delete mode 100644 src/metadata/media/interfaces/IMediaMetadata.sol delete mode 100644 src/metadata/media/storage/MediaMetadataStorageV1.sol delete mode 100644 src/metadata/media/types/MediaMetadataTypesV1.sol delete mode 100644 src/minters/interfaces/IMintStrategy.sol delete mode 100644 src/token/interfaces/IBaseToken.sol rename src/{metadata/property/PropertyMetadata.sol => token/metadata/MetadataRenderer.sol} (89%) create mode 100644 src/token/metadata/interfaces/IBaseMetadata.sol rename src/{metadata/property/interfaces/IPropertyMetadata.sol => token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol} (69%) rename src/{metadata/property/storage/PropertyMetadataStorageV1.sol => token/metadata/storage/MetadataRendererStorageV1.sol} (79%) rename src/{metadata/property/storage/PropertyMetadataStorageV2.sol => token/metadata/storage/MetadataRendererStorageV2.sol} (66%) rename src/{metadata/property/types/PropertyMetadataTypesV1.sol => token/metadata/types/MetadataRendererTypesV1.sol} (90%) rename src/{metadata/property/types/PropertyMetadataTypesV2.sol => token/metadata/types/MetadataRendererTypesV2.sol} (78%) delete mode 100644 test/MediaMetadata.t.sol delete mode 100644 test/utils/mocks/MockMinter.sol diff --git a/script/DeployMetadataUpgrade.s.sol b/script/DeployMetadataUpgrade.s.sol deleted file mode 100644 index e72af27..0000000 --- a/script/DeployMetadataUpgrade.s.sol +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; - -import { IManager, Manager } from "../src/manager/Manager.sol"; -import { IToken, Token } from "../src/token/default/Token.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 { 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 { - using Strings for uint256; - - function run() public { - uint256 chainID = vm.envUint("CHAIN_ID"); - console.log("CHAIN_ID", chainID); - uint256 key = vm.envUint("PRIVATE_KEY"); - address deployerAddress = vm.addr(key); - address managerProxy = vm.envAddress("MANAGER_PROXY"); - address tokenImpl = vm.envAddress("TOKEN_IMPLEMENTATION"); - address auctionImpl = vm.envAddress("AUCTION_IMPLEMENTATION"); - address treasuryImpl = vm.envAddress("TREASURY_IMPLEMENTATION"); - address governorImpl = vm.envAddress("GOVERNOR_IMPLEMENTATION"); - - console2.log("~~~~~~~~~~ DEPLOYER ADDRESS ~~~~~~~~~~~"); - console2.logAddress(deployerAddress); - - console2.log("~~~~~~~~~~ TOKEN IMPL ~~~~~~~~~~~"); - console2.logAddress(tokenImpl); - - console2.log("~~~~~~~~~~ MANAGER PROXY ~~~~~~~~~~~"); - console2.logAddress(managerProxy); - - console2.log("~~~~~~~~~~ AUCTION IMPL ~~~~~~~~~~~"); - console2.logAddress(auctionImpl); - - console2.log("~~~~~~~~~~ TREASURY IMPL ~~~~~~~~~~~"); - console2.logAddress(treasuryImpl); - - console2.log("~~~~~~~~~~ GOVERNOR IMPL ~~~~~~~~~~~"); - console2.logAddress(governorImpl); - - vm.startBroadcast(deployerAddress); - - // Deploy root manager implementation + proxy - Manager manager = Manager(managerProxy); - - // Deploy metadata renderer implementation - address metadataRendererImpl = address(new PropertyMetadata(managerProxy)); - - address managerImpl = address(new Manager()); - - console2.log("MR"); - console2.log(metadataRendererImpl); - - console2.log("M"); - console2.log(managerImpl); - - // console2.log("OWNER", manager.owner()); - - // manager.upgradeTo(managerImpl); - - vm.stopBroadcast(); - - string memory filePath = string(abi.encodePacked("deploys/", chainID.toString(), ".upgradeMetadata.txt")); - vm.writeFile(filePath, ""); - vm.writeLine(filePath, string(abi.encodePacked("Metadata Renderer implementation: ", addressToString(metadataRendererImpl)))); - vm.writeLine(filePath, string(abi.encodePacked("Manager implementation: ", addressToString(managerImpl)))); - } - - function addressToString(address _addr) private pure returns (string memory) { - bytes memory s = new bytes(40); - for (uint256 i = 0; i < 20; i++) { - bytes1 b = bytes1(uint8(uint256(uint160(_addr)) / (2**(8 * (19 - i))))); - bytes1 hi = bytes1(uint8(b) / 16); - bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); - s[2 * i] = char(hi); - s[2 * i + 1] = char(lo); - } - return string(abi.encodePacked("0x", string(s))); - } - - function char(bytes1 b) private pure returns (bytes1 c) { - if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); - else return bytes1(uint8(b) + 0x57); - } -} diff --git a/script/DeployNewDAO.s.sol b/script/DeployNewDAO.s.sol index e741dce..4157976 100644 --- a/script/DeployNewDAO.s.sol +++ b/script/DeployNewDAO.s.sol @@ -6,13 +6,14 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IManager } from "../src/manager/IManager.sol"; import { IPartialMirrorToken } from "../src/token/partial-mirror/IPartialMirrorToken.sol"; -import { IPropertyMetadata } from "../src/metadata/property/interfaces/IPropertyMetadata.sol"; +import { IBaseMetadata } from "../src/token/metadata/interfaces/IBaseMetadata.sol"; import { IAuction } from "../src/auction/IAuction.sol"; import { IGovernor } from "../src/governance/governor/IGovernor.sol"; import { ITreasury } from "../src/governance/treasury/ITreasury.sol"; import { MerkleReserveMinter } from "../src/minters/MerkleReserveMinter.sol"; contract SetupDaoScript is Script { + /* using Strings for uint256; string configFile; @@ -102,4 +103,5 @@ contract SetupDaoScript is Script { vm.stopBroadcast(); } + */ } diff --git a/script/DeployTokenUpgrade.s.sol b/script/DeployTokenUpgrade.s.sol deleted file mode 100644 index 9d00c94..0000000 --- a/script/DeployTokenUpgrade.s.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "forge-std/console2.sol"; -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; - -import { IManager, Manager } from "../src/manager/Manager.sol"; -import { IToken, Token } from "../src/token/default/Token.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 { 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 { - using Strings for uint256; - - string configFile; - - function _getKey(string memory key) internal returns (address result) { - (result) = abi.decode(vm.parseJson(configFile, key), (address)); - } - - function run() public { - uint256 chainID = vm.envUint("CHAIN_ID"); - console.log("CHAIN_ID", chainID); - uint256 key = vm.envUint("PRIVATE_KEY"); - address deployerAddress = vm.addr(key); - - configFile = vm.readFile(string.concat("./addresses/", Strings.toString(chainID), ".json")); - - address auctionImpl = _getKey("Auction"); - address governorImpl = _getKey("Governor"); - address managerProxy = _getKey("Manager"); - address metadataImpl = _getKey("MetadataRenderer"); - address tokenImpl = _getKey("Token"); - address treasuryImpl = _getKey("Treasury"); - - console2.log("~~~~~~~~~~ DEPLOYER ADDRESS ~~~~~~~~~~~"); - console2.logAddress(deployerAddress); - - console2.log("~~~~~~~~~~ METADATA IMPL ~~~~~~~~~~~"); - console2.logAddress(metadataImpl); - - console2.log("~~~~~~~~~~ MANAGER PROXY ~~~~~~~~~~~"); - console2.logAddress(managerProxy); - - console2.log("~~~~~~~~~~ AUCTION IMPL ~~~~~~~~~~~"); - console2.logAddress(auctionImpl); - - console2.log("~~~~~~~~~~ TREASURY IMPL ~~~~~~~~~~~"); - console2.logAddress(treasuryImpl); - - console2.log("~~~~~~~~~~ GOVERNOR IMPL ~~~~~~~~~~~"); - console2.logAddress(governorImpl); - - vm.startBroadcast(deployerAddress); - - // Deploy root manager implementation + proxy - Manager manager = Manager(managerProxy); - - // Deploy token upgrade implementation - address tokenUpgradeImpl = address(new Token(managerProxy)); - - address managerImpl = address(new Manager()); - - console2.log("TU"); - console2.log(tokenUpgradeImpl); - - console2.log("M"); - console2.log(managerImpl); - - // console2.log("OWNER", manager.owner()); - - // manager.upgradeTo(managerImpl); - - vm.stopBroadcast(); - - string memory filePath = string(abi.encodePacked("deploys/", chainID.toString(), ".upgradeToken.txt")); - vm.writeFile(filePath, ""); - vm.writeLine(filePath, string(abi.encodePacked("Token Upgrade implementation: ", addressToString(tokenUpgradeImpl)))); - vm.writeLine(filePath, string(abi.encodePacked("Manager implementation: ", addressToString(managerImpl)))); - } - - function addressToString(address _addr) private pure returns (string memory) { - bytes memory s = new bytes(40); - for (uint256 i = 0; i < 20; i++) { - bytes1 b = bytes1(uint8(uint256(uint160(_addr)) / (2**(8 * (19 - i))))); - bytes1 hi = bytes1(uint8(b) / 16); - bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); - s[2 * i] = char(hi); - s[2 * i + 1] = char(lo); - } - return string(abi.encodePacked("0x", string(s))); - } - - function char(bytes1 b) private pure returns (bytes1 c) { - if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); - else return bytes1(uint8(b) + 0x57); - } -} diff --git a/script/DeployV2Core.s.sol b/script/DeployV2Core.s.sol index ad9d7b8..0bfdd73 100644 --- a/script/DeployV2Core.s.sol +++ b/script/DeployV2Core.s.sol @@ -6,11 +6,12 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IManager, Manager } from "../src/manager/Manager.sol"; import { IToken, Token } from "../src/token/default/Token.sol"; +import { PartialMirrorToken } from "../src/token/partial-mirror/PartialMirrorToken.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 { PropertyMetadata } from "../src/metadata/property/PropertyMetadata.sol"; -import { PropertyMetadataTypesV1 } from "../src/metadata/property/types/PropertyMetadataTypesV1.sol"; +import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol"; +import { MetadataRendererTypesV1 } from "../src/token/metadata/types/MetadataRendererTypesV1.sol"; import { ERC1967Proxy } from "../src/lib/proxy/ERC1967Proxy.sol"; import { ProtocolRewards } from "../src/rewards/ProtocolRewards.sol"; @@ -41,7 +42,7 @@ contract DeployContracts is Script { vm.startBroadcast(deployerAddress); // Deploy root manager implementation + proxy - address managerImpl0 = address(new Manager()); + address managerImpl0 = address(new Manager(address(0), address(0), address(0), address(0), address(0), address(0))); Manager manager = Manager(address(new ERC1967Proxy(managerImpl0, abi.encodeWithSignature("initialize(address)", deployerAddress)))); @@ -50,8 +51,10 @@ contract DeployContracts is Script { // Deploy standard token implementation address tokenImpl = address(new Token(address(manager))); + address mirrorTokenImpl = address(new PartialMirrorToken(address(manager))); + // Deploy metadata renderer implementation - address metadataRendererImpl = address(new PropertyMetadata(address(manager))); + address metadataRendererImpl = address(new MetadataRenderer(address(manager))); // Deploy auction house implementation address auctionImpl = address(new Auction(address(manager), address(rewards), weth)); @@ -62,21 +65,10 @@ contract DeployContracts is Script { // Deploy governor implementation address governorImpl = address(new Governor(address(manager))); - address managerImpl = address(new Manager()); + address managerImpl = address(new Manager(tokenImpl, mirrorTokenImpl, metadataRendererImpl, auctionImpl, treasuryImpl, governorImpl)); manager.upgradeTo(managerImpl); - // Register implementations - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_TOKEN(), tokenImpl); - - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_METADATA(), metadataRendererImpl); - - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_AUCTION(), auctionImpl); - - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_GOVERNOR(), governorImpl); - - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_TREASURY(), treasuryImpl); - vm.stopBroadcast(); string memory filePath = string(abi.encodePacked("deploys/", chainID.toString(), ".version2_core.txt")); diff --git a/script/DeployV2New.s.sol b/script/DeployV2New.s.sol index e9c79f7..b9c8529 100644 --- a/script/DeployV2New.s.sol +++ b/script/DeployV2New.s.sol @@ -9,7 +9,6 @@ import { PartialMirrorToken } from "../src/token/partial-mirror/PartialMirrorTok import { ERC1967Proxy } from "../src/lib/proxy/ERC1967Proxy.sol"; import { ERC721RedeemMinter } from "../src/minters/ERC721RedeemMinter.sol"; import { MerkleReserveMinter } from "../src/minters/MerkleReserveMinter.sol"; -import { MediaMetadata } from "../src/metadata/media/MediaMetadata.sol"; contract DeployContracts is Script { using Strings for uint256; @@ -49,11 +48,6 @@ contract DeployContracts is Script { address merkleMinter = address(new MerkleReserveMinter(manager)); - address mediaMetadata = address(new MediaMetadata(address(manager))); - - // Register implementations - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_TOKEN(), mirrorTokenImpl); - vm.stopBroadcast(); string memory filePath = string(abi.encodePacked("deploys/", chainID.toString(), ".version2_new.txt")); @@ -62,7 +56,6 @@ contract DeployContracts is Script { vm.writeLine(filePath, string(abi.encodePacked("Mirror Token implementation: ", addressToString(mirrorTokenImpl)))); vm.writeLine(filePath, string(abi.encodePacked("ERC721 Redeem Minter: ", addressToString(erc721Minter)))); vm.writeLine(filePath, string(abi.encodePacked("Merkle Reserve Minter: ", addressToString(merkleMinter)))); - vm.writeLine(filePath, string(abi.encodePacked("Media Metadata Renderer: ", addressToString(mediaMetadata)))); console2.log("~~~~~~~~~~ MIRROR TOKEN IMPL ~~~~~~~~~~~"); console2.logAddress(mirrorTokenImpl); @@ -72,9 +65,6 @@ contract DeployContracts is Script { console2.log("~~~~~~~~~~ MERKLE RESERVE MINTER ~~~~~~~~~~~"); console2.logAddress(merkleMinter); - - console2.log("~~~~~~~~~~ MEDIA METADATA RENDERER ~~~~~~~~~~~"); - console2.logAddress(mediaMetadata); } function addressToString(address _addr) private pure returns (string memory) { diff --git a/script/DeployVersion1_1.s.sol b/script/DeployVersion1_1.s.sol deleted file mode 100644 index 7437cd4..0000000 --- a/script/DeployVersion1_1.s.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "forge-std/console2.sol"; -import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; - -import { IManager, Manager } from "../src/manager/Manager.sol"; -import { IToken, Token } from "../src/token/default/Token.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 { PropertyMetadata } from "../src/metadata/property/PropertyMetadata.sol"; -import { PropertyMetadataTypesV1 } from "../src/metadata/property/types/PropertyMetadataTypesV1.sol"; -import { ERC1967Proxy } from "../src/lib/proxy/ERC1967Proxy.sol"; -import { ProtocolRewards } from "../src/rewards/ProtocolRewards.sol"; - -contract DeployVersion1_1 is Script { - using Strings for uint256; - - string configFile; - - function _getKey(string memory key) internal returns (address result) { - (result) = abi.decode(vm.parseJson(configFile, key), (address)); - } - - function run() public { - uint256 chainID = vm.envUint("CHAIN_ID"); - console.log("CHAIN_ID", chainID); - uint256 key = vm.envUint("PRIVATE_KEY"); - address deployerAddress = vm.addr(key); - - configFile = vm.readFile(string.concat("./addresses/", Strings.toString(chainID), ".json")); - - address managerProxy = _getKey("Manager"); - address weth = _getKey("WETH"); - address builderRewardsRecipent = _getKey("BuilderDAO"); - - console2.log("~~~~~~~~~~ DEPLOYER ADDRESS ~~~~~~~~~~~"); - console2.logAddress(deployerAddress); - - console2.log("~~~~~~~~~~ MANAGER PROXY ~~~~~~~~~~~"); - console2.logAddress(managerProxy); - - console2.log("~~~~~~~~~~ WETH ADDRESS ~~~~~~~~~~~"); - console2.logAddress(weth); - - vm.startBroadcast(deployerAddress); - - // Get root manager implementation + proxy - Manager manager = Manager(managerProxy); - - ProtocolRewards rewards = new ProtocolRewards(address(manager), builderRewardsRecipent); - - // Deploy auction upgrade implementation - address auctionUpgradeImpl = address(new Auction(managerProxy, address(rewards), weth)); - // Deploy governor upgrade implementation - address governorUpgradeImpl = address(new Governor(managerProxy)); - // Deploy treasury upgrade implementation - address treasuryUpgradeImpl = address(new Treasury(managerProxy)); - // Deploy token upgrade implementation - address tokenUpgradeImpl = address(new Token(managerProxy)); - // Deploy metadata upgrade implementation - address metadataUpgradeImpl = address(new PropertyMetadata(managerProxy)); - - address managerImpl = address(new Manager()); - - vm.stopBroadcast(); - - string memory filePath = string(abi.encodePacked("deploys/", chainID.toString(), ".version1_1.txt")); - vm.writeFile(filePath, ""); - vm.writeLine(filePath, string(abi.encodePacked("Auction Upgrade implementation: ", addressToString(auctionUpgradeImpl)))); - vm.writeLine(filePath, string(abi.encodePacked("Governor Upgrade implementation: ", addressToString(governorUpgradeImpl)))); - vm.writeLine(filePath, string(abi.encodePacked("Treasury Upgrade implementation: ", addressToString(treasuryUpgradeImpl)))); - vm.writeLine(filePath, string(abi.encodePacked("Token Upgrade implementation: ", addressToString(tokenUpgradeImpl)))); - vm.writeLine(filePath, string(abi.encodePacked("Metadata Upgrade implementation: ", addressToString(metadataUpgradeImpl)))); - vm.writeLine(filePath, string(abi.encodePacked("Manager implementation: ", addressToString(managerImpl)))); - } - - function addressToString(address _addr) private pure returns (string memory) { - bytes memory s = new bytes(40); - for (uint256 i = 0; i < 20; i++) { - bytes1 b = bytes1(uint8(uint256(uint160(_addr)) / (2**(8 * (19 - i))))); - bytes1 hi = bytes1(uint8(b) / 16); - bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); - s[2 * i] = char(hi); - s[2 * i + 1] = char(lo); - } - return string(abi.encodePacked("0x", string(s))); - } - - function char(bytes1 b) private pure returns (bytes1 c) { - if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); - else return bytes1(uint8(b) + 0x57); - } -} diff --git a/script/GetInterfaceIds.s.sol b/script/GetInterfaceIds.s.sol index 0b94081..84bcac5 100644 --- a/script/GetInterfaceIds.s.sol +++ b/script/GetInterfaceIds.s.sol @@ -4,14 +4,14 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; import "forge-std/console2.sol"; -import { IBaseMetadata } from "../src/metadata/interfaces/IBaseMetadata.sol"; -import { IPropertyMetadata } from "../src/metadata/property/interfaces/IPropertyMetadata.sol"; +import { IBaseMetadata } from "../src/token/metadata/interfaces/IBaseMetadata.sol"; +import { IPropertyIPFSMetadataRenderer } from "../src/token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol"; import { IMirrorToken } from "../src/token/interfaces/IMirrorToken.sol"; contract GetInterfaceIds is Script { function run() public view { console2.logBytes4(type(IBaseMetadata).interfaceId); - console2.logBytes4(type(IPropertyMetadata).interfaceId); + console2.logBytes4(type(IPropertyIPFSMetadataRenderer).interfaceId); console2.logBytes4(type(IMirrorToken).interfaceId); } } diff --git a/script/MockTreasury.sol b/script/MockTreasury.sol deleted file mode 100644 index b16998f..0000000 --- a/script/MockTreasury.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -// contract MockTreasury { -// address owner; - -// constructor(address _owner) { -// owner = _owner; -// } - -// function initialize(address govenor, uint256 timelockDelay) external { -// // do nothing -// } - -// function execute(address target, bytes calldata data) public { -// // require(msg.sender == owner, "only owner"); -// target.call(data); -// } -// } diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index ffcbdab..c43e3c7 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -8,11 +8,11 @@ import { Pausable } from "../lib/utils/Pausable.sol"; import { SafeCast } from "../lib/utils/SafeCast.sol"; import { AuctionStorageV1 } from "./storage/AuctionStorageV1.sol"; -import { IBaseToken } from "../token/interfaces/IBaseToken.sol"; import { AuctionStorageV2 } from "./storage/AuctionStorageV2.sol"; import { IManager } from "../manager/IManager.sol"; import { IAuction } from "./IAuction.sol"; import { IWETH } from "../lib/interfaces/IWETH.sol"; +import { Token } from "../token/default/Token.sol"; import { IProtocolRewards } from "../rewards/interfaces/IProtocolRewards.sol"; import { VersionedContract } from "../VersionedContract.sol"; @@ -64,16 +64,22 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, /// INITIALIZER /// /// /// - /// @notice Initializes a DAO's auction contract + /// @notice Initializes a DAO's auction house /// @param _token The ERC-721 token address - /// @param _initialOwner The account responsible for starting the first auction + /// @param _founder The founder responsible for starting the first auction /// @param _treasury The treasury address where ETH will be sent - /// @param _data The encoded auction settings + /// @param _duration The duration of each auction + /// @param _reservePrice The reserve price of each auction + /// @param _founderRewardRecipent The address to recieve founders rewards + /// @param _founderRewardBPS The percent of rewards a founder receives in BPS for each auction function initialize( address _token, - address _initialOwner, + address _founder, address _treasury, - bytes calldata _data + uint256 _duration, + uint256 _reservePrice, + address _founderRewardRecipent, + uint256 _founderRewardBPS ) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); @@ -82,26 +88,24 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, __ReentrancyGuard_init(); // Grant initial ownership to a founder - __Ownable_init(_initialOwner); + __Ownable_init(_founder); // Pause the contract until the first auction __Pausable_init(true); // Store DAO's ERC-721 token - token = IBaseToken(_token); - - AuctionParams memory params = abi.decode(_data, (AuctionParams)); + token = Token(_token); // Store the auction house settings - settings.duration = SafeCast.toUint40(params.duration); - settings.reservePrice = params.reservePrice; + settings.duration = SafeCast.toUint40(_duration); + settings.reservePrice = _reservePrice; settings.treasury = _treasury; settings.timeBuffer = INITIAL_TIME_BUFFER; settings.minBidIncrement = INITIAL_MIN_BID_INCREMENT_PERCENT; // Store the founder rewards settings - founderRewardRecipent = params.founderRewardRecipent; - founderRewardBPS = params.founderRewardBPS; + founderRewardRecipent = _founderRewardRecipent; + founderRewardBPS = _founderRewardBPS; } /// /// diff --git a/src/auction/IAuction.sol b/src/auction/IAuction.sol index a8f270e..419bfb7 100644 --- a/src/auction/IAuction.sol +++ b/src/auction/IAuction.sol @@ -97,36 +97,24 @@ interface IAuction is IUUPS, IOwnable, IPausable { /// @dev Thrown if the auction creation failed error AUCTION_CREATE_FAILED_TO_LAUNCH(); - /// /// - /// STRUCTS /// - /// /// - - /// @notice The auction initilization parameters - struct AuctionParams { - /// @notice The duration of each auction - uint256 duration; - /// @notice The reserve price of each auction - uint256 reservePrice; - /// @notice The address to recieve founders rewards - address founderRewardRecipent; - /// @notice The percent of rewards a founder receives in BPS for each auction - uint256 founderRewardBPS; - } - /// /// /// FUNCTIONS /// /// /// /// @notice Initializes a DAO's auction house /// @param token The ERC-721 token address - /// @param initialOwner The account responsible for starting the first auction + /// @param founder The founder responsible for starting the first auction /// @param treasury The treasury address where ETH will be sent - /// @param data The encoded auction initialization data + /// @param duration The duration of each auction + /// @param reservePrice The reserve price of each auction function initialize( address token, - address initialOwner, + address founder, address treasury, - bytes calldata data + uint256 duration, + uint256 reservePrice, + address founderRewardRecipent, + uint256 founderRewardBPS ) external; /// @notice Creates a bid for the current token diff --git a/src/auction/storage/AuctionStorageV1.sol b/src/auction/storage/AuctionStorageV1.sol index 046a07d..6897f24 100644 --- a/src/auction/storage/AuctionStorageV1.sol +++ b/src/auction/storage/AuctionStorageV1.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { IBaseToken } from "../../token/interfaces/IBaseToken.sol"; +import { Token } from "../../token/default/Token.sol"; import { AuctionTypesV1 } from "../types/AuctionTypesV1.sol"; /// @title AuctionStorageV1 @@ -12,7 +12,7 @@ contract AuctionStorageV1 is AuctionTypesV1 { Settings internal settings; /// @notice The ERC-721 token - IBaseToken public token; + Token public token; /// @notice The state of the current auction Auction public auction; diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 8514cda..eef9c36 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -5,10 +5,9 @@ import { UUPS } from "../../lib/proxy/UUPS.sol"; import { Ownable } from "../../lib/utils/Ownable.sol"; import { EIP712 } from "../../lib/utils/EIP712.sol"; import { SafeCast } from "../../lib/utils/SafeCast.sol"; -import { ERC721 } from "../../lib/token/ERC721.sol"; import { GovernorStorageV1 } from "./storage/GovernorStorageV1.sol"; -import { IBaseToken } from "../../token/interfaces/IBaseToken.sol"; +import { Token } from "../../token/default/Token.sol"; import { Treasury } from "../treasury/Treasury.sol"; import { IManager } from "../../manager/IManager.sol"; import { IGovernor } from "./IGovernor.sol"; @@ -76,11 +75,19 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos /// @notice Initializes a DAO's governor /// @param _treasury The DAO's treasury address /// @param _token The DAO's governance token address - /// @param _data The encoded governor parameters + /// @param _vetoer The address eligible to veto proposals + /// @param _votingDelay The voting delay + /// @param _votingPeriod The voting period + /// @param _proposalThresholdBps The proposal threshold basis points + /// @param _quorumThresholdBps The quorum threshold basis points function initialize( address _treasury, address _token, - bytes calldata _data + address _vetoer, + uint256 _votingDelay, + uint256 _votingPeriod, + uint256 _proposalThresholdBps, + uint256 _quorumThresholdBps ) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); @@ -89,38 +96,27 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos if (_treasury == address(0)) revert ADDRESS_ZERO(); if (_token == address(0)) revert ADDRESS_ZERO(); - GovParams memory params = abi.decode(_data, (GovParams)); - // If a vetoer is specified, store its address - if (params.vetoer != address(0)) settings.vetoer = params.vetoer; + if (_vetoer != address(0)) settings.vetoer = _vetoer; // Ensure the specified governance settings are valid - if (params.proposalThresholdBps < MIN_PROPOSAL_THRESHOLD_BPS || params.proposalThresholdBps > MAX_PROPOSAL_THRESHOLD_BPS) { - revert INVALID_PROPOSAL_THRESHOLD_BPS(); - } - if (params.quorumThresholdBps < MIN_QUORUM_THRESHOLD_BPS || params.quorumThresholdBps > MAX_QUORUM_THRESHOLD_BPS) { - revert INVALID_QUORUM_THRESHOLD_BPS(); - } - if (params.proposalThresholdBps >= params.quorumThresholdBps) { + if (_proposalThresholdBps < MIN_PROPOSAL_THRESHOLD_BPS || _proposalThresholdBps > MAX_PROPOSAL_THRESHOLD_BPS) revert INVALID_PROPOSAL_THRESHOLD_BPS(); - } - if (params.votingDelay < MIN_VOTING_DELAY || params.votingDelay > MAX_VOTING_DELAY) { - revert INVALID_VOTING_DELAY(); - } - if (params.votingPeriod < MIN_VOTING_PERIOD || params.votingPeriod > MAX_VOTING_PERIOD) { - revert INVALID_VOTING_PERIOD(); - } + if (_quorumThresholdBps < MIN_QUORUM_THRESHOLD_BPS || _quorumThresholdBps > MAX_QUORUM_THRESHOLD_BPS) revert INVALID_QUORUM_THRESHOLD_BPS(); + if (_proposalThresholdBps >= _quorumThresholdBps) revert INVALID_PROPOSAL_THRESHOLD_BPS(); + if (_votingDelay < MIN_VOTING_DELAY || _votingDelay > MAX_VOTING_DELAY) revert INVALID_VOTING_DELAY(); + if (_votingPeriod < MIN_VOTING_PERIOD || _votingPeriod > MAX_VOTING_PERIOD) revert INVALID_VOTING_PERIOD(); // Store the governor settings settings.treasury = Treasury(payable(_treasury)); - settings.token = IBaseToken(_token); - settings.votingDelay = SafeCast.toUint48(params.votingDelay); - settings.votingPeriod = SafeCast.toUint48(params.votingPeriod); - settings.proposalThresholdBps = SafeCast.toUint16(params.proposalThresholdBps); - settings.quorumThresholdBps = SafeCast.toUint16(params.quorumThresholdBps); + settings.token = Token(_token); + settings.votingDelay = SafeCast.toUint48(_votingDelay); + settings.votingPeriod = SafeCast.toUint48(_votingPeriod); + settings.proposalThresholdBps = SafeCast.toUint16(_proposalThresholdBps); + settings.quorumThresholdBps = SafeCast.toUint16(_quorumThresholdBps); // Initialize EIP-712 support - __EIP712_init(string.concat(ERC721(_token).symbol(), " GOV"), "1"); + __EIP712_init(string.concat(settings.token.symbol(), " GOV"), "1"); // Grant ownership to the treasury __Ownable_init(_treasury); diff --git a/src/governance/governor/IGovernor.sol b/src/governance/governor/IGovernor.sol index e2b2253..1e8ff06 100644 --- a/src/governance/governor/IGovernor.sol +++ b/src/governance/governor/IGovernor.sol @@ -113,24 +113,6 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { /// @dev Reverts if the caller was not the contract manager error ONLY_MANAGER(); - /// /// - /// STRUCTS /// - /// /// - - /// @notice The governance initilization parameters - struct GovParams { - /// @notice votingDelay The time delay to vote on a created proposal - uint256 votingDelay; - /// @notice votingPeriod The time period to vote on a proposal - uint256 votingPeriod; - /// @notice proposalThresholdBps The basis points of the token supply required to create a proposal - uint256 proposalThresholdBps; - /// @notice quorumThresholdBps The basis points of the token supply required to reach quorum - uint256 quorumThresholdBps; - /// @notice vetoer The address authorized to veto proposals (address(0) if none desired) - address vetoer; - } - /// /// /// FUNCTIONS /// /// /// @@ -138,11 +120,19 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { /// @notice Initializes a DAO's governor /// @param treasury The DAO's treasury address /// @param token The DAO's governance token address - /// @param data The encoded governance parameters + /// @param vetoer The address eligible to veto proposals + /// @param votingDelay The voting delay + /// @param votingPeriod The voting period + /// @param proposalThresholdBps The proposal threshold basis points + /// @param quorumThresholdBps The quorum threshold basis points function initialize( address treasury, address token, - bytes calldata data + address vetoer, + uint256 votingDelay, + uint256 votingPeriod, + uint256 proposalThresholdBps, + uint256 quorumThresholdBps ) external; /// @notice Creates a proposal diff --git a/src/governance/governor/types/GovernorTypesV1.sol b/src/governance/governor/types/GovernorTypesV1.sol index 1224ec3..576d852 100644 --- a/src/governance/governor/types/GovernorTypesV1.sol +++ b/src/governance/governor/types/GovernorTypesV1.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { IBaseToken } from "../../../token/interfaces/IBaseToken.sol"; +import { Token } from "../../../token/default/Token.sol"; import { Treasury } from "../../treasury/Treasury.sol"; /// @title GovernorTypesV1 @@ -17,7 +17,7 @@ interface GovernorTypesV1 { /// @param votingPeriod The time period to vote on a proposal /// @param vetoer The address with the ability to veto proposals struct Settings { - IBaseToken token; + Token token; uint16 proposalThresholdBps; uint16 quorumThresholdBps; Treasury treasury; diff --git a/src/governance/treasury/ITreasury.sol b/src/governance/treasury/ITreasury.sol index e1a4f74..84e84a9 100644 --- a/src/governance/treasury/ITreasury.sol +++ b/src/governance/treasury/ITreasury.sol @@ -54,24 +54,14 @@ interface ITreasury is IUUPS, IOwnable { /// @dev Reverts if the caller was not the contract manager error ONLY_MANAGER(); - /// /// - /// STRUCTS /// - /// /// - - /// @notice The trasury initilization parameters - struct TreasuryParams { - /// @notice timelockDelay The time delay to execute a queued transaction - uint256 timelockDelay; - } - /// /// /// FUNCTIONS /// /// /// /// @notice Initializes a DAO's treasury /// @param governor The governor address - /// @param data The encoded treasury initialization data - function initialize(address governor, bytes calldata data) external; + /// @param timelockDelay The time delay to execute a queued transaction + function initialize(address governor, uint256 timelockDelay) external; /// @notice The timestamp that a proposal is valid to execute /// @param proposalId The proposal id diff --git a/src/governance/treasury/Treasury.sol b/src/governance/treasury/Treasury.sol index c8b02ba..04b5474 100644 --- a/src/governance/treasury/Treasury.sol +++ b/src/governance/treasury/Treasury.sol @@ -49,8 +49,8 @@ contract Treasury is ITreasury, VersionedContract, UUPS, Ownable, ProposalHasher /// @notice Initializes an instance of a DAO's treasury /// @param _governor The DAO's governor address - /// @param _data The encoded treasury parameters - function initialize(address _governor, bytes calldata _data) external initializer { + /// @param _delay The time delay to execute a queued transaction + function initialize(address _governor, uint256 _delay) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); @@ -60,15 +60,13 @@ contract Treasury is ITreasury, VersionedContract, UUPS, Ownable, ProposalHasher // Grant ownership to the governor __Ownable_init(_governor); - TreasuryParams memory params = abi.decode(_data, (TreasuryParams)); - // Store the time delay - settings.delay = SafeCast.toUint128(params.timelockDelay); + settings.delay = SafeCast.toUint128(_delay); // Set the default grace period settings.gracePeriod = INITIAL_GRACE_PERIOD; - emit DelayUpdated(0, params.timelockDelay); + emit DelayUpdated(0, _delay); } /// /// diff --git a/src/manager/IManager.sol b/src/manager/IManager.sol index 4233d57..d534795 100644 --- a/src/manager/IManager.sol +++ b/src/manager/IManager.sol @@ -20,16 +20,6 @@ interface IManager is IUUPS, IOwnable { /// @param governor The governor address event DAODeployed(address token, address metadata, address auction, address treasury, address governor); - /// @notice Emitted when an implementation is registered by the Builder DAO - /// @param implType The type of implementation - /// @param implAddress The implementation address - event ImplementationRegistered(uint8 implType, address implAddress); - - /// @notice Emitted when an implementation is unregistered by the Builder DAO - /// @param implType The type of implementation - /// @param implAddress The implementation address - event ImplementationRemoved(uint8 implType, address implAddress); - /// @notice Emitted when an upgrade is registered by the Builder DAO /// @param baseImpl The base implementation address /// @param upgradeImpl The upgrade implementation address @@ -88,11 +78,51 @@ interface IManager is IUUPS, IOwnable { } /// @notice The ERC-721 token parameters - /// @param impl The address of the implementation - /// @param data The encoded implementation parameters - struct ImplementationParams { - address impl; - bytes data; + /// @param initStrings The encoded token name, symbol, collection description, collection image uri, renderer base uri + /// @param reservedUntilTokenId The tokenId that a DAO's auctions will start at + struct TokenParams { + bytes initStrings; + uint256 reservedUntilTokenId; + } + + /// @notice The ERC-721 token parameters + /// @param initStrings The encoded token name, symbol, collection description, collection image uri, renderer base uri + /// @param reservedUntilTokenId The tokenId that a DAO's auctions will start at + /// @param tokenToMirror The token contract to be mirrored + struct MirrorTokenParams { + bytes initStrings; + uint256 reservedUntilTokenId; + address tokenToMirror; + } + + /// @notice The auction parameters + /// @param reservePrice The reserve price of each auction + /// @param duration The duration of each auction + struct AuctionParams { + /// @notice The duration of each auction + uint256 duration; + /// @notice The reserve price of each auction + uint256 reservePrice; + /// @notice The address to recieve founders rewards + address founderRewardRecipent; + /// @notice The percent of rewards a founder receives in BPS for each auction + uint256 founderRewardBPS; + } + + /// @notice The governance parameters + /// @param timelockDelay The time delay to execute a queued transaction + /// @param votingDelay The time delay to vote on a created proposal + /// @param votingPeriod The time period to vote on a proposal + /// @param proposalThresholdBps The basis points of the token supply required to create a proposal + /// @param quorumThresholdBps The basis points of the token supply required to reach quorum + /// @param vetoer The address authorized to veto proposals (address(0) if none desired) + struct GovParams { + uint256 timelockDelay; + uint256 votingDelay; + uint256 votingPeriod; + uint256 proposalThresholdBps; + uint256 quorumThresholdBps; + address vetoer; } /// /// @@ -101,12 +131,14 @@ interface IManager is IUUPS, IOwnable { /// @notice Deploys a DAO with custom token, auction, and governance settings /// @param founderParams The DAO founder(s) - /// @param implAddresses The implementation addresses - /// @param implData The encoded list of implementation data + /// @param tokenParams The ERC-721 token settings + /// @param auctionParams The auction settings + /// @param govParams The governance settings function deploy( FounderParams[] calldata founderParams, - address[] calldata implAddresses, - bytes[] calldata implData + TokenParams calldata tokenParams, + AuctionParams calldata auctionParams, + GovParams calldata govParams ) external returns ( diff --git a/src/manager/Manager.sol b/src/manager/Manager.sol index 9c8b2ee..bf56247 100644 --- a/src/manager/Manager.sol +++ b/src/manager/Manager.sol @@ -6,10 +6,10 @@ import { Ownable } from "../lib/utils/Ownable.sol"; import { ERC1967Proxy } from "../lib/proxy/ERC1967Proxy.sol"; import { ManagerStorageV1 } from "./storage/ManagerStorageV1.sol"; -import { ManagerStorageV2 } from "./storage/ManagerStorageV2.sol"; import { IManager } from "./IManager.sol"; -import { IBaseToken } from "../token/interfaces/IBaseToken.sol"; -import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol"; +import { IToken } from "../token/default/IToken.sol"; +import { IPartialMirrorToken } from "../token/partial-mirror/IPartialMirrorToken.sol"; +import { IBaseMetadata } from "../token/metadata/interfaces/IBaseMetadata.sol"; import { IAuction } from "../auction/IAuction.sol"; import { ITreasury } from "../governance/treasury/ITreasury.sol"; import { IGovernor } from "../governance/governor/IGovernor.sol"; @@ -24,21 +24,48 @@ import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC16 /// @author Neokry & Rohan Kulkarni /// @custom:repo github.com/ourzora/nouns-protocol /// @notice The DAO deployer and upgrade manager -contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1, ManagerStorageV2 { +contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 { /// /// - /// CONSTANTS /// + /// IMMUTABLES /// /// /// - /// @notice The count of implementation types - uint8 public constant IMPLEMENTATION_TYPE_COUNT = 5; + /// @notice The token implementation address + address public immutable tokenImpl; - // Public constants for implementation types. - // Allows for more clarity when adding new types compared to a enum. - uint8 public constant IMPLEMENTATION_TYPE_TOKEN = 0; - uint8 public constant IMPLEMENTATION_TYPE_METADATA = 1; - uint8 public constant IMPLEMENTATION_TYPE_AUCTION = 2; - uint8 public constant IMPLEMENTATION_TYPE_TREASURY = 3; - uint8 public constant IMPLEMENTATION_TYPE_GOVERNOR = 4; + /// @notice The mirror implementation address + address public immutable mirrorTokenImpl; + + /// @notice The metadata renderer implementation address + address public immutable metadataImpl; + + /// @notice The auction house implementation address + address public immutable auctionImpl; + + /// @notice The treasury implementation address + address public immutable treasuryImpl; + + /// @notice The governor implementation address + address public immutable governorImpl; + + /// /// + /// CONSTRUCTOR /// + /// /// + + constructor( + address _tokenImpl, + address _mirrorTokenImpl, + address _metadataImpl, + address _auctionImpl, + address _treasuryImpl, + address _governorImpl + ) payable initializer { + tokenImpl = _tokenImpl; + mirrorTokenImpl = _mirrorTokenImpl; + metadataImpl = _metadataImpl; + auctionImpl = _auctionImpl; + treasuryImpl = _treasuryImpl; + governorImpl = _governorImpl; + } /// /// /// INITIALIZER /// @@ -60,12 +87,14 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 /// @notice Deploys a DAO with custom token, auction, and governance settings /// @param _founderParams The DAO founders - /// @param _implAddresses The implementation addresses - /// @param _implData The encoded list of implementation data + /// @param _tokenParams The ERC-721 token settings + /// @param _auctionParams The auction settings + /// @param _govParams The governance settings function deploy( FounderParams[] calldata _founderParams, - address[] calldata _implAddresses, - bytes[] calldata _implData + TokenParams calldata _tokenParams, + AuctionParams calldata _auctionParams, + GovParams calldata _govParams ) external returns ( @@ -83,45 +112,128 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 // Ensure at least one founder is provided if ((founder = _founderParams[0].wallet) == address(0)) revert FOUNDER_REQUIRED(); - uint256 implAddressesLength = _implAddresses.length; + { + // Deploy the DAO's ERC-721 governance token + token = address(new ERC1967Proxy(tokenImpl, "")); - // Ensure implementation parameters are correct length - if (implAddressesLength != IMPLEMENTATION_TYPE_COUNT || _implData.length != IMPLEMENTATION_TYPE_COUNT) revert INVALID_IMPLEMENTATION_PARAMS(); + // Use the token address to precompute the DAO's remaining addresses + bytes32 salt = bytes32(uint256(uint160(token)) << 96); - // 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(); - } + // Deploy the remaining DAO contracts + metadata = address(new ERC1967Proxy{ salt: salt }(metadataImpl, "")); + auction = address(new ERC1967Proxy{ salt: salt }(auctionImpl, "")); + treasury = address(new ERC1967Proxy{ salt: salt }(treasuryImpl, "")); + governor = address(new ERC1967Proxy{ salt: salt }(governorImpl, "")); } - // Deploy the DAO's ERC-721 governance token - token = address(new ERC1967Proxy(_implAddresses[IMPLEMENTATION_TYPE_TOKEN], "")); + daoAddressesByToken[token] = DAOAddresses({ metadata: metadata, auction: auction, treasury: treasury, governor: governor }); - // Use the token address to precompute the DAO's remaining addresses - bytes32 salt = bytes32(uint256(uint160(token)) << 96); + // Initialize each instance with the provided settings + IToken(token).initialize({ + founders: _founderParams, + initStrings: _tokenParams.initStrings, + reservedUntilTokenId: _tokenParams.reservedUntilTokenId, + metadataRenderer: metadata, + auction: auction, + initialOwner: founder + }); + IBaseMetadata(metadata).initialize({ initStrings: _tokenParams.initStrings, token: token }); + IAuction(auction).initialize({ + token: token, + founder: founder, + treasury: treasury, + duration: _auctionParams.duration, + reservePrice: _auctionParams.reservePrice, + founderRewardRecipent: _auctionParams.founderRewardRecipent, + founderRewardBPS: _auctionParams.founderRewardBPS + }); + ITreasury(treasury).initialize({ governor: governor, timelockDelay: _govParams.timelockDelay }); + IGovernor(governor).initialize({ + treasury: treasury, + token: token, + vetoer: _govParams.vetoer, + votingDelay: _govParams.votingDelay, + votingPeriod: _govParams.votingPeriod, + proposalThresholdBps: _govParams.proposalThresholdBps, + quorumThresholdBps: _govParams.quorumThresholdBps + }); - // Deploy the remaining DAO contracts - metadata = address(new ERC1967Proxy{ salt: salt }(_implAddresses[IMPLEMENTATION_TYPE_METADATA], "")); - auction = address(new ERC1967Proxy{ salt: salt }(_implAddresses[IMPLEMENTATION_TYPE_AUCTION], "")); - treasury = address(new ERC1967Proxy{ salt: salt }(_implAddresses[IMPLEMENTATION_TYPE_TREASURY], "")); - governor = address(new ERC1967Proxy{ salt: salt }(_implAddresses[IMPLEMENTATION_TYPE_GOVERNOR], "")); + emit DAODeployed({ token: token, metadata: metadata, auction: auction, treasury: treasury, governor: governor }); + } + + /// @notice Deploys a DAO with partial mirror token functionality + /// @param _founderParams The DAO founders + /// @param _mirrorTokenParams The partial mirror token settings + /// @param _auctionParams The auction settings + /// @param _govParams The governance settings + function deployWithMirror( + FounderParams[] calldata _founderParams, + MirrorTokenParams calldata _mirrorTokenParams, + AuctionParams calldata _auctionParams, + GovParams calldata _govParams + ) + external + returns ( + address token, + address metadata, + address auction, + address treasury, + address governor + ) + { + // Used to store the address of the first (or only) founder + // This founder is responsible for adding token artwork and launching the first auction -- they're also free to transfer this responsiblity + address founder; + + // Ensure at least one founder is provided + if ((founder = _founderParams[0].wallet) == address(0)) revert FOUNDER_REQUIRED(); + + { + // Deploy the DAO's ERC-721 governance token + token = address(new ERC1967Proxy(mirrorTokenImpl, "")); + + // Use the token address to precompute the DAO's remaining addresses + bytes32 salt = bytes32(uint256(uint160(token)) << 96); + + // Deploy the remaining DAO contracts + metadata = address(new ERC1967Proxy{ salt: salt }(metadataImpl, "")); + auction = address(new ERC1967Proxy{ salt: salt }(auctionImpl, "")); + treasury = address(new ERC1967Proxy{ salt: salt }(treasuryImpl, "")); + governor = address(new ERC1967Proxy{ salt: salt }(governorImpl, "")); + } daoAddressesByToken[token] = DAOAddresses({ metadata: metadata, auction: auction, treasury: treasury, governor: governor }); // Initialize each instance with the provided settings - IBaseToken(token).initialize({ + IPartialMirrorToken(token).initialize({ founders: _founderParams, + initStrings: _mirrorTokenParams.initStrings, + reservedUntilTokenId: _mirrorTokenParams.reservedUntilTokenId, + tokenToMirror: _mirrorTokenParams.tokenToMirror, metadataRenderer: metadata, auction: auction, - initialOwner: founder, - data: _implData[IMPLEMENTATION_TYPE_TOKEN] + initialOwner: founder + }); + IBaseMetadata(metadata).initialize({ initStrings: _mirrorTokenParams.initStrings, token: token }); + IAuction(auction).initialize({ + token: token, + founder: founder, + treasury: treasury, + duration: _auctionParams.duration, + reservePrice: _auctionParams.reservePrice, + founderRewardRecipent: _auctionParams.founderRewardRecipent, + founderRewardBPS: _auctionParams.founderRewardBPS + }); + ITreasury(treasury).initialize({ governor: governor, timelockDelay: _govParams.timelockDelay }); + IGovernor(governor).initialize({ + treasury: treasury, + token: token, + vetoer: _govParams.vetoer, + votingDelay: _govParams.votingDelay, + votingPeriod: _govParams.votingPeriod, + proposalThresholdBps: _govParams.proposalThresholdBps, + quorumThresholdBps: _govParams.quorumThresholdBps }); - IBaseMetadata(metadata).initialize({ token: token, data: _implData[IMPLEMENTATION_TYPE_METADATA] }); - IAuction(auction).initialize({ token: token, initialOwner: founder, treasury: treasury, data: _implData[IMPLEMENTATION_TYPE_AUCTION] }); - ITreasury(treasury).initialize({ governor: governor, data: _implData[IMPLEMENTATION_TYPE_TREASURY] }); - IGovernor(governor).initialize({ treasury: treasury, token: token, data: _implData[IMPLEMENTATION_TYPE_GOVERNOR] }); emit DAODeployed({ token: token, metadata: metadata, auction: auction, treasury: treasury, governor: governor }); } @@ -145,7 +257,7 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 IBaseMetadata(metadata).initialize(_setupRenderer, _token); } - IBaseToken(_token).setMetadataRenderer(IBaseMetadata(metadata)); + IToken(_token).setMetadataRenderer(IBaseMetadata(metadata)); emit MetadataRendererUpdated({ sender: msg.sender, renderer: metadata }); } @@ -178,37 +290,6 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 governor = addresses.governor; } - /// /// - /// DAO Implementations /// - /// /// - - /// @notice If an implementation is registered by the Builder DAO as an option for deployment - /// @param _implType The implementation type - /// @param _implAddress The implementation address - function isRegisteredImplementation(uint8 _implType, address _implAddress) external view returns (bool) { - return isImplementation[_implType][_implAddress]; - } - - /// @notice Called by the Builder DAO to offer implementation choices when creating DAOs - /// @param _implType The implementation type - /// @param _implAddress The implementation address - function registerImplementation(uint8 _implType, address _implAddress) external onlyOwner { - if (_isInvalidImplementationType(_implType)) revert INVALID_IMPLEMENTATION_TYPE(); - isImplementation[_implType][_implAddress] = true; - - emit ImplementationRegistered(_implType, _implAddress); - } - - /// @notice Called by the Builder DAO to remove an implementation option - /// @param _implType The implementation type - /// @param _implAddress The implementation address - function removeImplementation(uint8 _implType, address _implAddress) external onlyOwner { - if (_isInvalidImplementationType(_implType)) revert INVALID_IMPLEMENTATION_TYPE(); - delete isImplementation[_implType][_implAddress]; - - emit ImplementationRemoved(_implType, _implAddress); - } - /// /// /// DAO UPGRADES /// /// /// @@ -238,12 +319,6 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 emit UpgradeRemoved(_baseImpl, _upgradeImpl); } - /// @notice Check if an implementation type is invalid - /// @param _implType The implementation type to check - function _isInvalidImplementationType(uint8 _implType) internal pure returns (bool) { - return _implType > IMPLEMENTATION_TYPE_COUNT; - } - /// @notice Safely get the contract version of a target contract. /// @param target The ERC-721 token address /// @dev Assume `target` is a contract diff --git a/src/manager/storage/ManagerStorageV2.sol b/src/manager/storage/ManagerStorageV2.sol deleted file mode 100644 index 7855464..0000000 --- a/src/manager/storage/ManagerStorageV2.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -/// @notice Manager Storage V2 -/// @author Neokry -/// @notice The Manager storage contract -contract ManagerStorageV2 { - /// @notice Determine if a contract is a registered implementation - /// @dev Implementation type => Implementation address => Registered - mapping(uint8 => mapping(address => bool)) internal isImplementation; -} diff --git a/src/metadata/interfaces/IBaseMetadata.sol b/src/metadata/interfaces/IBaseMetadata.sol deleted file mode 100644 index e446e0b..0000000 --- a/src/metadata/interfaces/IBaseMetadata.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -import { IUUPS } from "../../lib/interfaces/IUUPS.sol"; - -/// @title IBaseMetadata -/// @author Rohan Kulkarni -/// @notice The external Base Metadata errors and functions -interface IBaseMetadata is IUUPS { - /// /// - /// ERRORS /// - /// /// - - /// @dev Reverts if the caller was not the contract manager - error ONLY_MANAGER(); - - /// /// - /// EVENTS /// - /// /// - - /// @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); - - /// /// - /// FUNCTIONS /// - /// /// - - /// @notice Initializes a DAO's token metadata renderer - /// @param data The encoded token and metadata initialization strings - /// @param token The associated ERC-721 token address - function initialize(bytes calldata data, address token) external; - - /// @notice Generates attributes for a token upon mint - /// @param tokenId The ERC-721 token id - function onMinted(uint256 tokenId) external returns (bool); - - /// @notice The token URI - /// @param tokenId The ERC-721 token id - function tokenURI(uint256 tokenId) external view returns (string memory); - - /// @notice The token data - /// @param tokenId The ERC-721 token id - function tokenData(uint256 tokenId) - external - view - returns ( - string memory name, - string memory imageURI, - string memory contentURI - ); - - /// @notice The contract URI - function contractURI() external view returns (string memory); - - /// @notice The associated ERC-721 token - function token() external view returns (address); - - /// @notice Get metadata owner address - function owner() external view returns (address); - - /// @notice The contract image - function contractImage() external view returns (string memory); - - /// @notice The collection description - function description() external view returns (string memory); - - /// @notice The collection uri - function projectURI() 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/MediaMetadata.sol b/src/metadata/media/MediaMetadata.sol deleted file mode 100644 index 89680d2..0000000 --- a/src/metadata/media/MediaMetadata.sol +++ /dev/null @@ -1,281 +0,0 @@ -// 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/default/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 { - /// /// - /// 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 - MediaMetadataParams memory params = abi.decode(_data, (MediaMetadataParams)); - - // Store the renderer settings - settings.projectURI = params.projectURI; - settings.description = params.description; - settings.contractImage = params.contractImage; - settings.projectURI = params.projectURI; - settings.token = _token; - } - - /// /// - /// PROPERTIES & ITEMS /// - /// /// - - /// @notice The number of total media items - function mediaItemsCount() 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 Adds media items to be sequentially chosen from during token minting - /// @param _items The items to add - function addMediaItems(MediaItem[] calldata _items) external onlyOwner { - _addMediaItems(_items); - } - - /// @notice Deletes existing media items to be sequentially chosen from during token minting, replacing them with provided items. 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 - 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; - - // Minimum of 1 media item required - 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].contentURI = _items[i].contentURI; - } - } - } - - /// /// - /// ATTRIBUTE GENERATION /// - /// /// - - /// @notice Generates attributes for a token upon mint - /// @param _tokenId The ERC-721 token id - function onMinted(uint256 _tokenId) external view override returns (bool) { - // Ensure the caller is the token contract - if (msg.sender != settings.token) revert ONLY_TOKEN(); - - return _tokenId < mediaItems.length; - } - - /// /// - /// 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) { - // Pull the media item refrence by tokenId - MediaItem storage mediaItem = mediaItems[_tokenId]; - - MetadataBuilder.JSONItem[] memory items = new MetadataBuilder.JSONItem[](4); - - // Set JSON properties - 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: mediaItem.imageURI, quote: true }); - items[3] = MetadataBuilder.JSONItem({ key: MetadataJSONKeys.keyAnimationURL, value: mediaItem.contentURI, quote: true }); - - return MetadataBuilder.generateEncodedJSON(items); - } - - /// @notice The token data - /// @param tokenId The ERC-721 token id - function tokenData(uint256 tokenId) - external - view - override - returns ( - string memory name, - string memory imageURI, - string memory contentURI - ) - { - MediaItem storage mediaItem = mediaItems[tokenId]; - name = string.concat(_name(), " #", Strings.toString(tokenId)); - imageURI = mediaItem.imageURI; - contentURI = mediaItem.contentURI; - } - - /// /// - /// 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 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; - } - - /// @notice Updates the project URI - /// @param _newProjectURI The new URI - 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 deleted file mode 100644 index 2b2e4b4..0000000 --- a/src/metadata/media/interfaces/IMediaMetadata.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -import { MediaMetadataTypesV1 } from "../types/MediaMetadataTypesV1.sol"; -import { IBaseMetadata } from "../../interfaces/IBaseMetadata.sol"; - -/// @title IMediaMetadata -/// @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); - - /// /// - /// 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(); - - /// /// - /// STRUCTS /// - /// /// - - struct MediaMetadataParams { - /// @notice The collection description - string description; - /// @notice The contract image - string contractImage; - /// @notice The project URI - string projectURI; - } -} diff --git a/src/metadata/media/storage/MediaMetadataStorageV1.sol b/src/metadata/media/storage/MediaMetadataStorageV1.sol deleted file mode 100644 index 5fd0292..0000000 --- a/src/metadata/media/storage/MediaMetadataStorageV1.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -import { MediaMetadataTypesV1 } from "../types/MediaMetadataTypesV1.sol"; - -/// @title MediaMetadataStorageV1 -/// @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 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 deleted file mode 100644 index aac67a0..0000000 --- a/src/metadata/media/types/MediaMetadataTypesV1.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -/// @title MediaMetadataTypesV1 -/// @author Neokry -/// @notice The Metadata Renderer custom data types -interface MediaMetadataTypesV1 { - struct MediaItem { - /// @notice The image content URI - string imageURI; - /// @notice The content URI - string contentURI; - } - - struct Settings { - /// @notice The token address - address token; - /// @notice The project URI - string projectURI; - /// @notice The project description - string description; - /// @notice The token contract image - string contractImage; - } - - struct AdditionalTokenProperty { - string key; - string value; - bool quote; - } -} diff --git a/src/minters/ERC721RedeemMinter.sol b/src/minters/ERC721RedeemMinter.sol index a0fc559..ed86337 100644 --- a/src/minters/ERC721RedeemMinter.sol +++ b/src/minters/ERC721RedeemMinter.sol @@ -2,15 +2,14 @@ pragma solidity 0.8.16; import { IERC721 } from "../lib/interfaces/IERC721.sol"; -import { IBaseToken } from "../token/interfaces/IBaseToken.sol"; +import { IToken } from "../token/default/IToken.sol"; import { IManager } from "../manager/IManager.sol"; import { IOwnable } from "../lib/interfaces/IOwnable.sol"; -import { IMintStrategy } from "./interfaces/IMintStrategy.sol"; /// @title ERC721RedeemMinter /// @notice A mint strategy that allows ERC721 token holders to redeem DAO tokens /// @author @neokry -contract ERC721RedeemMinter is IMintStrategy { +contract ERC721RedeemMinter { /// /// /// EVENTS /// /// /// @@ -142,7 +141,7 @@ contract ERC721RedeemMinter is IMintStrategy { } // Mint to the redeeem token owner - IBaseToken(tokenContract).mintFromReserveTo(owner, tokenId); + IToken(tokenContract).mintFromReserveTo(owner, tokenId); } } @@ -221,22 +220,6 @@ contract ERC721RedeemMinter is IMintStrategy { /// SETTINGS /// /// /// - // @notice Sets the minter settings from the token contract with generic data - /// @param data Encoded settings to set - function setMintSettings(bytes calldata data) external { - // Decode settings data - RedeemSettings memory settings = abi.decode(data, (RedeemSettings)); - - // Cache sender - address sender = msg.sender; - - // Set new collection settings - _setMintSettings(sender, settings); - - // Emit event for new settings - emit MinterSet(sender, settings); - } - /// @notice Sets the minter settings for a token /// @param tokenContract Token contract to set settings for /// @param settings Settings to set diff --git a/src/minters/MerkleReserveMinter.sol b/src/minters/MerkleReserveMinter.sol index b0b581a..f77cd96 100644 --- a/src/minters/MerkleReserveMinter.sol +++ b/src/minters/MerkleReserveMinter.sol @@ -3,14 +3,13 @@ pragma solidity 0.8.16; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import { IOwnable } from "../lib/interfaces/IOwnable.sol"; -import { IBaseToken } from "../token/interfaces/IBaseToken.sol"; +import { IToken } from "../token/default/IToken.sol"; import { IManager } from "../manager/IManager.sol"; -import { IMintStrategy } from "./interfaces/IMintStrategy.sol"; /// @title MerkleReserveMinter /// @notice A mint strategy that mints reserved tokens based on a merkle tree /// @author @neokry -contract MerkleReserveMinter is IMintStrategy { +contract MerkleReserveMinter { /// /// /// EVENTS /// /// /// @@ -151,7 +150,7 @@ contract MerkleReserveMinter is IMintStrategy { } // Only allowing reserved tokens to be minted for this strategy - IBaseToken(tokenContract).mintFromReserveTo(claim.mintTo, claim.tokenId); + IToken(tokenContract).mintFromReserveTo(claim.mintTo, claim.tokenId); } } @@ -170,23 +169,6 @@ contract MerkleReserveMinter is IMintStrategy { /// /// /// Settings /// - /// /// - - /// @notice Sets the minter settings from the token contract with generic data - /// @param data Encoded settings to set - function setMintSettings(bytes calldata data) external { - // Decode settings data - MerkleMinterSettings memory settings = abi.decode(data, (MerkleMinterSettings)); - - // Cache sender - address sender = msg.sender; - - // Set new collection settings - _setMintSettings(sender, settings); - - // Emit event for new settings - emit MinterSet(sender, settings); - } /// @notice Sets the minter settings for a token /// @param tokenContract Token contract to set settings for diff --git a/src/minters/interfaces/IMintStrategy.sol b/src/minters/interfaces/IMintStrategy.sol deleted file mode 100644 index 2611d2c..0000000 --- a/src/minters/interfaces/IMintStrategy.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -/// @title IMintStrategy -/// @notice The interface for external token minting strategies -/// @author @neokry -interface IMintStrategy { - /// @notice Sets the mint settings for a token - /// @param data The encoded mint settings - function setMintSettings(bytes calldata data) external; -} diff --git a/src/token/default/IToken.sol b/src/token/default/IToken.sol index 67b8897..6cff84f 100644 --- a/src/token/default/IToken.sol +++ b/src/token/default/IToken.sol @@ -4,14 +4,14 @@ 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 { IBaseToken } from "../interfaces/IBaseToken.sol"; import { TokenTypesV1 } from "./types/TokenTypesV1.sol"; import { TokenTypesV2 } from "./types/TokenTypesV2.sol"; +import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol"; /// @title IToken /// @author Rohan Kulkarni /// @notice The external Token events, errors and functions -interface IToken is IUUPS, IERC721Votes, IBaseToken, TokenTypesV1, TokenTypesV2 { +interface IToken is IUUPS, IERC721Votes, TokenTypesV1, TokenTypesV2 { /// /// /// EVENTS /// /// /// @@ -66,41 +66,46 @@ interface IToken is IUUPS, IERC721Votes, IBaseToken, TokenTypesV1, TokenTypesV2 /// @dev Reverts if the token is not reserved error TOKEN_NOT_RESERVED(); - /// /// - /// STRUCTS /// - /// /// - - /// @notice The tokens initilization parameters - struct TokenParams { - /// @notice The token name - string name; - /// @notice The token symbol - string symbol; - /// @notice The tokenId that a DAO's auctions will start at - uint256 reservedUntilTokenId; - /// @notice The minter a DAO enables by default - address initialMinter; - /// @notice The initilization data for the initial minter - bytes initialMinterData; - } - /// /// /// FUNCTIONS /// /// /// - /// @notice Initializes a DAO's ERC-721 token - /// @param founders The founding members to receive vesting allocations - /// @param data The encoded token and metadata initialization strings + /// @notice Initializes a DAO's ERC-721 token contract + /// @param founders The DAO founders + /// @param initStrings The encoded token and metadata initialization strings + /// @param reservedUntilTokenId The tokenId that a DAO's auctions will start at /// @param metadataRenderer The token's metadata renderer /// @param auction The token's auction house + /// @param initialOwner The initial owner of the token function initialize( IManager.FounderParams[] calldata founders, - bytes calldata data, + bytes calldata initStrings, + uint256 reservedUntilTokenId, address metadataRenderer, address auction, address initialOwner ) external; + /// @notice Mints tokens to the caller and handles founder vesting + function mint() external returns (uint256 tokenId); + + /// @notice Mints tokens to the recipient and handles founder vesting + function mintTo(address recipient) external returns (uint256 tokenId); + + /// @notice Mints the specified amount of tokens to the recipient and handles founder vesting + function mintBatchTo(uint256 amount, address recipient) external returns (uint256[] memory tokenIds); + + /// @notice Burns a token owned by the caller + /// @param tokenId The ERC-721 token id + function burn(uint256 tokenId) external; + + /// @notice The URI for a token + /// @param tokenId The ERC-721 token id + function tokenURI(uint256 tokenId) external view returns (string memory); + + /// @notice The URI for the contract + function contractURI() external view returns (string memory); + /// @notice The number of founders function totalFounders() external view returns (uint256); @@ -118,6 +123,9 @@ interface IToken is IUUPS, IERC721Votes, IBaseToken, TokenTypesV1, TokenTypesV2 /// @param newFounders the full list of FounderParam structs function updateFounders(IManager.FounderParams[] calldata newFounders) external; + /// @notice Mints tokens from the reserve to the recipient + function mintFromReserveTo(address recipient, uint256 tokenId) external; + /// @notice Update minters /// @param _minters Array of structs containing address status as a minter function updateMinters(MinterParams[] calldata _minters) external; @@ -125,4 +133,8 @@ interface IToken is IUUPS, IERC721Votes, IBaseToken, TokenTypesV1, TokenTypesV2 /// @notice Check if an address is a minter /// @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; } diff --git a/src/token/default/Token.sol b/src/token/default/Token.sol index 81be0dc..eb9268c 100644 --- a/src/token/default/Token.sol +++ b/src/token/default/Token.sol @@ -12,10 +12,8 @@ import { TokenStorageV3 } from "./storage/TokenStorageV3.sol"; import { IManager } from "../../manager/IManager.sol"; import { IAuction } from "../../auction/IAuction.sol"; import { IToken } from "./IToken.sol"; -import { IBaseToken } from "../interfaces/IBaseToken.sol"; import { VersionedContract } from "../../VersionedContract.sol"; -import { IBaseMetadata } from "../../metadata/interfaces/IBaseMetadata.sol"; -import { IMintStrategy } from "../../minters/interfaces/IMintStrategy.sol"; +import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol"; /// @title Token /// @author Rohan Kulkarni & Neokry @@ -66,13 +64,15 @@ contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC /// @notice Initializes a DAO's ERC-721 token contract /// @param _founders The DAO founders - /// @param _data The encoded token initialization parameters + /// @param _initStrings The encoded token and metadata initialization strings + /// @param _reservedUntilTokenId The tokenId that a DAO's auctions will start at /// @param _metadataRenderer The token's metadata renderer /// @param _auction The token's auction house /// @param _initialOwner The initial owner of the token function initialize( IManager.FounderParams[] calldata _founders, - bytes calldata _data, + bytes calldata _initStrings, + uint256 _reservedUntilTokenId, address _metadataRenderer, address _auction, address _initialOwner @@ -88,34 +88,24 @@ contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC // Setup ownable __Ownable_init(_initialOwner); - // Decode the token name and symbol - IToken.TokenParams memory params = abi.decode(_data, (IToken.TokenParams)); - // Store the founders and compute their allocations - _addFounders(_founders, params.reservedUntilTokenId); + _addFounders(_founders, _reservedUntilTokenId); + + // Decode the token name and symbol + (string memory _name, string memory _symbol, , , , ) = abi.decode(_initStrings, (string, string, string, string, string, string)); // Initialize the ERC-721 token - __ERC721_init(params.name, params.symbol); + __ERC721_init(_name, _symbol); // Store the metadata renderer and auction house settings.metadataRenderer = IBaseMetadata(_metadataRenderer); settings.auction = _auction; - reservedUntilTokenId = params.reservedUntilTokenId; - - // Check if an initial minter was specified - if (params.initialMinter != address(0)) { - minter[params.initialMinter] = true; - - // Set minter settings if specified - if (params.initialMinterData.length > 0) { - IMintStrategy(params.initialMinter).setMintSettings(params.initialMinterData); - } - } + reservedUntilTokenId = _reservedUntilTokenId; } /// @notice Called by the auction upon the first unpause / token mint to transfer ownership from founder to treasury /// @dev Only callable by the auction contract - function onFirstAuctionStarted() external override { + function onFirstAuctionStarted() external { if (msg.sender != settings.auction) { revert ONLY_AUCTION(); } @@ -325,12 +315,12 @@ contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC /// @notice The URI for a token /// @param _tokenId The ERC-721 token id - function tokenURI(uint256 _tokenId) public view override(IBaseToken, ERC721) returns (string memory) { + function tokenURI(uint256 _tokenId) public view override(ERC721, IToken) returns (string memory) { return settings.metadataRenderer.tokenURI(_tokenId); } /// @notice The URI for the contract - function contractURI() public view override(IBaseToken, ERC721) returns (string memory) { + function contractURI() public view override(ERC721, IToken) returns (string memory) { return settings.metadataRenderer.contractURI(); } @@ -466,7 +456,7 @@ contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC } /// @notice The contract owner - function owner() public view override(IBaseToken, Ownable) returns (address) { + function owner() public view override returns (address) { return super.owner(); } diff --git a/src/token/default/types/TokenTypesV1.sol b/src/token/default/types/TokenTypesV1.sol index 7d91255..72162ea 100644 --- a/src/token/default/types/TokenTypesV1.sol +++ b/src/token/default/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/src/token/interfaces/IBaseToken.sol b/src/token/interfaces/IBaseToken.sol deleted file mode 100644 index 80c8750..0000000 --- a/src/token/interfaces/IBaseToken.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -import { IERC721 } from "../../lib/interfaces/IERC721.sol"; -import { IERC721Votes } from "../../lib/interfaces/IERC721Votes.sol"; -import { IManager } from "../../manager/IManager.sol"; -import { IBaseMetadata } from "../../metadata/interfaces/IBaseMetadata.sol"; - -/// @title IBaseToken -/// @author Neokry -/// @notice The external Token events, errors and functions -interface IBaseToken is IERC721, IERC721Votes { - /// /// - /// FUNCTIONS /// - /// /// - - /// @notice Initializes a DAO's ERC-721 token - /// @param founders The founding members to receive vesting allocations - /// @param data The encoded token and metadata initialization strings - /// @param metadataRenderer The token's metadata renderer - /// @param auction The token's auction house - function initialize( - IManager.FounderParams[] calldata founders, - bytes calldata data, - address metadataRenderer, - address auction, - address initialOwner - ) external; - - /// @notice Mints tokens to the caller and handles founder vesting - function mint() external returns (uint256 tokenId); - - /// @notice Mints tokens to the recipient and handles founder vesting - function mintTo(address recipient) external returns (uint256 tokenId); - - /// @notice Mints the specified amount of tokens to the recipient and handles founder vesting - function mintBatchTo(uint256 amount, address recipient) external returns (uint256[] memory tokenIds); - - /// @notice Mints the specified token from the reserve to the recipent - function mintFromReserveTo(address recipient, uint256 tokenId) external; - - /// @notice Burns a token owned by the caller - /// @param tokenId The ERC-721 token id - function burn(uint256 tokenId) external; - - /// @notice The URI for a token - /// @param tokenId The ERC-721 token id - function tokenURI(uint256 tokenId) external view returns (string memory); - - /// @notice The URI for the contract - function contractURI() external view returns (string memory); - - /// @notice The total supply of tokens - function totalSupply() external view returns (uint256); - - /// @notice The token's auction house - function auction() external view returns (address); - - /// @notice The token's metadata renderer - function metadataRenderer() external view returns (address); - - /// @notice The owner of the token and metadata renderer - function owner() external view returns (address); - - /// @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/metadata/property/PropertyMetadata.sol b/src/token/metadata/MetadataRenderer.sol similarity index 89% rename from src/metadata/property/PropertyMetadata.sol rename to src/token/metadata/MetadataRenderer.sol index e2363fc..32b7bde 100644 --- a/src/metadata/property/PropertyMetadata.sol +++ b/src/token/metadata/MetadataRenderer.sol @@ -12,19 +12,25 @@ import { Initializable } from "../../lib/utils/Initializable.sol"; import { IOwnable } from "../../lib/interfaces/IOwnable.sol"; import { ERC721 } from "../../lib/token/ERC721.sol"; -import { IToken } from "../../token/default/IToken.sol"; -import { PropertyMetadataStorageV1 } from "./storage/PropertyMetadataStorageV1.sol"; -import { PropertyMetadataStorageV2 } from "./storage/PropertyMetadataStorageV2.sol"; -import { IPropertyMetadata } from "./interfaces/IPropertyMetadata.sol"; +import { MetadataRendererStorageV1 } from "./storage/MetadataRendererStorageV1.sol"; +import { MetadataRendererStorageV2 } from "./storage/MetadataRendererStorageV2.sol"; +import { IToken } from "../default/IToken.sol"; +import { IPropertyIPFSMetadataRenderer } from "./interfaces/IPropertyIPFSMetadataRenderer.sol"; import { IManager } from "../../manager/IManager.sol"; -import { IBaseMetadata } from "../interfaces/IBaseMetadata.sol"; import { VersionedContract } from "../../VersionedContract.sol"; -/// @title Property IPFS Metadata Renderer +/// @title Metadata Renderer /// @author Iain Nash & Rohan Kulkarni /// @notice A DAO's artwork generator and renderer /// @custom:repo github.com/ourzora/nouns-protocol -contract PropertyMetadata is IPropertyMetadata, VersionedContract, Initializable, UUPS, PropertyMetadataStorageV1, PropertyMetadataStorageV2 { +contract MetadataRenderer is + IPropertyIPFSMetadataRenderer, + VersionedContract, + Initializable, + UUPS, + MetadataRendererStorageV1, + MetadataRendererStorageV2 +{ /// /// /// IMMUTABLES /// /// /// @@ -59,23 +65,26 @@ contract PropertyMetadata is IPropertyMetadata, VersionedContract, Initializable /// /// /// @notice Initializes a DAO's token metadata renderer - /// @param _data The encoded metadata initialization parameters + /// @param _initStrings The encoded token and metadata initialization strings /// @param _token The ERC-721 token address - function initialize(bytes calldata _data, address _token) external initializer { + function initialize(bytes calldata _initStrings, address _token) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) { revert ONLY_MANAGER(); } // Decode the token initialization strings - PropertyMetadataParams memory params = abi.decode(_data, (PropertyMetadataParams)); + (, , string memory _description, string memory _contractImage, string memory _projectURI, string memory _rendererBase) = abi.decode( + _initStrings, + (string, string, string, string, string, string) + ); // 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.projectURI = _projectURI; + settings.description = _description; + settings.contractImage = _contractImage; + settings.rendererBase = _rendererBase; + settings.projectURI = _projectURI; settings.token = _token; } @@ -233,19 +242,38 @@ contract PropertyMetadata is IPropertyMetadata, VersionedContract, Initializable 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); - } - /// @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; + // 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 (uint256 i = startId; i < tokensLen; ++i) { - results[i] = _generateMetadata(i); + // 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; } /// @notice The properties and query string for a generated token @@ -297,40 +325,6 @@ contract PropertyMetadata is IPropertyMetadata, VersionedContract, Initializable } } - 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); - - // Get next 16 random bits - 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))); @@ -395,25 +389,6 @@ contract PropertyMetadata is IPropertyMetadata, VersionedContract, Initializable return MetadataBuilder.generateEncodedJSON(items); } - /// @notice The token data - /// @param tokenId The ERC-721 token id - function tokenData(uint256 tokenId) - external - view - override - returns ( - string memory name, - string memory imageURI, - string memory contentURI - ) - { - (, string memory queryString) = getAttributes(tokenId); - - name = string.concat(_name(), " #", Strings.toString(tokenId)); - imageURI = string.concat(settings.rendererBase, queryString); - contentURI = ""; - } - /// /// /// METADATA SETTINGS /// /// /// diff --git a/src/token/metadata/interfaces/IBaseMetadata.sol b/src/token/metadata/interfaces/IBaseMetadata.sol new file mode 100644 index 0000000..265d0a7 --- /dev/null +++ b/src/token/metadata/interfaces/IBaseMetadata.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +import { IUUPS } from "../../../lib/interfaces/IUUPS.sol"; + + +/// @title IBaseMetadata +/// @author Rohan Kulkarni +/// @notice The external Base Metadata errors and functions +interface IBaseMetadata is IUUPS { + /// /// + /// ERRORS /// + /// /// + + /// @dev Reverts if the caller was not the contract manager + error ONLY_MANAGER(); + + /// /// + /// FUNCTIONS /// + /// /// + + /// @notice Initializes a DAO's token metadata renderer + /// @param initStrings The encoded token and metadata initialization strings + /// @param token The associated ERC-721 token address + function initialize( + bytes calldata initStrings, + address token + ) external; + + /// @notice Generates attributes for a token upon mint + /// @param tokenId The ERC-721 token id + function onMinted(uint256 tokenId) external returns (bool); + + /// @notice The token URI + /// @param tokenId The ERC-721 token id + function tokenURI(uint256 tokenId) external view returns (string memory); + + /// @notice The contract URI + function contractURI() external view returns (string memory); + + /// @notice The associated ERC-721 token + function token() external view returns (address); + + /// @notice Get metadata owner address + function owner() external view returns (address); +} diff --git a/src/metadata/property/interfaces/IPropertyMetadata.sol b/src/token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol similarity index 69% rename from src/metadata/property/interfaces/IPropertyMetadata.sol rename to src/token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol index 34a4b11..1a8df9a 100644 --- a/src/metadata/property/interfaces/IPropertyMetadata.sol +++ b/src/token/metadata/interfaces/IPropertyIPFSMetadataRenderer.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { PropertyMetadataTypesV1 } from "../types/PropertyMetadataTypesV1.sol"; -import { PropertyMetadataTypesV2 } from "../types/PropertyMetadataTypesV2.sol"; -import { IBaseMetadata } from "../../interfaces/IBaseMetadata.sol"; +import { MetadataRendererTypesV1 } from "../types/MetadataRendererTypesV1.sol"; +import { MetadataRendererTypesV2 } from "../types/MetadataRendererTypesV2.sol"; +import { IBaseMetadata } from "./IBaseMetadata.sol"; -/// @title IPropertyMetadata +/// @title IPropertyIPFSMetadataRenderer /// @author Iain Nash & Rohan Kulkarni /// @notice The external Metadata Renderer events, errors, and functions -interface IPropertyMetadata is IBaseMetadata, PropertyMetadataTypesV1, PropertyMetadataTypesV2 { +interface IPropertyIPFSMetadataRenderer is IBaseMetadata, MetadataRendererTypesV1, MetadataRendererTypesV2 { /// /// /// EVENTS /// /// /// @@ -19,9 +19,18 @@ interface IPropertyMetadata is IBaseMetadata, PropertyMetadataTypesV1, PropertyM /// @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 renderer base is updated event RendererBaseUpdated(string prevRendererBase, string newRendererBase); + /// @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 /// /// /// @@ -41,21 +50,6 @@ interface IPropertyMetadata is IBaseMetadata, PropertyMetadataTypesV1, PropertyM /// error TOO_MANY_PROPERTIES(); - /// /// - /// STRUCTS /// - /// /// - - struct PropertyMetadataParams { - /// @notice The collection description - string description; - /// @notice The contract image - string contractImage; - /// @notice The project URI - string projectURI; - /// @notice The renderer base - string rendererBase; - } - /// /// /// FUNCTIONS /// /// /// @@ -81,10 +75,24 @@ interface IPropertyMetadata is IBaseMetadata, PropertyMetadataTypesV1, PropertyM /// @param tokenId The ERC-721 token id function getAttributes(uint256 tokenId) external view returns (string memory resultAttributes, string memory queryString); + /// @notice The contract image + function contractImage() external view returns (string memory); + /// @notice The renderer base function rendererBase() 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 renderer base /// @param newRendererBase The new renderer base function updateRendererBase(string memory newRendererBase) external; + + /// @notice Updates the collection description + /// @param newDescription The new description + function updateDescription(string memory newDescription) external; } diff --git a/src/metadata/property/storage/PropertyMetadataStorageV1.sol b/src/token/metadata/storage/MetadataRendererStorageV1.sol similarity index 79% rename from src/metadata/property/storage/PropertyMetadataStorageV1.sol rename to src/token/metadata/storage/MetadataRendererStorageV1.sol index 474b426..be0f856 100644 --- a/src/metadata/property/storage/PropertyMetadataStorageV1.sol +++ b/src/token/metadata/storage/MetadataRendererStorageV1.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { PropertyMetadataTypesV1 } from "../types/PropertyMetadataTypesV1.sol"; +import { MetadataRendererTypesV1 } from "../types/MetadataRendererTypesV1.sol"; -/// @title PropertyMetadataTypesV1 +/// @title MetadataRendererTypesV1 /// @author Iain Nash & Rohan Kulkarni /// @notice The Metadata Renderer storage contract -contract PropertyMetadataStorageV1 is PropertyMetadataTypesV1 { +contract MetadataRendererStorageV1 is MetadataRendererTypesV1 { /// @notice The metadata renderer settings Settings public settings; diff --git a/src/metadata/property/storage/PropertyMetadataStorageV2.sol b/src/token/metadata/storage/MetadataRendererStorageV2.sol similarity index 66% rename from src/metadata/property/storage/PropertyMetadataStorageV2.sol rename to src/token/metadata/storage/MetadataRendererStorageV2.sol index 2c5ecbf..3b28adc 100644 --- a/src/metadata/property/storage/PropertyMetadataStorageV2.sol +++ b/src/token/metadata/storage/MetadataRendererStorageV2.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import { PropertyMetadataTypesV2 } from "../types/PropertyMetadataTypesV2.sol"; +import { MetadataRendererTypesV2 } from "../types/MetadataRendererTypesV2.sol"; -/// @title PropertyMetadataTypesV1 +/// @title MetadataRendererTypesV1 /// @author Iain Nash & Rohan Kulkarni /// @notice The Metadata Renderer storage contract -contract PropertyMetadataStorageV2 is PropertyMetadataTypesV2 { +contract MetadataRendererStorageV2 is MetadataRendererTypesV2 { /// @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/property/types/PropertyMetadataTypesV1.sol b/src/token/metadata/types/MetadataRendererTypesV1.sol similarity index 90% rename from src/metadata/property/types/PropertyMetadataTypesV1.sol rename to src/token/metadata/types/MetadataRendererTypesV1.sol index ca88604..062e7b7 100644 --- a/src/metadata/property/types/PropertyMetadataTypesV1.sol +++ b/src/token/metadata/types/MetadataRendererTypesV1.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -/// @title PropertyMetadataTypesV1 +/// @title MetadataRendererTypesV1 /// @author Iain Nash & Rohan Kulkarni /// @notice The Metadata Renderer custom data types -interface PropertyMetadataTypesV1 { +interface MetadataRendererTypesV1 { struct ItemParam { uint256 propertyId; string name; diff --git a/src/metadata/property/types/PropertyMetadataTypesV2.sol b/src/token/metadata/types/MetadataRendererTypesV2.sol similarity index 78% rename from src/metadata/property/types/PropertyMetadataTypesV2.sol rename to src/token/metadata/types/MetadataRendererTypesV2.sol index fce8856..9321780 100644 --- a/src/metadata/property/types/PropertyMetadataTypesV2.sol +++ b/src/token/metadata/types/MetadataRendererTypesV2.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -/// @title PropertyMetadataTypesV2 +/// @title MetadataRendererTypesV2 /// @author Iain Nash & Rohan Kulkarni /// @notice The Metadata Renderer custom data types -interface PropertyMetadataTypesV2 { +interface MetadataRendererTypesV2 { struct AdditionalTokenProperty { string key; string value; diff --git a/src/token/partial-mirror/IPartialMirrorToken.sol b/src/token/partial-mirror/IPartialMirrorToken.sol index 6cb1843..f3914bc 100644 --- a/src/token/partial-mirror/IPartialMirrorToken.sol +++ b/src/token/partial-mirror/IPartialMirrorToken.sol @@ -4,14 +4,13 @@ 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 { IBaseToken } from "../interfaces/IBaseToken.sol"; import { IMirrorToken } from "../interfaces/IMirrorToken.sol"; import { PartialMirrorTokenTypesV1 } from "./types/PartialMirrorTokenTypesV1.sol"; /// @title IToken /// @author Neokry /// @notice The external Token events, errors and functions -interface IPartialMirrorToken is IUUPS, IERC721Votes, IBaseToken, IMirrorToken, PartialMirrorTokenTypesV1 { +interface IPartialMirrorToken is IUUPS, IERC721Votes, IMirrorToken, PartialMirrorTokenTypesV1 { /// /// /// EVENTS /// /// /// @@ -68,38 +67,22 @@ interface IPartialMirrorToken is IUUPS, IERC721Votes, IBaseToken, IMirrorToken, /// @dev Reverts if an approval function for a reserved token has been called error NO_APPROVALS(); - /// /// - /// STRUCTS /// - /// /// - - /// @notice The tokens initilization parameters - struct TokenParams { - /// @notice The token name - string name; - /// @notice The token symbol - string symbol; - /// @notice The tokenId that a DAO's auctions will start at - uint256 reservedUntilTokenId; - /// @notice The token contract to be mirrored - address tokenToMirror; - /// @notice The minter a DAO enables by default - address initialMinter; - /// @notice The initilization data for the initial minter - bytes initialMinterData; - } - /// /// /// FUNCTIONS /// /// /// - /// @notice Initializes a DAO's ERC-721 token - /// @param founders The founding members to receive vesting allocations - /// @param data The encoded token and metadata initialization strings + /// @notice Initializes a DAO's ERC-721 token contract + /// @param founders The DAO founders + /// @param initStrings The encoded token and metadata initialization strings + /// @param reservedUntilTokenId The tokenId that a DAO's auctions will start at /// @param metadataRenderer The token's metadata renderer /// @param auction The token's auction house + /// @param initialOwner The initial owner of the token function initialize( IManager.FounderParams[] calldata founders, - bytes calldata data, + bytes calldata initStrings, + uint256 reservedUntilTokenId, + address tokenToMirror, address metadataRenderer, address auction, address initialOwner diff --git a/src/token/partial-mirror/PartialMirrorToken.sol b/src/token/partial-mirror/PartialMirrorToken.sol index d2ed245..5cd5535 100644 --- a/src/token/partial-mirror/PartialMirrorToken.sol +++ b/src/token/partial-mirror/PartialMirrorToken.sol @@ -8,14 +8,12 @@ import { ERC721 } from "../../lib/token/ERC721.sol"; import { IERC721 } from "../../lib/interfaces/IERC721.sol"; import { Ownable } from "../../lib/utils/Ownable.sol"; import { PartialMirrorTokenStorageV1 } from "./storage/PartialMirrorTokenStorageV1.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 { IPartialMirrorToken } from "./IPartialMirrorToken.sol"; -import { IBaseToken } from "../interfaces/IBaseToken.sol"; import { VersionedContract } from "../../VersionedContract.sol"; -import { IMintStrategy } from "../../minters/interfaces/IMintStrategy.sol"; import { IMirrorToken } from "../interfaces/IMirrorToken.sol"; /// @title Token @@ -67,13 +65,16 @@ contract PartialMirrorToken is IPartialMirrorToken, VersionedContract, UUPS, Own /// @notice Initializes a DAO's ERC-721 token contract /// @param _founders The DAO founders - /// @param _data The encoded token initialization parameters + /// @param _initStrings The encoded token and metadata initialization strings + /// @param _reservedUntilTokenId The tokenId that a DAO's auctions will start at /// @param _metadataRenderer The token's metadata renderer /// @param _auction The token's auction house /// @param _initialOwner The initial owner of the token function initialize( IManager.FounderParams[] calldata _founders, - bytes calldata _data, + bytes calldata _initStrings, + uint256 _reservedUntilTokenId, + address _tokenToMirror, address _metadataRenderer, address _auction, address _initialOwner @@ -89,30 +90,20 @@ contract PartialMirrorToken is IPartialMirrorToken, VersionedContract, UUPS, Own // Setup ownable __Ownable_init(_initialOwner); - // Decode the token name and symbol - IPartialMirrorToken.TokenParams memory params = abi.decode(_data, (IPartialMirrorToken.TokenParams)); - // Store the founders and compute their allocations - _addFounders(_founders, params.reservedUntilTokenId); + _addFounders(_founders, _reservedUntilTokenId); + + // Decode the token name and symbol + (string memory _name, string memory _symbol, , , , ) = abi.decode(_initStrings, (string, string, string, string, string, string)); // Initialize the ERC-721 token - __ERC721_init(params.name, params.symbol); + __ERC721_init(_name, _symbol); // Store the metadata renderer and auction house settings.metadataRenderer = IBaseMetadata(_metadataRenderer); settings.auction = _auction; - reservedUntilTokenId = params.reservedUntilTokenId; - tokenToMirror = params.tokenToMirror; - - // Check if an initial minter was specified - if (params.initialMinter != address(0)) { - minter[params.initialMinter] = true; - - // Set minter settings if specified - if (params.initialMinterData.length > 0) { - IMintStrategy(params.initialMinter).setMintSettings(params.initialMinterData); - } - } + reservedUntilTokenId = _reservedUntilTokenId; + tokenToMirror = _tokenToMirror; } /// @notice Called by the auction upon the first unpause / token mint to transfer ownership from founder to treasury @@ -461,12 +452,12 @@ contract PartialMirrorToken is IPartialMirrorToken, VersionedContract, UUPS, Own /// @notice The URI for a token /// @param _tokenId The ERC-721 token id - function tokenURI(uint256 _tokenId) public view override(IBaseToken, ERC721) returns (string memory) { + function tokenURI(uint256 _tokenId) public view override(ERC721) returns (string memory) { return settings.metadataRenderer.tokenURI(_tokenId); } /// @notice The URI for the contract - function contractURI() public view override(IBaseToken, ERC721) returns (string memory) { + function contractURI() public view override(ERC721) returns (string memory) { return settings.metadataRenderer.contractURI(); } @@ -602,7 +593,7 @@ contract PartialMirrorToken is IPartialMirrorToken, VersionedContract, UUPS, Own } /// @notice The contract owner - function owner() public view override(IBaseToken, Ownable) returns (address) { + function owner() public view override(Ownable) returns (address) { return super.owner(); } diff --git a/src/token/partial-mirror/types/PartialMirrorTokenTypesV1.sol b/src/token/partial-mirror/types/PartialMirrorTokenTypesV1.sol index fbe46d7..2ad3425 100644 --- a/src/token/partial-mirror/types/PartialMirrorTokenTypesV1.sol +++ b/src/token/partial-mirror/types/PartialMirrorTokenTypesV1.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 PartialMirrorTokenTypesV1 /// @author Neokry diff --git a/test/Auction.t.sol b/test/Auction.t.sol index 7d837f9..ae60f17 100644 --- a/test/Auction.t.sol +++ b/test/Auction.t.sol @@ -34,9 +34,7 @@ contract AuctionTest is NounsBuilderTest { setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); } @@ -57,7 +55,7 @@ contract AuctionTest is NounsBuilderTest { deployMock(); vm.expectRevert(abi.encodeWithSignature("ALREADY_INITIALIZED()")); - auction.initialize(address(token), address(this), address(treasury), implData[2]); + auction.initialize(address(token), address(this), address(treasury), 1 minutes, 0 ether, address(0), 0); } function test_Unpause() public { diff --git a/test/ERC721RedeemMinter.t.sol b/test/ERC721RedeemMinter.t.sol index cb26a10..c103d09 100644 --- a/test/ERC721RedeemMinter.t.sol +++ b/test/ERC721RedeemMinter.t.sol @@ -17,7 +17,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { function setUp() public virtual override { super.setUp(); - minter = new ERC721RedeemMinter(manager, builderDAO); + minter = new ERC721RedeemMinter(manager, zoraDAO); redeemToken = new MockERC721(); claimer1 = address(0xC1); @@ -27,21 +27,27 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { function deployAltMockAndSetMinter( uint256 _reservedUntilTokenId, address _minter, - bytes memory _minterData + ERC721RedeemMinter.RedeemSettings memory _minterData ) internal virtual { setMockFounderParams(); - setMockTokenParamsWithReserveAndMinter(_reservedUntilTokenId, _minter, _minterData); + setMockTokenParamsWithReserve(_reservedUntilTokenId); setMockAuctionParams(); setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); + + TokenTypesV2.MinterParams[] memory minters = new TokenTypesV2.MinterParams[](1); + minters[0] = TokenTypesV2.MinterParams({ minter: _minter, allowed: true }); + + vm.startPrank(token.owner()); + token.updateMinters(minters); + minter.setMintSettings(address(token), _minterData); + vm.stopPrank(); } function test_MintFlow() public { @@ -52,7 +58,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { redeemToken: address(redeemToken) }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); (uint64 mintStart, uint64 mintEnd, uint64 pricePerToken, address redeem) = minter.redeemSettings(address(token)); assertEq(mintStart, settings.mintStart); @@ -78,7 +84,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { redeemToken: address(redeemToken) }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); redeemToken.mint(claimer1, 4); redeemToken.mint(claimer1, 5); @@ -104,7 +110,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { redeemToken: address(redeemToken) }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); uint256[] memory tokenIds = new uint256[](1); tokenIds[0] = 4; @@ -121,7 +127,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { redeemToken: address(redeemToken) }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); redeemToken.mint(claimer1, 4); @@ -148,7 +154,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { redeemToken: address(redeemToken) }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); redeemToken.mint(claimer1, 4); redeemToken.mint(claimer1, 5); @@ -181,7 +187,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { redeemToken: address(redeemToken) }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); redeemToken.mint(claimer1, 4); redeemToken.mint(claimer1, 5); @@ -206,7 +212,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { redeemToken: address(redeemToken) }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); redeemToken.mint(claimer1, 4); @@ -227,7 +233,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { vm.warp(block.timestamp + 2); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); redeemToken.mint(claimer1, 4); @@ -246,7 +252,7 @@ contract ERC721RedeemMinterTest is NounsBuilderTest { redeemToken: address(redeemToken) }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); vm.prank(founder); minter.resetMintSettings(address(token)); diff --git a/test/Gov.t.sol b/test/Gov.t.sol index 9f96e3c..d6d77e1 100644 --- a/test/Gov.t.sol +++ b/test/Gov.t.sol @@ -17,7 +17,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { address internal voter2; uint256 internal voter2PK; - IGovernor.GovParams internal altGovParams; + IManager.GovParams internal altGovParams; function setUp() public virtual override { super.setUp(); @@ -48,9 +48,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { setGovParams(2 days, 1 days, 1 weeks, 25, 1000, founder); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); } @@ -77,9 +75,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { setGovParams(2 days, 1 days, 1 weeks, 100, 1000, founder); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); } @@ -221,21 +217,21 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { deployMock(); assertEq(treasury.owner(), address(governor)); - assertEq(treasury.delay(), treasuryParams.timelockDelay); + assertEq(treasury.delay(), govParams.timelockDelay); } function testRevert_CannotReinitializeGovernor() public { deployMock(); vm.expectRevert(abi.encodeWithSignature("ALREADY_INITIALIZED()")); - governor.initialize(address(this), address(this), new bytes(0)); + governor.initialize(address(this), address(this), address(this), 0, 0, 0, 0); } function testRevert_CannotReinitializeTreasury() public { deployMock(); vm.expectRevert(abi.encodeWithSignature("ALREADY_INITIALIZED()")); - treasury.initialize(address(this), new bytes(0)); + treasury.initialize(address(this), 0); } function test_CreateProposal() public { diff --git a/test/Manager.t.sol b/test/Manager.t.sol index 9eb0427..f3effba 100644 --- a/test/Manager.t.sol +++ b/test/Manager.t.sol @@ -84,7 +84,7 @@ contract ManagerTest is NounsBuilderTest { deployMock(); assertEq(treasury.owner(), address(governor)); - assertEq(treasury.delay(), treasuryParams.timelockDelay); + assertEq(treasury.delay(), govParams.timelockDelay); } function test_GovernorInitialized() public { @@ -105,7 +105,7 @@ contract ManagerTest is NounsBuilderTest { foundersArr.push(); vm.expectRevert(abi.encodeWithSignature("FOUNDER_REQUIRED()")); - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); } function test_RegisterUpgrade() public { @@ -139,78 +139,11 @@ contract ManagerTest is NounsBuilderTest { manager.removeUpgrade(address(token), address(mockImpl)); } - function test_RegisterImplementation() public { - address owner = manager.owner(); - - vm.prank(owner); - manager.registerImplementation(0, address(mockImpl)); - - assertTrue(manager.isRegisteredImplementation(0, address(mockImpl))); - } - - function test_RemoveImplementation() public { - address owner = manager.owner(); - - vm.prank(owner); - manager.registerImplementation(0, address(mockImpl)); - - vm.prank(owner); - manager.removeImplementation(0, address(mockImpl)); - - assertFalse(manager.isRegisteredImplementation(0, address(mockImpl))); - } - - function testRevert_OnlyOwnerCanRegisterImplementation() public { - vm.expectRevert(abi.encodeWithSignature("ONLY_OWNER()")); - manager.registerImplementation(0, address(mockImpl)); - } - - function testRevert_OnlyOwnerCanRemoveImplementation() public { - vm.expectRevert(abi.encodeWithSignature("ONLY_OWNER()")); - manager.removeImplementation(0, address(mockImpl)); - } - - function testRevert_InvalidImplementationType() public { - address owner = manager.owner(); - - vm.prank(owner); - vm.expectRevert(abi.encodeWithSignature("INVALID_IMPLEMENTATION_TYPE()")); - manager.registerImplementation(8, address(mockImpl)); - } - - function testRevert_InvalidImplementationAddresses() public { - address[] memory altImplAddresses = new address[](1); - - setupAltMock(); - - vm.expectRevert(abi.encodeWithSignature("INVALID_IMPLEMENTATION_PARAMS()")); - deploy(foundersArr, altImplAddresses, implData); - } - - function testRevert_InvalidImplementationData() public { - bytes[] memory altImplData = new bytes[](1); - - setupAltMock(); - - vm.expectRevert(abi.encodeWithSignature("INVALID_IMPLEMENTATION_PARAMS()")); - deploy(foundersArr, implAddresses, altImplData); - } - - function testRevert_UnregisteredImplementation() public { - address[] memory altImplAddresses = new address[](5); - altImplAddresses[0] = address(24); - - setupAltMock(); - - vm.expectRevert(abi.encodeWithSignature("IMPLEMENTATION_NOT_REGISTERED()")); - deploy(foundersArr, altImplAddresses, implData); - } - function test_SetNewRenderer() public { deployMock(); vm.startPrank(founder); - manager.setMetadataRenderer(address(token), metadataRendererImpl, implData[manager.IMPLEMENTATION_TYPE_METADATA()]); + manager.setMetadataRenderer(address(token), metadataRendererImpl, tokenParams.initStrings); vm.stopPrank(); } } diff --git a/test/MediaMetadata.t.sol b/test/MediaMetadata.t.sol deleted file mode 100644 index a158807..0000000 --- a/test/MediaMetadata.t.sol +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -import { NounsBuilderTest } from "./utils/NounsBuilderTest.sol"; -import { MediaMetadata } from "../src/metadata/media/MediaMetadata.sol"; -import { MediaMetadataTypesV1 } from "../src/metadata/media/types/MediaMetadataTypesV1.sol"; - -import { Base64URIDecoder } from "./utils/Base64URIDecoder.sol"; -import "forge-std/console2.sol"; - -contract MediaMetadataTest is NounsBuilderTest, MediaMetadataTypesV1 { - MediaMetadata mediaMetadata; - - function setUp() public virtual override { - super.setUp(); - - deployWithoutMetadata(); - - address mediaMetadataImpl = address(new MediaMetadata(address(manager))); - - vm.startPrank(founder); - address mediaAddress = manager.setMetadataRenderer(address(token), mediaMetadataImpl, implData[manager.IMPLEMENTATION_TYPE_METADATA()]); - vm.stopPrank(); - - mediaMetadata = MediaMetadata(mediaAddress); - } - - function testRevert_MustAddAtLeastOneMediaItem() public { - MediaItem[] memory items = new MediaItem[](0); - - vm.prank(founder); - vm.expectRevert(abi.encodeWithSignature("ONE_MEDIA_ITEM_REQUIRED()")); - mediaMetadata.addMediaItems(items); - - // Attempt to mint token #0 - vm.prank(address(token)); - bool response = mediaMetadata.onMinted(0); - - assertFalse(response); - } - - function test_AddNewMediaItems() public { - MediaItem[] memory items = new MediaItem[](1); - items[0] = MediaItem({ imageURI: "img1", contentURI: "content1" }); - - vm.prank(founder); - mediaMetadata.addMediaItems(items); - - vm.prank(address(token)); - bool response = mediaMetadata.onMinted(0); - assertTrue(response); - } - - function test_deleteAndRecreateMediaItems() public { - MediaItem[] memory items = new MediaItem[](2); - items[0] = MediaItem({ imageURI: "img1", contentURI: "content1" }); - items[1] = MediaItem({ imageURI: "img2", contentURI: "content2" }); - - vm.prank(founder); - mediaMetadata.addMediaItems(items); - - vm.prank(address(token)); - bool response = mediaMetadata.onMinted(0); - assertTrue(response); - - items[0] = MediaItem({ imageURI: "upsertImg1", contentURI: "upsertcontent1" }); - items[1] = MediaItem({ imageURI: "upsertImg2", contentURI: "upsertcontent2" }); - - vm.prank(founder); - mediaMetadata.deleteAndRecreateMediaItems(items); - - vm.prank(address(token)); - response = mediaMetadata.onMinted(0); - assertTrue(response); - } - - function test_MintPastItemCount() public { - MediaItem[] memory items = new MediaItem[](2); - items[0] = MediaItem({ imageURI: "img1", contentURI: "content1" }); - items[1] = MediaItem({ imageURI: "img2", contentURI: "content2" }); - - vm.prank(founder); - mediaMetadata.addMediaItems(items); - - vm.prank(address(token)); - bool response = mediaMetadata.onMinted(3); - assertFalse(response); - } - - function test_MintPastItemCountAndContinue() public { - MediaItem[] memory items = new MediaItem[](2); - items[0] = MediaItem({ imageURI: "img1", contentURI: "content1" }); - items[1] = MediaItem({ imageURI: "img2", contentURI: "content2" }); - - vm.prank(founder); - mediaMetadata.addMediaItems(items); - - vm.prank(address(token)); - bool response = mediaMetadata.onMinted(3); - assertFalse(response); - - MediaItem[] memory newItems = new MediaItem[](2); - newItems[0] = MediaItem({ imageURI: "img3", contentURI: "content3" }); - newItems[1] = MediaItem({ imageURI: "img4", contentURI: "content4" }); - - vm.prank(founder); - mediaMetadata.addMediaItems(newItems); - - vm.prank(address(token)); - response = mediaMetadata.onMinted(3); - assertTrue(response); - } - - function test_UpdateMetadata() public { - assertEq(mediaMetadata.description(), "This is a mock token"); - assertEq(mediaMetadata.projectURI(), "https://nouns.build"); - - vm.startPrank(founder); - mediaMetadata.updateDescription("new description"); - mediaMetadata.updateProjectURI("https://nouns.build/about"); - vm.stopPrank(); - - assertEq(mediaMetadata.description(), "new description"); - assertEq(mediaMetadata.projectURI(), "https://nouns.build/about"); - } - - function test_ContractURI() public { - /** - base64 -d - eyJuYW1lIjogIk1vY2sgVG9rZW4iLCJkZXNjcmlwdGlvbiI6ICJUaGlzIGlzIGEgbW9jayB0b2tlbiIsImltYWdlIjogImlwZnM6Ly9RbWV3N1RkeUduajZZUlVqUVI2OHNVSk4zMjM5TVlYUkQ4dXhvd3hGNnJHSzhqIiwiZXh0ZXJuYWxfdXJsIjogImh0dHBzOi8vbm91bnMuYnVpbGQifQ== - {"name": "Mock Token","description": "This is a mock token","image": "ipfs://Qmew7TdyGnj6YRUjQR68sUJN3239MYXRD8uxowxF6rGK8j","external_url": "https://nouns.build"} - */ - assertEq( - token.contractURI(), - "data:application/json;base64,eyJuYW1lIjogIk1vY2sgVG9rZW4iLCJkZXNjcmlwdGlvbiI6ICJUaGlzIGlzIGEgbW9jayB0b2tlbiIsImltYWdlIjogImlwZnM6Ly9RbWV3N1RkeUduajZZUlVqUVI2OHNVSk4zMjM5TVlYUkQ4dXhvd3hGNnJHSzhqIiwiZXh0ZXJuYWxfdXJsIjogImh0dHBzOi8vbm91bnMuYnVpbGQifQ==" - ); - } - - function test_TokenURI() public { - MediaItem[] memory items = new MediaItem[](3); - items[0] = MediaItem({ imageURI: "img0", contentURI: "content0" }); - items[1] = MediaItem({ imageURI: "img1", contentURI: "content1" }); - items[2] = MediaItem({ imageURI: "img2", contentURI: "content2" }); - - vm.prank(founder); - mediaMetadata.addMediaItems(items); - - vm.prank(address(auction)); - token.mint(); - - /** - TokenURI Result Pretty JSON: - { - "name": "Mock Token #0", - "description": "This is a mock token", - "image": "img0", - "animation_url": "content0" - } - */ - - string memory json = Base64URIDecoder.decodeURI("data:application/json;base64,", token.tokenURI(0)); - - assertEq(json, '{"name": "Mock Token #0","description": "This is a mock token","image": "img0","animation_url": "content0"}'); - } -} diff --git a/test/MerkleReserveMinter.t.sol b/test/MerkleReserveMinter.t.sol index 961c67d..c8e41c6 100644 --- a/test/MerkleReserveMinter.t.sol +++ b/test/MerkleReserveMinter.t.sol @@ -28,9 +28,7 @@ contract MerkleReserveMinterTest is NounsBuilderTest { setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); } @@ -38,21 +36,27 @@ contract MerkleReserveMinterTest is NounsBuilderTest { function deployAltMockAndSetMinter( uint256 _reservedUntilTokenId, address _minter, - bytes memory _minterData + MerkleReserveMinter.MerkleMinterSettings memory _minterData ) internal virtual { setMockFounderParams(); - setMockTokenParamsWithReserveAndMinter(_reservedUntilTokenId, _minter, _minterData); + setMockTokenParamsWithReserve(_reservedUntilTokenId); setMockAuctionParams(); setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); + + TokenTypesV2.MinterParams[] memory minters = new TokenTypesV2.MinterParams[](1); + minters[0] = TokenTypesV2.MinterParams({ minter: _minter, allowed: true }); + + vm.startPrank(token.owner()); + token.updateMinters(minters); + minter.setMintSettings(address(token), _minterData); + vm.stopPrank(); } function test_MintFlow() public { @@ -103,7 +107,7 @@ contract MerkleReserveMinterTest is NounsBuilderTest { merkleRoot: root }); - deployAltMockAndSetMinter(20, address(minter), abi.encode(settings)); + deployAltMockAndSetMinter(20, address(minter), settings); (uint64 mintStart, uint64 mintEnd, uint64 pricePerToken, bytes32 merkleRoot) = minter.allowedMerkles(address(token)); assertEq(mintStart, settings.mintStart); diff --git a/test/MetadataRenderer.t.sol b/test/MetadataRenderer.t.sol index 7b463c8..ad638b9 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 { PropertyMetadataTypesV1 } from "../src/metadata/property/types/PropertyMetadataTypesV1.sol"; -import { PropertyMetadataTypesV2 } from "../src/metadata/property/types/PropertyMetadataTypesV2.sol"; +import { MetadataRendererTypesV1 } from "../src/token/metadata/types/MetadataRendererTypesV1.sol"; +import { MetadataRendererTypesV2 } from "../src/token/metadata/types/MetadataRendererTypesV2.sol"; import { Base64URIDecoder } from "./utils/Base64URIDecoder.sol"; import "forge-std/console2.sol"; -contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { +contract PropertyMetadataTest is NounsBuilderTest, MetadataRendererTypesV1 { function setUp() public virtual override { super.setUp(); @@ -94,7 +94,7 @@ contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { function testRevert_CannotExceedMaxProperties() public { string[] memory names = new string[](16); - PropertyMetadataTypesV1.ItemParam[] memory items = new PropertyMetadataTypesV1.ItemParam[](16); + MetadataRendererTypesV1.ItemParam[] memory items = new MetadataRendererTypesV1.ItemParam[](16); for (uint256 j; j < 16; j++) { names[j] = "aaa"; // Add random properties @@ -104,7 +104,7 @@ contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { items[j].isNewProperty = true; } - PropertyMetadataTypesV1.IPFSGroup memory group = PropertyMetadataTypesV1.IPFSGroup("aaa", "aaa"); + MetadataRendererTypesV1.IPFSGroup memory group = MetadataRendererTypesV1.IPFSGroup("aaa", "aaa"); vm.prank(founder); vm.expectRevert(abi.encodeWithSignature("TOO_MANY_PROPERTIES()")); @@ -187,9 +187,9 @@ contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { vm.prank(address(auction)); token.mint(); - PropertyMetadataTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](2); - additionalTokenProperties[0] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); - additionalTokenProperties[1] = PropertyMetadataTypesV2.AdditionalTokenProperty({ + MetadataRendererTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](2); + additionalTokenProperties[0] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); + additionalTokenProperties[1] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "participationAgreement", value: "This is a JSON quoted participation agreement.", quote: true @@ -238,9 +238,9 @@ contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { vm.prank(address(auction)); token.mint(); - PropertyMetadataTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](2); - additionalTokenProperties[0] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); - additionalTokenProperties[1] = PropertyMetadataTypesV2.AdditionalTokenProperty({ + MetadataRendererTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](2); + additionalTokenProperties[0] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); + additionalTokenProperties[1] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "participationAgreement", value: "This is a JSON quoted participation agreement.", quote: true @@ -250,7 +250,7 @@ contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { string memory withAdditionalTokenProperties = token.tokenURI(0); - PropertyMetadataTypesV2.AdditionalTokenProperty[] memory clearedTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](0); + MetadataRendererTypesV2.AdditionalTokenProperty[] memory clearedTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](0); vm.prank(founder); metadataRenderer.setAdditionalTokenProperties(clearedTokenProperties); @@ -282,9 +282,9 @@ contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { vm.prank(address(auction)); token.mint(); - PropertyMetadataTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](2); - additionalTokenProperties[0] = PropertyMetadataTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); - additionalTokenProperties[1] = PropertyMetadataTypesV2.AdditionalTokenProperty({ + MetadataRendererTypesV2.AdditionalTokenProperty[] memory additionalTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](2); + additionalTokenProperties[0] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "testing", value: "HELLO", quote: true }); + additionalTokenProperties[1] = MetadataRendererTypesV2.AdditionalTokenProperty({ key: "participationAgreement", value: "This is a JSON quoted participation agreement.", quote: true @@ -294,7 +294,7 @@ contract PropertyMetadataTest is NounsBuilderTest, PropertyMetadataTypesV1 { string memory withAdditionalTokenProperties = token.tokenURI(0); - PropertyMetadataTypesV2.AdditionalTokenProperty[] memory clearedTokenProperties = new PropertyMetadataTypesV2.AdditionalTokenProperty[](0); + MetadataRendererTypesV2.AdditionalTokenProperty[] memory clearedTokenProperties = new MetadataRendererTypesV2.AdditionalTokenProperty[](0); vm.prank(founder); metadataRenderer.setAdditionalTokenProperties(clearedTokenProperties); diff --git a/test/PartialMirrorToken.t.sol b/test/PartialMirrorToken.t.sol index d0fe5e5..679b700 100644 --- a/test/PartialMirrorToken.t.sol +++ b/test/PartialMirrorToken.t.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.16; import { NounsBuilderTest } from "./utils/NounsBuilderTest.sol"; import { PartialMirrorToken } from "../src/token/partial-mirror/PartialMirrorToken.sol"; import { IPartialMirrorToken } from "../src/token/partial-mirror/IPartialMirrorToken.sol"; -import { MockMinter } from "./utils/mocks/MockMinter.sol"; import { MockERC721 } from "./utils/mocks/MockERC721.sol"; import { IManager, Manager } from "../src/manager/Manager.sol"; @@ -35,67 +34,9 @@ contract PartialMirrorTokenTest is NounsBuilderTest, TokenTypesV1 { setMockGovParams(); - setImplementationAddresses(); + setMockMirrorTokenParams(_reservedUntilTokenId, address(tokenToMirror)); - mirrorTokenImpl = address(new PartialMirrorToken(address(manager))); - - vm.startPrank(zoraDAO); - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_TOKEN(), mirrorTokenImpl); - vm.stopPrank(); - - IPartialMirrorToken.TokenParams memory mirrorParams = IPartialMirrorToken.TokenParams({ - name: "Mock Token", - symbol: "MOCK", - reservedUntilTokenId: _reservedUntilTokenId, - tokenToMirror: address(tokenToMirror), - initialMinter: address(0), - initialMinterData: new bytes(0) - }); - - implAddresses[manager.IMPLEMENTATION_TYPE_TOKEN()] = mirrorTokenImpl; - implData[manager.IMPLEMENTATION_TYPE_TOKEN()] = abi.encode(mirrorParams); - - deploy(foundersArr, implAddresses, implData); - - mirrorToken = PartialMirrorToken(address(token)); - - setMockMetadata(); - } - - function deployAltMockAndSetMinter( - uint256 _reservedUntilTokenId, - address _minter, - bytes memory _minterData - ) internal virtual { - setMockFounderParams(); - - setMockTokenParamsWithReserveAndMinter(_reservedUntilTokenId, _minter, _minterData); - - setMockAuctionParams(); - - setMockGovParams(); - - setImplementationAddresses(); - - mirrorTokenImpl = address(new PartialMirrorToken(address(manager))); - - vm.startPrank(zoraDAO); - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_TOKEN(), mirrorTokenImpl); - vm.stopPrank(); - - IPartialMirrorToken.TokenParams memory mirrorParams = IPartialMirrorToken.TokenParams({ - name: "Mock Token", - symbol: "MOCK", - reservedUntilTokenId: _reservedUntilTokenId, - tokenToMirror: address(tokenToMirror), - initialMinter: _minter, - initialMinterData: _minterData - }); - - implAddresses[manager.IMPLEMENTATION_TYPE_TOKEN()] = mirrorTokenImpl; - implData[manager.IMPLEMENTATION_TYPE_TOKEN()] = abi.encode(mirrorParams); - - deploy(foundersArr, implAddresses, implData); + deployWithMirror(foundersArr, mirrorTokenParams, auctionParams, govParams); mirrorToken = PartialMirrorToken(address(token)); @@ -114,14 +55,6 @@ contract PartialMirrorTokenTest is NounsBuilderTest, TokenTypesV1 { assertEq(token.totalSupply(), 0); } - function test_MockTokenWithMinter() public { - MockMinter minter = new MockMinter(); - deployAltMockAndSetMinter(20, address(minter), hex"112233"); - - assertEq(token.minter(address(minter)), true); - assertEq(minter.data(address(token)), hex"112233"); - } - /// Test that the percentages for founders all ends up as expected function test_FounderShareAllocationFuzz( uint256 f1Percentage, diff --git a/test/Token.t.sol b/test/Token.t.sol index 73baea6..c94d640 100644 --- a/test/Token.t.sol +++ b/test/Token.t.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.16; import { NounsBuilderTest } from "./utils/NounsBuilderTest.sol"; -import { MockMinter } from "./utils/mocks/MockMinter.sol"; import { IManager, Manager } from "../src/manager/Manager.sol"; import { IToken, Token } from "../src/token/default/Token.sol"; @@ -36,29 +35,7 @@ contract TokenTest is NounsBuilderTest, TokenTypesV1 { setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); - - setMockMetadata(); - } - - function deployAltMockAndSetMinter( - uint256 _reservedUntilTokenId, - address _minter, - bytes memory _minterData - ) internal virtual { - setMockFounderParams(); - - setMockTokenParamsWithReserveAndMinter(_reservedUntilTokenId, _minter, _minterData); - - setMockAuctionParams(); - - setMockGovParams(); - - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); } @@ -75,22 +52,6 @@ contract TokenTest is NounsBuilderTest, TokenTypesV1 { assertEq(token.totalSupply(), 0); } - function test_MockTokenWithMinter() public { - MockMinter minter = new MockMinter(); - deployAltMockAndSetMinter(20, address(minter), new bytes(0)); - - assertEq(token.minter(address(minter)), true); - assertEq(minter.data(address(token)), new bytes(0)); - } - - function test_MockTokenWithMinterAndData() public { - MockMinter minter = new MockMinter(); - deployAltMockAndSetMinter(20, address(minter), hex"112233"); - - assertEq(token.minter(address(minter)), true); - assertEq(minter.data(address(token)), hex"112233"); - } - /// Test that the percentages for founders all ends up as expected function test_FounderShareAllocationFuzz( uint256 f1Percentage, diff --git a/test/forking/TestUpdateMinters.t.sol b/test/forking/TestUpdateMinters.t.sol index e0c5990..5dbe4cf 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/default/Token.sol"; -import { PropertyMetadata } from "../../src/metadata/property/PropertyMetadata.sol"; +import { MetadataRenderer } from "../../src/token/metadata/MetadataRenderer.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)); - PropertyMetadata internal immutable metadata = PropertyMetadata(0x963ac521C595D3D1BE72C1Eb057f24D4D42CB70b); + MetadataRenderer internal immutable metadata = MetadataRenderer(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 e08c744..23c401e 100644 --- a/test/utils/NounsBuilderTest.sol +++ b/test/utils/NounsBuilderTest.sol @@ -5,11 +5,13 @@ import { Test } from "forge-std/Test.sol"; import { IManager, Manager } from "../../src/manager/Manager.sol"; import { IToken, Token } from "../../src/token/default/Token.sol"; -import { IBaseMetadata, IPropertyMetadata, PropertyMetadata } from "../../src/metadata/property/PropertyMetadata.sol"; +import { PartialMirrorToken } from "../../src/token/partial-mirror/PartialMirrorToken.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 { PropertyMetadataTypesV1 } from "../../src/metadata/property/types/PropertyMetadataTypesV1.sol"; +import { MetadataRenderer } from "../../src/token/metadata/MetadataRenderer.sol"; +import { MetadataRendererTypesV1 } from "../../src/token/metadata/types/MetadataRendererTypesV1.sol"; import { ERC1967Proxy } from "../../src/lib/proxy/ERC1967Proxy.sol"; import { MockERC721 } from "../utils/mocks/MockERC721.sol"; @@ -28,6 +30,7 @@ contract NounsBuilderTest is Test { address internal managerImpl0; address internal managerImpl; address internal tokenImpl; + address internal mirrorImpl; address internal metadataRendererImpl; address internal auctionImpl; address internal treasuryImpl; @@ -35,7 +38,6 @@ contract NounsBuilderTest is Test { address internal nounsDAO; address internal zoraDAO; - address internal builderDAO; address internal founder; address internal founder2; address internal weth; @@ -51,10 +53,9 @@ contract NounsBuilderTest is Test { nounsDAO = vm.addr(0xA11CE); zoraDAO = vm.addr(0xB0B); - builderDAO = vm.addr(0xCAB); - founder = vm.addr(0xDAD); - founder2 = vm.addr(0xE1AD); + founder = vm.addr(0xCAB); + founder2 = vm.addr(0xDAD); vm.label(zoraDAO, "ZORA_DAO"); vm.label(nounsDAO, "NOUNS_DAO"); @@ -62,27 +63,21 @@ contract NounsBuilderTest is Test { vm.label(founder, "FOUNDER"); vm.label(founder2, "FOUNDER_2"); - managerImpl0 = address(new Manager()); + managerImpl0 = address(new Manager(address(0), address(0), address(0), address(0), address(0), address(0))); manager = Manager(address(new ERC1967Proxy(managerImpl0, abi.encodeWithSignature("initialize(address)", zoraDAO)))); - - rewards = new ProtocolRewards(address(manager), builderDAO); + rewards = new ProtocolRewards(address(manager), zoraDAO); tokenImpl = address(new Token(address(manager))); - metadataRendererImpl = address(new PropertyMetadata(address(manager))); + mirrorImpl = address(new PartialMirrorToken(address(manager))); + metadataRendererImpl = address(new MetadataRenderer(address(manager))); auctionImpl = address(new Auction(address(manager), address(rewards), weth)); treasuryImpl = address(new Treasury(address(manager))); governorImpl = address(new Governor(address(manager))); - managerImpl = address(new Manager()); + managerImpl = address(new Manager(tokenImpl, mirrorImpl, metadataRendererImpl, auctionImpl, treasuryImpl, governorImpl)); - vm.startPrank(zoraDAO); + vm.prank(zoraDAO); manager.upgradeTo(managerImpl); - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_TOKEN(), tokenImpl); - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_METADATA(), metadataRendererImpl); - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_AUCTION(), auctionImpl); - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_TREASURY(), treasuryImpl); - manager.registerImplementation(manager.IMPLEMENTATION_TYPE_GOVERNOR(), governorImpl); - vm.stopPrank(); } /// /// @@ -90,14 +85,10 @@ contract NounsBuilderTest is Test { /// /// IManager.FounderParams[] internal foundersArr; - IToken.TokenParams internal tokenParams; - IPropertyMetadata.PropertyMetadataParams internal metadataParams; - IAuction.AuctionParams internal auctionParams; - IGovernor.GovParams internal govParams; - ITreasury.TreasuryParams internal treasuryParams; - - address[] internal implAddresses; - bytes[] internal implData; + IManager.TokenParams internal tokenParams; + IManager.MirrorTokenParams internal mirrorTokenParams; + IManager.AuctionParams internal auctionParams; + IManager.GovParams internal govParams; function setMockFounderParams() internal virtual { address[] memory wallets = new address[](2); @@ -142,9 +133,7 @@ contract NounsBuilderTest is Test { "ipfs://Qmew7TdyGnj6YRUjQR68sUJN3239MYXRD8uxowxF6rGK8j", "https://nouns.build", "http://localhost:5000/render", - 0, - address(0), - new bytes(0) + 0 ); } @@ -156,18 +145,12 @@ contract NounsBuilderTest is Test { "ipfs://Qmew7TdyGnj6YRUjQR68sUJN3239MYXRD8uxowxF6rGK8j", "https://nouns.build", "http://localhost:5000/render", - _reservedUntilTokenId, - address(0), - new bytes(0) + _reservedUntilTokenId ); } - function setMockTokenParamsWithReserveAndMinter( - uint256 _reservedUntilTokenId, - address minter, - bytes memory minterData - ) internal virtual { - setTokenParams( + function setMockMirrorTokenParams(uint256 _reservedUntilTokenId, address _tokenToMirror) internal virtual { + setMirrorTokenParams( "Mock Token", "MOCK", "This is a mock token", @@ -175,12 +158,25 @@ contract NounsBuilderTest is Test { "https://nouns.build", "http://localhost:5000/render", _reservedUntilTokenId, - minter, - minterData + _tokenToMirror ); } function setTokenParams( + string memory _name, + string memory _symbol, + string memory _description, + string memory _contractImage, + string memory _contractURI, + string memory _rendererBase, + uint256 _reservedUntilTokenId + ) internal virtual { + bytes memory initStrings = abi.encode(_name, _symbol, _description, _contractImage, _contractURI, _rendererBase); + + tokenParams = IManager.TokenParams({ initStrings: initStrings, reservedUntilTokenId: _reservedUntilTokenId }); + } + + function setMirrorTokenParams( string memory _name, string memory _symbol, string memory _description, @@ -188,28 +184,15 @@ contract NounsBuilderTest is Test { string memory _contractURI, string memory _rendererBase, uint256 _reservedUntilTokenId, - address _initialMinter, - bytes memory _initialMinterData + address _tokenToMirror ) internal virtual { - tokenParams = IToken.TokenParams({ - name: _name, - symbol: _symbol, + bytes memory initStrings = abi.encode(_name, _symbol, _description, _contractImage, _contractURI, _rendererBase); + + mirrorTokenParams = IManager.MirrorTokenParams({ + initStrings: initStrings, reservedUntilTokenId: _reservedUntilTokenId, - initialMinter: _initialMinter, - initialMinterData: _initialMinterData - }); - metadataParams = IPropertyMetadata.PropertyMetadataParams({ - description: _description, - contractImage: _contractImage, - projectURI: _contractURI, - rendererBase: _rendererBase + tokenToMirror: _tokenToMirror }); - - implData.push(); - implData[manager.IMPLEMENTATION_TYPE_TOKEN()] = abi.encode(tokenParams); - - implData.push(); - implData[manager.IMPLEMENTATION_TYPE_METADATA()] = abi.encode(metadataParams); } function setMockAuctionParams() internal virtual { @@ -222,14 +205,12 @@ contract NounsBuilderTest is Test { address _founderRewardRecipent, uint256 _founderRewardBPS ) internal virtual { - implData.push(); - auctionParams = IAuction.AuctionParams({ + auctionParams = IManager.AuctionParams({ reservePrice: _reservePrice, duration: _duration, founderRewardRecipent: _founderRewardRecipent, founderRewardBPS: _founderRewardBPS }); - implData[manager.IMPLEMENTATION_TYPE_AUCTION()] = abi.encode(auctionParams); } function setMockGovParams() internal virtual { @@ -244,58 +225,36 @@ contract NounsBuilderTest is Test { uint256 _quorumThresholdBps, address _vetoer ) internal virtual { - implData.push(); - treasuryParams = ITreasury.TreasuryParams({ timelockDelay: _timelockDelay }); - implData[manager.IMPLEMENTATION_TYPE_TREASURY()] = abi.encode(treasuryParams); - - implData.push(); - govParams = IGovernor.GovParams({ + govParams = IManager.GovParams({ + timelockDelay: _timelockDelay, votingDelay: _votingDelay, votingPeriod: _votingPeriod, proposalThresholdBps: _proposalThresholdBps, quorumThresholdBps: _quorumThresholdBps, vetoer: _vetoer }); - implData[manager.IMPLEMENTATION_TYPE_GOVERNOR()] = abi.encode(govParams); } function setMockMetadata() internal { string[] memory names = new string[](1); names[0] = "testing"; - 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.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.IPFSGroup memory ipfsGroup = PropertyMetadataTypesV1.IPFSGroup({ baseUri: "BASE_URI", extension: "EXTENSION" }); + MetadataRendererTypesV1.IPFSGroup memory ipfsGroup = MetadataRendererTypesV1.IPFSGroup({ baseUri: "BASE_URI", extension: "EXTENSION" }); vm.prank(metadataRenderer.owner()); metadataRenderer.addProperties(names, items, ipfsGroup); } - function setImplementationAddresses() internal { - implAddresses.push(); - implAddresses[manager.IMPLEMENTATION_TYPE_TOKEN()] = tokenImpl; - - implAddresses.push(); - implAddresses[manager.IMPLEMENTATION_TYPE_METADATA()] = metadataRendererImpl; - - implAddresses.push(); - implAddresses[manager.IMPLEMENTATION_TYPE_AUCTION()] = auctionImpl; - - implAddresses.push(); - implAddresses[manager.IMPLEMENTATION_TYPE_TREASURY()] = treasuryImpl; - - implAddresses.push(); - implAddresses[manager.IMPLEMENTATION_TYPE_GOVERNOR()] = governorImpl; - } - /// /// /// DAO DEPLOY UTILS /// /// /// Token internal token; - PropertyMetadata internal metadataRenderer; + MetadataRenderer internal metadataRenderer; Auction internal auction; Treasury internal treasury; Governor internal governor; @@ -309,9 +268,7 @@ contract NounsBuilderTest is Test { setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); } @@ -329,9 +286,7 @@ contract NounsBuilderTest is Test { setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); } @@ -346,15 +301,13 @@ contract NounsBuilderTest is Test { ) internal { setMockFounderParams(); - setTokenParams(_name, _symbol, _description, _contractImage, _projectURI, _rendererBase, 0, address(0), new bytes(0)); + setTokenParams(_name, _symbol, _description, _contractImage, _projectURI, _rendererBase, 0); setMockAuctionParams(); setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); setMockMetadata(); } @@ -368,24 +321,50 @@ contract NounsBuilderTest is Test { setMockGovParams(); - setImplementationAddresses(); - - deploy(foundersArr, implAddresses, implData); + deploy(foundersArr, tokenParams, auctionParams, govParams); } function deploy( IManager.FounderParams[] memory _founderParams, - address[] memory _implAddresses, - bytes[] memory _implData + IManager.TokenParams memory _tokenParams, + IManager.AuctionParams memory _auctionParams, + IManager.GovParams memory _govParams ) internal virtual { (address _token, address _metadata, address _auction, address _treasury, address _governor) = manager.deploy( _founderParams, - _implAddresses, - _implData + _tokenParams, + _auctionParams, + _govParams + ); + + token = Token(_token); + metadataRenderer = MetadataRenderer(_metadata); + auction = Auction(_auction); + treasury = Treasury(payable(_treasury)); + governor = Governor(_governor); + + vm.label(address(token), "TOKEN"); + vm.label(address(metadataRenderer), "METADATA_RENDERER"); + vm.label(address(auction), "AUCTION"); + vm.label(address(treasury), "TREASURY"); + vm.label(address(governor), "GOVERNOR"); + } + + function deployWithMirror( + IManager.FounderParams[] memory _founderParams, + IManager.MirrorTokenParams memory _mirrorTokenParams, + IManager.AuctionParams memory _auctionParams, + IManager.GovParams memory _govParams + ) internal virtual { + (address _token, address _metadata, address _auction, address _treasury, address _governor) = manager.deployWithMirror( + _founderParams, + _mirrorTokenParams, + _auctionParams, + _govParams ); token = Token(_token); - metadataRenderer = PropertyMetadata(_metadata); + metadataRenderer = MetadataRenderer(_metadata); auction = Auction(_auction); treasury = Treasury(payable(_treasury)); governor = Governor(_governor); diff --git a/test/utils/mocks/MockMinter.sol b/test/utils/mocks/MockMinter.sol deleted file mode 100644 index c707dc7..0000000 --- a/test/utils/mocks/MockMinter.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -import { IMintStrategy } from "../../../src/minters/interfaces/IMintStrategy.sol"; - -contract MockMinter is IMintStrategy { - mapping(address => bytes) public data; - - function setMintSettings(bytes calldata _data) external override { - data[msg.sender] = _data; - } -}