From a7f3cc5efd359cf411443c205db864185d690866 Mon Sep 17 00:00:00 2001 From: neokry Date: Tue, 18 Jul 2023 14:51:58 +0800 Subject: [PATCH] Update manager to support multiple implementation contracts --- script/DeployContracts.s.sol | 6 +- script/DeployMetadataUpgrade.s.sol | 4 +- script/DeployTokenUpgrade.s.sol | 4 +- script/DeployVersion1_1.s.sol | 6 +- src/auction/Auction.sol | 19 +- src/auction/IAuction.sol | 20 +- src/governance/governor/Governor.sol | 62 ++---- src/governance/governor/IGovernor.sol | 49 ++--- src/governance/treasury/ITreasury.sol | 12 +- src/governance/treasury/Treasury.sol | 35 +--- src/manager/IManager.sol | 94 +++------ src/manager/Manager.sol | 188 ++++++++---------- src/manager/storage/ManagerStorageV2.sol | 11 + src/token/IToken.sol | 13 +- src/token/Token.sol | 8 +- src/token/metadata/MetadataRenderer.sol | 40 ++-- .../metadata/interfaces/IBaseMetadata.sol | 19 +- test/Auction.t.sol | 8 +- test/Gov.t.sol | 46 ++--- test/Manager.t.sol | 18 +- test/forking/TestBid.t.sol | 3 + test/utils/NounsBuilderTest.sol | 110 ++++++---- 22 files changed, 348 insertions(+), 427 deletions(-) create mode 100644 src/manager/storage/ManagerStorageV2.sol diff --git a/script/DeployContracts.s.sol b/script/DeployContracts.s.sol index bfedeed..224501d 100644 --- a/script/DeployContracts.s.sol +++ b/script/DeployContracts.s.sol @@ -38,7 +38,7 @@ contract DeployContracts is Script { vm.startBroadcast(deployerAddress); // Deploy root manager implementation + proxy - address managerImpl0 = address(new Manager(address(0), address(0), address(0), address(0), address(0))); + address managerImpl0 = address(new Manager()); Manager manager = Manager(address(new ERC1967Proxy(managerImpl0, abi.encodeWithSignature("initialize(address)", owner)))); @@ -57,7 +57,7 @@ contract DeployContracts is Script { // Deploy governor implementation address governorImpl = address(new Governor(address(manager))); - address managerImpl = address(new Manager(tokenImpl, metadataRendererImpl, auctionImpl, treasuryImpl, governorImpl)); + address managerImpl = address(new Manager()); // vm.prank(owner); // manager.upgradeTo(managerImpl); @@ -104,7 +104,7 @@ contract DeployContracts is Script { 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 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); diff --git a/script/DeployMetadataUpgrade.s.sol b/script/DeployMetadataUpgrade.s.sol index 0a536b0..aa48421 100644 --- a/script/DeployMetadataUpgrade.s.sol +++ b/script/DeployMetadataUpgrade.s.sol @@ -54,7 +54,7 @@ contract DeployMetadataUpgrade is Script { // Deploy metadata renderer implementation address metadataRendererImpl = address(new MetadataRenderer(managerProxy)); - address managerImpl = address(new Manager(tokenImpl, metadataRendererImpl, auctionImpl, treasuryImpl, governorImpl)); + address managerImpl = address(new Manager()); console2.log("MR"); console2.log(metadataRendererImpl); @@ -77,7 +77,7 @@ contract DeployMetadataUpgrade is Script { 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 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); diff --git a/script/DeployTokenUpgrade.s.sol b/script/DeployTokenUpgrade.s.sol index 128c469..c89f99c 100644 --- a/script/DeployTokenUpgrade.s.sol +++ b/script/DeployTokenUpgrade.s.sol @@ -65,7 +65,7 @@ contract DeployTokenUpgrade is Script { // Deploy token upgrade implementation address tokenUpgradeImpl = address(new Token(managerProxy)); - address managerImpl = address(new Manager(tokenUpgradeImpl, metadataImpl, auctionImpl, treasuryImpl, governorImpl)); + address managerImpl = address(new Manager()); console2.log("TU"); console2.log(tokenUpgradeImpl); @@ -88,7 +88,7 @@ contract DeployTokenUpgrade is Script { 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 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); diff --git a/script/DeployVersion1_1.s.sol b/script/DeployVersion1_1.s.sol index 77e6e39..115d19b 100644 --- a/script/DeployVersion1_1.s.sol +++ b/script/DeployVersion1_1.s.sol @@ -60,9 +60,7 @@ contract DeployVersion1_1 is Script { // Deploy metadata upgrade implementation address metadataUpgradeImpl = address(new MetadataRenderer(managerProxy)); - address managerImpl = address( - new Manager(tokenUpgradeImpl, metadataUpgradeImpl, auctionUpgradeImpl, treasuryUpgradeImpl, governorUpgradeImpl) - ); + address managerImpl = address(new Manager()); vm.stopBroadcast(); @@ -79,7 +77,7 @@ contract DeployVersion1_1 is Script { 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 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); diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index ace9137..1dcf249 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -18,7 +18,7 @@ import { VersionedContract } from "../VersionedContract.sol"; /// @title Auction /// @author Rohan Kulkarni /// @notice A DAO's auction house -/// @custom:repo github.com/ourzora/nouns-protocol +/// @custom:repo github.com/ourzora/nouns-protocol /// Modified from: /// - NounsAuctionHouse.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license. /// - Zora V3 ReserveAuctionCoreEth module commit 795aeca - licensed under the GPL-3.0 license. @@ -58,15 +58,8 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, /// @param _token The ERC-721 token address /// @param _founder The founder responsible for starting the first auction /// @param _treasury The treasury address where ETH will be sent - /// @param _duration The duration of each auction - /// @param _reservePrice The reserve price of each auction - function initialize( - address _token, - address _founder, - address _treasury, - uint256 _duration, - uint256 _reservePrice - ) external initializer { + /// @param _data The encoded auction settings + function initialize(address _token, address _founder, address _treasury, bytes calldata _data) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); @@ -82,9 +75,11 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, // Store DAO's ERC-721 token token = Token(_token); + AuctionParams memory params = abi.decode(_data, (AuctionParams)); + // Store the auction house settings - settings.duration = SafeCast.toUint40(_duration); - settings.reservePrice = _reservePrice; + settings.duration = SafeCast.toUint40(params.duration); + settings.reservePrice = params.reservePrice; settings.treasury = _treasury; settings.timeBuffer = INITIAL_TIME_BUFFER; settings.minBidIncrement = INITIAL_MIN_BID_INCREMENT_PERCENT; diff --git a/src/auction/IAuction.sol b/src/auction/IAuction.sol index 72ea273..f501d20 100644 --- a/src/auction/IAuction.sol +++ b/src/auction/IAuction.sol @@ -89,6 +89,15 @@ interface IAuction is IUUPS, IOwnable, IPausable { /// @dev Thrown if the auction creation failed error AUCTION_CREATE_FAILED_TO_LAUNCH(); + /// /// + /// STRUCTS /// + /// /// + + struct AuctionParams { + uint256 duration; + uint256 reservePrice; + } + /// /// /// FUNCTIONS /// /// /// @@ -97,15 +106,8 @@ interface IAuction is IUUPS, IOwnable, IPausable { /// @param token The ERC-721 token address /// @param founder The founder responsible for starting the first auction /// @param treasury The treasury address where ETH will be sent - /// @param duration The duration of each auction - /// @param reservePrice The reserve price of each auction - function initialize( - address token, - address founder, - address treasury, - uint256 duration, - uint256 reservePrice - ) external; + /// @param data The encoded auction initialization data + function initialize(address token, address founder, address treasury, bytes calldata data) external; /// @notice Creates a bid for the current token /// @param tokenId The ERC-721 token id diff --git a/src/governance/governor/Governor.sol b/src/governance/governor/Governor.sol index 2fa4623..6558aab 100644 --- a/src/governance/governor/Governor.sol +++ b/src/governance/governor/Governor.sol @@ -75,20 +75,8 @@ 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 _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, - address _vetoer, - uint256 _votingDelay, - uint256 _votingPeriod, - uint256 _proposalThresholdBps, - uint256 _quorumThresholdBps - ) external initializer { + /// @param _data The encoded governor parameters + function initialize(address _treasury, address _token, bytes calldata _data) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); @@ -96,24 +84,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 (_vetoer != address(0)) settings.vetoer = _vetoer; + if (params.vetoer != address(0)) settings.vetoer = params.vetoer; // Ensure the specified governance settings are valid - if (_proposalThresholdBps < MIN_PROPOSAL_THRESHOLD_BPS || _proposalThresholdBps > MAX_PROPOSAL_THRESHOLD_BPS) + if (params.proposalThresholdBps < MIN_PROPOSAL_THRESHOLD_BPS || params.proposalThresholdBps > MAX_PROPOSAL_THRESHOLD_BPS) revert INVALID_PROPOSAL_THRESHOLD_BPS(); - 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(); + if (params.quorumThresholdBps < MIN_QUORUM_THRESHOLD_BPS || params.quorumThresholdBps > MAX_QUORUM_THRESHOLD_BPS) + revert INVALID_QUORUM_THRESHOLD_BPS(); + if (params.proposalThresholdBps >= params.quorumThresholdBps) 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(); // Store the governor settings settings.treasury = Treasury(payable(_treasury)); settings.token = Token(_token); - settings.votingDelay = SafeCast.toUint48(_votingDelay); - settings.votingPeriod = SafeCast.toUint48(_votingPeriod); - settings.proposalThresholdBps = SafeCast.toUint16(_proposalThresholdBps); - settings.quorumThresholdBps = SafeCast.toUint16(_quorumThresholdBps); + settings.votingDelay = SafeCast.toUint48(params.votingDelay); + settings.votingPeriod = SafeCast.toUint48(params.votingPeriod); + settings.proposalThresholdBps = SafeCast.toUint16(params.proposalThresholdBps); + settings.quorumThresholdBps = SafeCast.toUint16(params.quorumThresholdBps); // Initialize EIP-712 support __EIP712_init(string.concat(settings.token.symbol(), " GOV"), "1"); @@ -209,11 +200,7 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos /// @param _proposalId The proposal id /// @param _support The support value (0 = Against, 1 = For, 2 = Abstain) /// @param _reason The vote reason - function castVoteWithReason( - bytes32 _proposalId, - uint256 _support, - string memory _reason - ) external returns (uint256) { + function castVoteWithReason(bytes32 _proposalId, uint256 _support, string memory _reason) external returns (uint256) { return _castVote(_proposalId, msg.sender, _support, _reason); } @@ -265,12 +252,7 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos /// @param _proposalId The proposal id /// @param _voter The voter address /// @param _support The vote choice - function _castVote( - bytes32 _proposalId, - address _voter, - uint256 _support, - string memory _reason - ) internal returns (uint256) { + function _castVote(bytes32 _proposalId, address _voter, uint256 _support, string memory _reason) internal returns (uint256) { // Ensure voting is active if (state(_proposalId) != ProposalState.Active) revert VOTING_NOT_STARTED(); @@ -518,15 +500,7 @@ contract Governor is IGovernor, VersionedContract, UUPS, Ownable, EIP712, Propos /// @notice The vote counts for a proposal /// @param _proposalId The proposal id - function proposalVotes(bytes32 _proposalId) - external - view - returns ( - uint256, - uint256, - uint256 - ) - { + function proposalVotes(bytes32 _proposalId) external view returns (uint256, uint256, uint256) { Proposal memory proposal = proposals[_proposalId]; return (proposal.againstVotes, proposal.forVotes, proposal.abstainVotes); diff --git a/src/governance/governor/IGovernor.sol b/src/governance/governor/IGovernor.sol index 1e8ff06..61e4e54 100644 --- a/src/governance/governor/IGovernor.sol +++ b/src/governance/governor/IGovernor.sol @@ -113,6 +113,24 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { /// @dev Reverts if the caller was not the contract manager error ONLY_MANAGER(); + /// /// + /// STRUCTS /// + /// /// + + /// @notice The governance parameters + /// @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 votingDelay; + uint256 votingPeriod; + uint256 proposalThresholdBps; + uint256 quorumThresholdBps; + address vetoer; + } + /// /// /// FUNCTIONS /// /// /// @@ -120,20 +138,8 @@ 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 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, - address vetoer, - uint256 votingDelay, - uint256 votingPeriod, - uint256 proposalThresholdBps, - uint256 quorumThresholdBps - ) external; + /// @param data The encoded governance parameters + function initialize(address treasury, address token, bytes calldata data) external; /// @notice Creates a proposal /// @param targets The target addresses to call @@ -156,11 +162,7 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { /// @param proposalId The proposal id /// @param support The support value (0 = Against, 1 = For, 2 = Abstain) /// @param reason The vote reason - function castVoteWithReason( - bytes32 proposalId, - uint256 support, - string memory reason - ) external returns (uint256); + function castVoteWithReason(bytes32 proposalId, uint256 support, string memory reason) external returns (uint256); /// @notice Casts a signed vote /// @param voter The voter address @@ -235,14 +237,7 @@ interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 { /// @notice The vote counts for a proposal /// @param proposalId The proposal id - function proposalVotes(bytes32 proposalId) - external - view - returns ( - uint256 againstVotes, - uint256 forVotes, - uint256 abstainVotes - ); + function proposalVotes(bytes32 proposalId) external view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes); /// @notice The timestamp valid to execute a proposal /// @param proposalId The proposal id diff --git a/src/governance/treasury/ITreasury.sol b/src/governance/treasury/ITreasury.sol index 84e84a9..77bceb5 100644 --- a/src/governance/treasury/ITreasury.sol +++ b/src/governance/treasury/ITreasury.sol @@ -54,14 +54,22 @@ interface ITreasury is IUUPS, IOwnable { /// @dev Reverts if the caller was not the contract manager error ONLY_MANAGER(); + /// /// + /// STRUCTS /// + /// /// + + struct TreasuryParams { + uint256 timelockDelay; + } + /// /// /// FUNCTIONS /// /// /// /// @notice Initializes a DAO's treasury /// @param governor The governor address - /// @param timelockDelay The time delay to execute a queued transaction - function initialize(address governor, uint256 timelockDelay) external; + /// @param data The encoded treasury initialization data + function initialize(address governor, bytes calldata data) 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 efdba99..2c27c82 100644 --- a/src/governance/treasury/Treasury.sol +++ b/src/governance/treasury/Treasury.sol @@ -15,7 +15,7 @@ import { VersionedContract } from "../../VersionedContract.sol"; /// @title Treasury /// @author Rohan Kulkarni /// @notice A DAO's treasury and transaction executor -/// @custom:repo github.com/ourzora/nouns-protocol +/// @custom:repo github.com/ourzora/nouns-protocol /// Modified from: /// - OpenZeppelin Contracts v4.7.3 (governance/TimelockController.sol) /// - NounsDAOExecutor.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license. @@ -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 _delay The time delay to execute a queued transaction - function initialize(address _governor, uint256 _delay) external initializer { + /// @param _data The encoded treasury parameters + function initialize(address _governor, bytes calldata _data) external initializer { // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); @@ -60,13 +60,15 @@ 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(_delay); + settings.delay = SafeCast.toUint128(params.timelockDelay); // Set the default grace period settings.gracePeriod = INITIAL_GRACE_PERIOD; - emit DelayUpdated(0, _delay); + emit DelayUpdated(0, params.timelockDelay); } /// /// @@ -228,34 +230,17 @@ contract Treasury is ITreasury, VersionedContract, UUPS, Ownable, ProposalHasher /// /// /// @dev Accepts all ERC-721 transfers - function onERC721Received( - address, - address, - uint256, - bytes memory - ) public pure returns (bytes4) { + function onERC721Received(address, address, uint256, bytes memory) public pure returns (bytes4) { return ERC721TokenReceiver.onERC721Received.selector; } /// @dev Accepts all ERC-1155 single id transfers - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes memory - ) public pure returns (bytes4) { + function onERC1155Received(address, address, uint256, uint256, bytes memory) public pure returns (bytes4) { return ERC1155TokenReceiver.onERC1155Received.selector; } /// @dev Accept all ERC-1155 batch id transfers - function onERC1155BatchReceived( - address, - address, - uint256[] memory, - uint256[] memory, - bytes memory - ) public pure returns (bytes4) { + function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) public pure returns (bytes4) { return ERC1155TokenReceiver.onERC1155BatchReceived.selector; } diff --git a/src/manager/IManager.sol b/src/manager/IManager.sol index 33bbbcc..47f81cc 100644 --- a/src/manager/IManager.sol +++ b/src/manager/IManager.sol @@ -20,6 +20,16 @@ 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 ImplemenetationRemoved(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 @@ -37,6 +47,12 @@ interface IManager is IUUPS, IOwnable { /// @dev Reverts if at least one founder is not provided upon deploy error FOUNDER_REQUIRED(); + /// @dev Reverts if implementation parameters are incorrect length + error INVALID_IMPLEMENTATION_PARAMS(); + + /// @dev Reverts if an implementation type is not valid + error INVALID_IMPLEMENTATION_TYPE(); + /// /// /// STRUCTS /// /// /// @@ -57,88 +73,34 @@ interface IManager is IUUPS, IOwnable { string metadata; string auction; string treasury; - string governor; + string governor; } /// @notice The ERC-721 token parameters - /// @param initStrings The encoded token name, symbol, collection description, collection image uri, renderer base uri - struct TokenParams { - bytes initStrings; - } - - /// @notice The auction parameters - /// @param reservePrice The reserve price of each auction - /// @param duration The duration of each auction - struct AuctionParams { - uint256 reservePrice; - uint256 duration; - } - - /// @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; + /// @param impl The address of the implementation + /// @param data The encoded implementation parameters + struct ImplementationParams { + address impl; + bytes data; } /// /// /// FUNCTIONS /// /// /// - /// @notice The token implementation address - function tokenImpl() external view returns (address); - - /// @notice The metadata renderer implementation address - function metadataImpl() external view returns (address); - - /// @notice The auction house implementation address - function auctionImpl() external view returns (address); - - /// @notice The treasury implementation address - function treasuryImpl() external view returns (address); - - /// @notice The governor implementation address - function governorImpl() external view returns (address); - /// @notice Deploys a DAO with custom token, auction, and governance settings /// @param founderParams The DAO founder(s) - /// @param tokenParams The ERC-721 token settings - /// @param auctionParams The auction settings - /// @param govParams The governance settings + /// @param implAddresses The implementation addresses + /// @param implData The encoded list of implementation data function deploy( FounderParams[] calldata founderParams, - TokenParams calldata tokenParams, - AuctionParams calldata auctionParams, - GovParams calldata govParams - ) - external - returns ( - address token, - address metadataRenderer, - address auction, - address treasury, - address governor - ); + address[] calldata implAddresses, + bytes[] calldata implData + ) external returns (address token, address metadataRenderer, address auction, address treasury, address governor); /// @notice A DAO's remaining contract addresses from its token address /// @param token The ERC-721 token address - function getAddresses(address token) - external - returns ( - address metadataRenderer, - address auction, - address treasury, - address governor - ); + function getAddresses(address token) external returns (address metadataRenderer, address auction, address treasury, address governor); /// @notice If an implementation is registered by the Builder DAO as an optional upgrade /// @param baseImpl The base implementation address diff --git a/src/manager/Manager.sol b/src/manager/Manager.sol index 7093bec..156d17d 100644 --- a/src/manager/Manager.sol +++ b/src/manager/Manager.sol @@ -6,6 +6,7 @@ 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 { IToken } from "../token/IToken.sol"; import { IBaseMetadata } from "../token/metadata/interfaces/IBaseMetadata.sol"; @@ -17,46 +18,24 @@ import { VersionedContract } from "../VersionedContract.sol"; import { IVersionedContract } from "../lib/interfaces/IVersionedContract.sol"; /// @title Manager -/// @author Rohan Kulkarni -/// @custom:repo github.com/ourzora/nouns-protocol +/// @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 { +contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1, ManagerStorageV2 { /// /// - /// IMMUTABLES /// + /// CONSTANTS /// /// /// - /// @notice The token implementation address - address public immutable tokenImpl; + /// @notice The count of implementation types + uint8 public constant IMPLEMENTATION_TYPE_COUNT = 5; - /// @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 _metadataImpl, - address _auctionImpl, - address _treasuryImpl, - address _governorImpl - ) payable initializer { - tokenImpl = _tokenImpl; - metadataImpl = _metadataImpl; - auctionImpl = _auctionImpl; - treasuryImpl = _treasuryImpl; - governorImpl = _governorImpl; - } + // Public constants for implementation types. + // Allows for adding new types later easily 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; /// /// /// INITIALIZER /// @@ -78,24 +57,13 @@ 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 _tokenParams The ERC-721 token settings - /// @param _auctionParams The auction settings - /// @param _govParams The governance settings + /// @param _implAddresses The implementation addresses + /// @param _implData The encoded list of implementation data function deploy( FounderParams[] calldata _founderParams, - TokenParams calldata _tokenParams, - AuctionParams calldata _auctionParams, - GovParams calldata _govParams - ) - external - returns ( - address token, - address metadata, - address auction, - address treasury, - address governor - ) - { + address[] calldata _implAddresses, + bytes[] calldata _implData + ) 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; @@ -103,46 +71,36 @@ 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(); + // Ensure implementation parameters are correct length + if (_implAddresses.length != IMPLEMENTATION_TYPE_COUNT || _implData.length != IMPLEMENTATION_TYPE_COUNT) + revert INVALID_IMPLEMENTATION_PARAMS(); + // Deploy the DAO's ERC-721 governance token - token = address(new ERC1967Proxy(tokenImpl, "")); + token = address(new ERC1967Proxy(_implAddresses[IMPLEMENTATION_TYPE_TOKEN], "")); // 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, "")); + 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], "")); daoAddressesByToken[token] = DAOAddresses({ metadata: metadata, auction: auction, treasury: treasury, governor: governor }); // Initialize each instance with the provided settings IToken(token).initialize({ founders: _founderParams, - initStrings: _tokenParams.initStrings, 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 - }); - 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 + initialOwner: founder, + data: _implData[IMPLEMENTATION_TYPE_TOKEN] }); + IBaseMetadata(metadata).initialize({ token: token, data: _implData[IMPLEMENTATION_TYPE_METADATA] }); + IAuction(auction).initialize({ token: token, founder: 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 }); } @@ -157,16 +115,7 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 /// @return auction Auction deployed address /// @return treasury Treasury deployed address /// @return governor Governor deployed address - function getAddresses(address _token) - public - view - returns ( - address metadata, - address auction, - address treasury, - address governor - ) - { + function getAddresses(address _token) public view returns (address metadata, address auction, address treasury, address governor) { DAOAddresses storage addresses = daoAddressesByToken[_token]; metadata = addresses.metadata; @@ -175,6 +124,37 @@ 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 ImplemenetationRemoved(_implType, _implAddress); + } + /// /// /// DAO UPGRADES /// /// /// @@ -204,37 +184,33 @@ 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. /// @dev Assume `target` is a contract /// @return Contract version if found, empty string if not. - function _safeGetVersion(address target) internal view returns (string memory) { + function _safeGetVersion(address target) internal pure returns (string memory) { try IVersionedContract(target).contractVersion() returns (string memory version) { return version; } catch { - return ''; + return ""; } } - function getDAOVersions(address token) external view returns (DAOVersionInfo memory) { (address metadata, address auction, address treasury, address governor) = getAddresses(token); - return DAOVersionInfo({ - token: _safeGetVersion(token), - metadata: _safeGetVersion(metadata), - auction: _safeGetVersion(auction), - treasury: _safeGetVersion(treasury), - governor: _safeGetVersion(governor) - }); - } - - function getLatestVersions() external view returns (DAOVersionInfo memory) { - return DAOVersionInfo({ - token: _safeGetVersion(tokenImpl), - metadata: _safeGetVersion(metadataImpl), - auction: _safeGetVersion(auctionImpl), - treasury: _safeGetVersion(treasuryImpl), - governor: _safeGetVersion(governorImpl) - }); + return + DAOVersionInfo({ + token: _safeGetVersion(token), + metadata: _safeGetVersion(metadata), + auction: _safeGetVersion(auction), + treasury: _safeGetVersion(treasury), + governor: _safeGetVersion(governor) + }); } /// /// diff --git a/src/manager/storage/ManagerStorageV2.sol b/src/manager/storage/ManagerStorageV2.sol new file mode 100644 index 0000000..7855464 --- /dev/null +++ b/src/manager/storage/ManagerStorageV2.sol @@ -0,0 +1,11 @@ +// 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/token/IToken.sol b/src/token/IToken.sol index c38a626..de5465e 100644 --- a/src/token/IToken.sol +++ b/src/token/IToken.sol @@ -58,18 +58,27 @@ interface IToken is IUUPS, IERC721Votes, TokenTypesV1, TokenTypesV2 { /// @dev Reverts if the caller was not the contract manager error ONLY_MANAGER(); + /// /// + /// STRUCTS /// + /// /// + + struct TokenParams { + string name; + string symbol; + } + /// /// /// FUNCTIONS /// /// /// /// @notice Initializes a DAO's ERC-721 token /// @param founders The founding members to receive vesting allocations - /// @param initStrings The encoded token and metadata initialization strings + /// @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 initStrings, + bytes calldata data, address metadataRenderer, address auction, address initialOwner diff --git a/src/token/Token.sol b/src/token/Token.sol index 6f4d0fe..3da5c98 100644 --- a/src/token/Token.sol +++ b/src/token/Token.sol @@ -54,13 +54,13 @@ contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC /// @notice Initializes a DAO's ERC-721 token contract /// @param _founders The DAO founders - /// @param _initStrings The encoded token and metadata initialization strings + /// @param _data The encoded token initialization parameters /// @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 _initStrings, + bytes calldata _data, address _metadataRenderer, address _auction, address _initialOwner @@ -80,10 +80,10 @@ contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC _addFounders(_founders); // Decode the token name and symbol - (string memory _name, string memory _symbol, , , , ) = abi.decode(_initStrings, (string, string, string, string, string, string)); + IToken.TokenParams memory params = abi.decode(_data, (IToken.TokenParams)); // Initialize the ERC-721 token - __ERC721_init(_name, _symbol); + __ERC721_init(params.name, params.symbol); // Store the metadata renderer and auction house settings.metadataRenderer = IBaseMetadata(_metadataRenderer); diff --git a/src/token/metadata/MetadataRenderer.sol b/src/token/metadata/MetadataRenderer.sol index b9be085..6c28481 100644 --- a/src/token/metadata/MetadataRenderer.sol +++ b/src/token/metadata/MetadataRenderer.sol @@ -17,12 +17,13 @@ import { MetadataRendererStorageV2 } from "./storage/MetadataRendererStorageV2.s import { IToken } from "../../token/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 Metadata Renderer /// @author Iain Nash & Rohan Kulkarni /// @notice A DAO's artwork generator and renderer -/// @custom:repo github.com/ourzora/nouns-protocol +/// @custom:repo github.com/ourzora/nouns-protocol contract MetadataRenderer is IPropertyIPFSMetadataRenderer, VersionedContract, @@ -65,26 +66,23 @@ contract MetadataRenderer is /// /// /// @notice Initializes a DAO's token metadata renderer - /// @param _initStrings The encoded token and metadata initialization strings + /// @param _data The encoded metadata initialization parameters /// @param _token The ERC-721 token address - function initialize(bytes calldata _initStrings, address _token) external initializer { + 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 - (, , string memory _description, string memory _contractImage, string memory _projectURI, string memory _rendererBase) = abi.decode( - _initStrings, - (string, string, string, string, string, string) - ); + IBaseMetadata.MetadataParams memory params = abi.decode(_data, (IBaseMetadata.MetadataParams)); // Store the renderer settings - settings.projectURI = _projectURI; - settings.description = _description; - settings.contractImage = _contractImage; - settings.rendererBase = _rendererBase; - settings.projectURI = _projectURI; + settings.projectURI = params.projectURI; + settings.description = params.description; + settings.contractImage = params.contractImage; + settings.rendererBase = params.rendererBase; + settings.projectURI = params.projectURI; settings.token = _token; } @@ -126,11 +124,7 @@ contract MetadataRenderer is /// @param _names The names of the properties to add /// @param _items The items to add to each property /// @param _ipfsGroup The IPFS base URI and extension - function addProperties( - string[] calldata _names, - ItemParam[] calldata _items, - IPFSGroup calldata _ipfsGroup - ) external onlyOwner { + function addProperties(string[] calldata _names, ItemParam[] calldata _items, IPFSGroup calldata _ipfsGroup) external onlyOwner { _addProperties(_names, _items, _ipfsGroup); } @@ -139,21 +133,13 @@ contract MetadataRenderer is /// @param _names The names of the properties to add /// @param _items The items to add to each property /// @param _ipfsGroup The IPFS base URI and extension - function deleteAndRecreateProperties( - string[] calldata _names, - ItemParam[] calldata _items, - IPFSGroup calldata _ipfsGroup - ) external onlyOwner { + function deleteAndRecreateProperties(string[] calldata _names, ItemParam[] calldata _items, IPFSGroup calldata _ipfsGroup) external onlyOwner { delete ipfsData; delete properties; _addProperties(_names, _items, _ipfsGroup); } - function _addProperties( - string[] calldata _names, - ItemParam[] calldata _items, - IPFSGroup calldata _ipfsGroup - ) internal { + function _addProperties(string[] calldata _names, ItemParam[] calldata _items, IPFSGroup calldata _ipfsGroup) internal { // Cache the existing amount of IPFS data stored uint256 dataLength = ipfsData.length; diff --git a/src/token/metadata/interfaces/IBaseMetadata.sol b/src/token/metadata/interfaces/IBaseMetadata.sol index 265d0a7..a1ca513 100644 --- a/src/token/metadata/interfaces/IBaseMetadata.sol +++ b/src/token/metadata/interfaces/IBaseMetadata.sol @@ -3,7 +3,6 @@ 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 @@ -15,17 +14,25 @@ interface IBaseMetadata is IUUPS { /// @dev Reverts if the caller was not the contract manager error ONLY_MANAGER(); + /// /// + /// STRUCTS /// + /// /// + + struct MetadataParams { + string description; + string contractImage; + string projectURI; + string rendererBase; + } + /// /// /// FUNCTIONS /// /// /// /// @notice Initializes a DAO's token metadata renderer - /// @param initStrings The encoded token and metadata initialization strings + /// @param data The encoded token and metadata initialization strings /// @param token The associated ERC-721 token address - function initialize( - bytes calldata initStrings, - address token - ) external; + function initialize(bytes calldata data, address token) external; /// @notice Generates attributes for a token upon mint /// @param tokenId The ERC-721 token id diff --git a/test/Auction.t.sol b/test/Auction.t.sol index 70abda0..5a5fbb4 100644 --- a/test/Auction.t.sol +++ b/test/Auction.t.sol @@ -41,7 +41,7 @@ contract AuctionTest is NounsBuilderTest { deployMock(); vm.expectRevert(abi.encodeWithSignature("ALREADY_INITIALIZED()")); - auction.initialize(address(token), address(this), address(treasury), 1 minutes, 0 ether); + auction.initialize(address(token), address(this), address(treasury), implData[2]); } function test_Unpause() public { @@ -96,7 +96,7 @@ contract AuctionTest is NounsBuilderTest { vm.prank(bidder1); // 0 value bid placed - auction.createBid{value: 0}(2); + auction.createBid{ value: 0 }(2); (, uint256 highestBidOriginal, address highestBidderOriginal, , , ) = auction.auction(); assertEq(highestBidOriginal, 0); @@ -127,12 +127,12 @@ contract AuctionTest is NounsBuilderTest { // 0 value bid placed vm.prank(bidder1); - auction.createBid{value: 0}(2); + auction.createBid{ value: 0 }(2); // another 0 value bid should not be able to be vm.prank(bidder2); vm.expectRevert(IAuction.MINIMUM_BID_NOT_MET.selector); - auction.createBid{value: 0}(2); + auction.createBid{ value: 0 }(2); } function test_CreateBid(uint256 _amount) public { diff --git a/test/Gov.t.sol b/test/Gov.t.sol index cf246f1..cd7297c 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; - IManager.GovParams internal altGovParams; + IGovernor.GovParams internal altGovParams; function setUp() public virtual override { super.setUp(); @@ -48,7 +48,9 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { setGovParams(2 days, 1 days, 1 weeks, 25, 1000, founder); - deploy(foundersArr, tokenParams, auctionParams, govParams); + setImplementationAddresses(); + + deploy(foundersArr, implAddresses, implData); setMockMetadata(); } @@ -75,7 +77,9 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { setGovParams(2 days, 1 days, 1 weeks, 100, 1000, founder); - deploy(foundersArr, tokenParams, auctionParams, govParams); + setImplementationAddresses(); + + deploy(foundersArr, implAddresses, implData); setMockMetadata(); } @@ -115,12 +119,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { vm.warp(block.timestamp + 20); } - function castVotes( - bytes32 _proposalId, - uint256 _numAgainst, - uint256 _numFor, - uint256 _numAbstain - ) internal { + function castVotes(bytes32 _proposalId, uint256 _numAgainst, uint256 _numFor, uint256 _numAbstain) internal { uint256 currentVoterIndex; for (uint256 i = 0; i < _numAgainst; ++i) { @@ -145,15 +144,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { } } - function mockProposal() - internal - view - returns ( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas - ) - { + function mockProposal() internal view returns (address[] memory targets, uint256[] memory values, bytes[] memory calldatas) { targets = new address[](1); values = new uint256[](1); calldatas = new bytes[](1); @@ -179,12 +170,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { proposalId = governor.propose(targets, values, calldatas, ""); } - function createProposal( - address _proposer, - address _target, - uint256 _value, - bytes memory _calldata - ) internal returns (bytes32 proposalId) { + function createProposal(address _proposer, address _target, uint256 _value, bytes memory _calldata) internal returns (bytes32 proposalId) { deployMock(); address[] memory targets = new address[](1); @@ -217,21 +203,21 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { deployMock(); assertEq(treasury.owner(), address(governor)); - assertEq(treasury.delay(), govParams.timelockDelay); + assertEq(treasury.delay(), treasuryParams.timelockDelay); } function testRevert_CannotReinitializeGovernor() public { deployMock(); vm.expectRevert(abi.encodeWithSignature("ALREADY_INITIALIZED()")); - governor.initialize(address(this), address(this), address(this), 0, 0, 0, 0); + governor.initialize(address(this), address(this), new bytes(0)); } function testRevert_CannotReinitializeTreasury() public { deployMock(); vm.expectRevert(abi.encodeWithSignature("ALREADY_INITIALIZED()")); - treasury.initialize(address(this), 0); + treasury.initialize(address(this), new bytes(0)); } function test_CreateProposal() public { @@ -286,7 +272,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { function test_VerifySubmittedProposalHash() public { deployMock(); - // Mint a token to voter 1 to have quorum + // Mint a token to voter 1 to have quorum mintVoter1(); (address[] memory targets, uint256[] memory values, bytes[] memory calldatas) = mockProposal(); @@ -416,7 +402,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { function test_CastVote() public { deployMock(); - // This mints a token to voter1 + // This mints a token to voter1 bytes32 proposalId = createProposal(); uint256 votingDelay = governor.votingDelay(); @@ -455,7 +441,7 @@ contract GovTest is NounsBuilderTest, GovernorTypesV1 { function test_CastVoteWithSig() public { deployMock(); - // This mints a token to voter1 + // This mints a token to voter1 bytes32 proposalId = createProposal(); uint256 votingDelay = governor.votingDelay(); diff --git a/test/Manager.t.sol b/test/Manager.t.sol index c8658e7..aa0528f 100644 --- a/test/Manager.t.sol +++ b/test/Manager.t.sol @@ -47,18 +47,6 @@ contract ManagerTest is NounsBuilderTest { assertEq(metadataRenderer.owner(), address(founder)); } - function test_GetLatestVersions() public { - deployMock(); - - string memory version = manager.contractVersion(); - IManager.DAOVersionInfo memory versionInfo = manager.getLatestVersions(); - assertEq(versionInfo.token, version); - assertEq(versionInfo.metadata, version); - assertEq(versionInfo.governor, version); - assertEq(versionInfo.auction, version); - assertEq(versionInfo.treasury, version); - } - function test_GetDAOVersions() public { deployMock(); @@ -84,13 +72,11 @@ contract ManagerTest is NounsBuilderTest { assertEq(auction.minBidIncrement(), 10); } - - function test_TreasuryInitialized() public { deployMock(); assertEq(treasury.owner(), address(governor)); - assertEq(treasury.delay(), govParams.timelockDelay); + assertEq(treasury.delay(), treasuryParams.timelockDelay); } function test_GovernorInitialized() public { @@ -111,7 +97,7 @@ contract ManagerTest is NounsBuilderTest { foundersArr.push(); vm.expectRevert(abi.encodeWithSignature("FOUNDER_REQUIRED()")); - deploy(foundersArr, tokenParams, auctionParams, govParams); + deploy(foundersArr, implAddresses, implData); } function test_RegisterUpgrade() public { diff --git a/test/forking/TestBid.t.sol b/test/forking/TestBid.t.sol index 31acab9..568de98 100644 --- a/test/forking/TestBid.t.sol +++ b/test/forking/TestBid.t.sol @@ -21,6 +21,8 @@ contract TestBidError is Test { vm.rollFork(16200201); } + /* + function testBidIssue() public { (address metadata, address auction, address treasury, address governor) = manager.getAddresses(address(token)); address bidder1 = address(0xb1dd331); @@ -44,4 +46,5 @@ contract TestBidError is Test { vm.prank(bidder1); Auction(auction).createBid{ value: 0.30 ether }(2); } + */ } diff --git a/test/utils/NounsBuilderTest.sol b/test/utils/NounsBuilderTest.sol index bb886f0..548c468 100644 --- a/test/utils/NounsBuilderTest.sol +++ b/test/utils/NounsBuilderTest.sol @@ -5,11 +5,10 @@ import { Test } from "forge-std/Test.sol"; import { IManager, Manager } from "../../src/manager/Manager.sol"; import { IToken, Token } from "../../src/token/Token.sol"; -import { MetadataRenderer } from "../../src/token/metadata/MetadataRenderer.sol"; +import { IBaseMetadata, MetadataRenderer } from "../../src/token/metadata/MetadataRenderer.sol"; import { IAuction, Auction } from "../../src/auction/Auction.sol"; import { IGovernor, Governor } from "../../src/governance/governor/Governor.sol"; import { ITreasury, Treasury } from "../../src/governance/treasury/Treasury.sol"; -import { MetadataRenderer } from "../../src/token/metadata/MetadataRenderer.sol"; import { MetadataRendererTypesV1 } from "../../src/token/metadata/types/MetadataRendererTypesV1.sol"; import { ERC1967Proxy } from "../../src/lib/proxy/ERC1967Proxy.sol"; @@ -59,7 +58,7 @@ contract NounsBuilderTest is Test { vm.label(founder, "FOUNDER"); vm.label(founder2, "FOUNDER_2"); - managerImpl0 = address(new Manager(address(0), address(0), address(0), address(0), address(0))); + managerImpl0 = address(new Manager()); manager = Manager(address(new ERC1967Proxy(managerImpl0, abi.encodeWithSignature("initialize(address)", zoraDAO)))); tokenImpl = address(new Token(address(manager))); @@ -68,10 +67,16 @@ contract NounsBuilderTest is Test { treasuryImpl = address(new Treasury(address(manager))); governorImpl = address(new Governor(address(manager))); - managerImpl = address(new Manager(tokenImpl, metadataRendererImpl, auctionImpl, treasuryImpl, governorImpl)); + managerImpl = address(new Manager()); - vm.prank(zoraDAO); + vm.startPrank(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(); } /// /// @@ -79,9 +84,14 @@ contract NounsBuilderTest is Test { /// /// IManager.FounderParams[] internal foundersArr; - IManager.TokenParams internal tokenParams; - IManager.AuctionParams internal auctionParams; - IManager.GovParams internal govParams; + IToken.TokenParams internal tokenParams; + IBaseMetadata.MetadataParams internal metadataParams; + IAuction.AuctionParams internal auctionParams; + IGovernor.GovParams internal govParams; + ITreasury.TreasuryParams internal treasuryParams; + + address[] internal implAddresses; + bytes[] internal implData; function setMockFounderParams() internal virtual { address[] memory wallets = new address[](2); @@ -100,11 +110,7 @@ contract NounsBuilderTest is Test { setFounderParams(wallets, percents, vestingEnds); } - function setFounderParams( - address[] memory _wallets, - uint256[] memory _percents, - uint256[] memory _vestingEnds - ) internal virtual { + function setFounderParams(address[] memory _wallets, uint256[] memory _percents, uint256[] memory _vestingEnds) internal virtual { uint256 numFounders = _wallets.length; require(numFounders == _percents.length && numFounders == _vestingEnds.length); @@ -137,9 +143,19 @@ contract NounsBuilderTest is Test { string memory _contractURI, string memory _rendererBase ) internal virtual { - bytes memory initStrings = abi.encode(_name, _symbol, _description, _contractImage, _contractURI, _rendererBase); + tokenParams = IToken.TokenParams({ name: _name, symbol: _symbol }); + metadataParams = IBaseMetadata.MetadataParams({ + description: _description, + contractImage: _contractImage, + projectURI: _contractURI, + rendererBase: _rendererBase + }); - tokenParams = IManager.TokenParams({ initStrings: initStrings }); + implData.push(); + implData[manager.IMPLEMENTATION_TYPE_TOKEN()] = abi.encode(tokenParams); + + implData.push(); + implData[manager.IMPLEMENTATION_TYPE_METADATA()] = abi.encode(metadataParams); } function setMockAuctionParams() internal virtual { @@ -147,7 +163,9 @@ contract NounsBuilderTest is Test { } function setAuctionParams(uint256 _reservePrice, uint256 _duration) internal virtual { - auctionParams = IManager.AuctionParams({ reservePrice: _reservePrice, duration: _duration }); + implData.push(); + auctionParams = IAuction.AuctionParams({ reservePrice: _reservePrice, duration: _duration }); + implData[manager.IMPLEMENTATION_TYPE_AUCTION()] = abi.encode(auctionParams); } function setMockGovParams() internal virtual { @@ -162,14 +180,19 @@ contract NounsBuilderTest is Test { uint256 _quorumThresholdBps, address _vetoer ) internal virtual { - govParams = IManager.GovParams({ - timelockDelay: _timelockDelay, + implData.push(); + treasuryParams = ITreasury.TreasuryParams({ timelockDelay: _timelockDelay }); + implData[manager.IMPLEMENTATION_TYPE_TREASURY()] = abi.encode(treasuryParams); + + implData.push(); + govParams = IGovernor.GovParams({ votingDelay: _votingDelay, votingPeriod: _votingPeriod, proposalThresholdBps: _proposalThresholdBps, quorumThresholdBps: _quorumThresholdBps, vetoer: _vetoer }); + implData[manager.IMPLEMENTATION_TYPE_GOVERNOR()] = abi.encode(govParams); } function setMockMetadata() internal { @@ -186,6 +209,23 @@ contract NounsBuilderTest is Test { 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 /// /// /// @@ -205,16 +245,14 @@ contract NounsBuilderTest is Test { setMockGovParams(); - deploy(foundersArr, tokenParams, auctionParams, govParams); + setImplementationAddresses(); + + deploy(foundersArr, implAddresses, implData); setMockMetadata(); } - function deployWithCustomFounders( - address[] memory _wallets, - uint256[] memory _percents, - uint256[] memory _vestExpirys - ) internal virtual { + function deployWithCustomFounders(address[] memory _wallets, uint256[] memory _percents, uint256[] memory _vestExpirys) internal virtual { setFounderParams(_wallets, _percents, _vestExpirys); setMockTokenParams(); @@ -223,7 +261,9 @@ contract NounsBuilderTest is Test { setMockGovParams(); - deploy(foundersArr, tokenParams, auctionParams, govParams); + setImplementationAddresses(); + + deploy(foundersArr, implAddresses, implData); setMockMetadata(); } @@ -244,7 +284,9 @@ contract NounsBuilderTest is Test { setMockGovParams(); - deploy(foundersArr, tokenParams, auctionParams, govParams); + setImplementationAddresses(); + + deploy(foundersArr, implAddresses, implData); setMockMetadata(); } @@ -258,20 +300,16 @@ contract NounsBuilderTest is Test { setMockGovParams(); - deploy(foundersArr, tokenParams, auctionParams, govParams); + setImplementationAddresses(); + + deploy(foundersArr, implAddresses, implData); } - function deploy( - IManager.FounderParams[] memory _founderParams, - IManager.TokenParams memory _tokenParams, - IManager.AuctionParams memory _auctionParams, - IManager.GovParams memory _govParams - ) internal virtual { + function deploy(IManager.FounderParams[] memory _founderParams, address[] memory _implAddresses, bytes[] memory _implData) internal virtual { (address _token, address _metadata, address _auction, address _treasury, address _governor) = manager.deploy( _founderParams, - _tokenParams, - _auctionParams, - _govParams + _implAddresses, + _implData ); token = Token(_token);