diff --git a/addresses/84531.json b/addresses/84531.json index ffa8c98..0aabe33 100644 --- a/addresses/84531.json +++ b/addresses/84531.json @@ -2,17 +2,14 @@ "BuilderDAO": "0x7498e6e471f31e869f038D8DBffbDFdf650c3F95", "WETH": "0x4200000000000000000000000000000000000006", "CrossDomainMessenger": "0x4200000000000000000000000000000000000007", - "Manager": "0x0351045fda7eb52635d516f2a0ffc0b9d1d21f75", - "ManagerImpl": "0xb713fa8f71e0154d2bc867e491af2c2e4f6c5741", - "Auction": "0xbaafa316e549c21ae811ec39278f90e6bc770947", - "Token": "0x36d5157f02069a40969c7895fd3e21d0c2097dff", - "MirrorToken": "0x7AB6a3EB70ae18Afa72562f5D51BACea20a8AbBa", - "MetadataRenderer": "0x444ce208c0f0f6787d4fbaaa0657c2a0fd095eb6", - "Treasury": "0x44a2bb76be05c5086e626f5d7e5cbeb043c9d91d", - "Governor": "0x2eefd2e9fbb2ebc5c393f092bbdd90418521bbf1", - "ProtocolRewards": "0xb601cf7945e77d26111a4e41b82b0ac82e2be10c", - "ERC721RedeemMinter": "0x6cbd4119cc78df069673b34f42e643bffe3e473a", - "MerkleReserveMinter": "0xf2dcdd37bec020b07faff1014dd7b23db193f104", - "MigrationDeployer": "0xd49a4ef9d13ea17e6c84aa4ecf4565f9f05cdfd0", - "CollectionPlusDeployer": "0xa05e7d87e412b9c7d1e7fec77ed797517142e78a" + "ProtocolRewards": "0x7777777F279eba3d3Ad8F4E708545291A6fDBA8B", + "Manager": "0x2177ab5b03866ca31b5acd0b837d7fd54c7d2936", + "ManagerImpl": "0x7d09fcd78ded047e112060f66ce4d4ec7c59e0be", + "Auction": "0xc7f35eb5896aa32b4decbe7768c903db43a20044", + "Token": "0x209452d7b38a36b042967ab0d9c6366ae1242dca", + "MetadataRenderer": "0x4238d016947087d21e9959c628688b6b5a527adc", + "Treasury": "0x23cbb0637dfe7eec42202c885cbfe76c09c7c759", + "Governor": "0x11e646c27529af1a18e6977d909be90d0cf504f5", + "MerkleReserveMinter": "0x1991abb6d9613ae4e339a1a747a894201d5e4dae", + "MigrationDeployer": "0xfad4bd5777fb472609f97fcde9fb001f5c0dd506" } diff --git a/deploys/84531.version2_core.txt b/deploys/84531.version2_core.txt index 1e98b4b..62f64ad 100644 --- a/deploys/84531.version2_core.txt +++ b/deploys/84531.version2_core.txt @@ -1,8 +1,7 @@ -Manager: 0x0351045fda7eb52635d516f2a0ffc0b9d1d21f75 -Default Token implementation: 0x36d5157f02069a40969c7895fd3e21d0c2097dff -Protocol Rewards:0xb601cf7945e77d26111a4e41b82b0ac82e2be10c -Metadata Renderer implementation: 0x444ce208c0f0f6787d4fbaaa0657c2a0fd095eb6 -Auction implementation: 0xbaafa316e549c21ae811ec39278f90e6bc770947 -Treasury implementation: 0x44a2bb76be05c5086e626f5d7e5cbeb043c9d91d -Governor implementation: 0x2eefd2e9fbb2ebc5c393f092bbdd90418521bbf1 -Manager implementation: 0xb713fa8f71e0154d2bc867e491af2c2e4f6c5741 +Manager: 0x2177ab5b03866ca31b5acd0b837d7fd54c7d2936 +Token implementation: 0x209452d7b38a36b042967ab0d9c6366ae1242dca +Metadata Renderer implementation: 0x4238d016947087d21e9959c628688b6b5a527adc +Auction implementation: 0xc7f35eb5896aa32b4decbe7768c903db43a20044 +Treasury implementation: 0x23cbb0637dfe7eec42202c885cbfe76c09c7c759 +Governor implementation: 0x11e646c27529af1a18e6977d909be90d0cf504f5 +Manager implementation: 0x7d09fcd78ded047e112060f66ce4d4ec7c59e0be diff --git a/deploys/84531.version2_new.txt b/deploys/84531.version2_new.txt index 6dd00be..793e3d2 100644 --- a/deploys/84531.version2_new.txt +++ b/deploys/84531.version2_new.txt @@ -1,4 +1,2 @@ -ERC721 Redeem Minter: 0x6cbd4119cc78df069673b34f42e643bffe3e473a -Merkle Reserve Minter: 0xf2dcdd37bec020b07faff1014dd7b23db193f104 -Migration Deployer: 0xd49a4ef9d13ea17e6c84aa4ecf4565f9f05cdfd0 -Collection Plus Deployer: 0xa05e7d87e412b9c7d1e7fec77ed797517142e78a +Merkle Reserve Minter: 0x1991abb6d9613ae4e339a1a747a894201d5e4dae +Migration Deployer: 0xfad4bd5777fb472609f97fcde9fb001f5c0dd506 diff --git a/package.json b/package.json index 0c4bd6c..b68be6c 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,11 @@ "prepublishOnly": "rm -rf ./dist && forge clean && mkdir -p ./dist/artifacts && yarn build && cp -R src dist && cp -R addresses dist", "generate:interfaces": "forge script script/GetInterfaceIds.s.sol:GetInterfaceIds -vvvv", "deploy:dao": "source .env && forge script script/DeployNewDAO.s.sol:SetupDaoScript --private-key $PRIVATE_KEY --broadcast --rpc-url $RPC_URL -vvvv", - "deploy:contracts-local": "source .env && forge script script/DeployContracts.s.sol:DeployContracts --private-key $PRIVATE_KEY --broadcast --rpc-url $RPC_URL", - "deploy:contracts-v2-local": "source .env && forge script script/DeployContractsV2.s.sol:DeployContracts --private-key $PRIVATE_KEY --broadcast --rpc-url $RPC_URL", - "deploy:contracts-v2-core": "source .env && forge script script/DeployV2Core.s.sol:DeployContracts --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY", - "deploy:contracts-v2-new": "source .env && forge script script/DeployV2New.s.sol:DeployContracts --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY", - "deploy:contracts-zora": "source .env && forge script script/DeployContracts.s.sol:DeployContracts --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast --verify --verifier blockscout --verifier-url https://explorer.zora.energy/api? -vvvv", + "deploy:local": "source .env && forge script script/DeployContracts.s.sol:DeployContracts --private-key $PRIVATE_KEY --broadcast --rpc-url $RPC_URL", + "deploy:v2-local": "source .env && forge script script/DeployContractsV2.s.sol:DeployContracts --private-key $PRIVATE_KEY --broadcast --rpc-url $RPC_URL", + "deploy:v2-core": "source .env && forge script script/DeployV2Core.s.sol:DeployContracts --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY", + "deploy:v2-new": "source .env && forge script script/DeployV2New.s.sol:DeployContracts --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY", + "deploy:zora": "source .env && forge script script/DeployContracts.s.sol:DeployContracts --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast --verify --verifier blockscout --verifier-url https://explorer.zora.energy/api? -vvvv", "test": "echo 'temporarily skipping metadata tests, remove this when fixed' && forge test --no-match-test 'WithAddress' -vvv", "typechain": "typechain --target=ethers-v5 'dist/artifacts/*/*.json' --out-dir dist/typechain", "storage-inspect:check": "./script/storage-check.sh check Manager Auction Governor Treasury Token", diff --git a/src/auction/Auction.sol b/src/auction/Auction.sol index 5d876dc..7a83125 100644 --- a/src/auction/Auction.sol +++ b/src/auction/Auction.sol @@ -26,6 +26,16 @@ import { VersionedContract } from "../VersionedContract.sol"; /// - NounsAuctionHouse.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license. /// - Zora V3 ReserveAuctionCoreEth module commit 795aeca - licensed under the GPL-3.0 license. contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, Pausable, AuctionStorageV1, AuctionStorageV2 { + /// /// + /// CONSTANTS /// + /// /// + + /// @notice The basis points for 100% + uint256 private constant BPS_PER_100_PERCENT = 10_000; + + /// @notice The maximum rewards percentage + uint256 private constant MAX_FOUNDER_REWARDS_BPS = 3_000; + /// /// /// IMMUTABLES /// /// /// @@ -86,6 +96,9 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, // Ensure the caller is the contract manager if (msg.sender != address(manager)) revert ONLY_MANAGER(); + // Ensure the founder reward is not more than max + if (_founderRewardBps > MAX_FOUNDER_REWARDS_BPS) revert INVALID_REWARDS_CONFIG(); + // Initialize the reentrancy guard __ReentrancyGuard_init(); @@ -417,6 +430,10 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, /// @notice Updates the founder reward recipent address /// @param reward The new founder reward settings function setFounderReward(FounderReward calldata reward) external onlyOwner whenPaused { + // Ensure the reward is not more than max + if (reward.percentBps > MAX_FOUNDER_REWARDS_BPS) revert INVALID_REWARDS_CONFIG(); + + // Update the founder reward settings founderReward = reward; emit FounderRewardUpdated(reward); @@ -442,12 +459,12 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, uint256 totalBPS = _founderRewardBps + referralBps + builderBps; // Verify percentage is not more than 100 - if (totalBPS >= 10_000) { + if (totalBPS >= BPS_PER_100_PERCENT) { revert INVALID_REWARD_TOTAL(); } // Calulate total rewards - split.totalRewards = (_finalBidAmount * totalBPS) / 10_000; + split.totalRewards = (_finalBidAmount * totalBPS) / BPS_PER_100_PERCENT; // Set the recipients split.recipients = new address[](3); @@ -457,9 +474,9 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard, // Calculate reward splits split.amounts = new uint256[](3); - split.amounts[0] = (_finalBidAmount * _founderRewardBps) / 10_000; - split.amounts[1] = (_finalBidAmount * referralBps) / 10_000; - split.amounts[2] = (_finalBidAmount * builderBps) / 10_000; + split.amounts[0] = (_finalBidAmount * _founderRewardBps) / BPS_PER_100_PERCENT; + split.amounts[1] = (_finalBidAmount * referralBps) / BPS_PER_100_PERCENT; + split.amounts[2] = (_finalBidAmount * builderBps) / BPS_PER_100_PERCENT; // Leave reasons empty split.reasons = new bytes4[](3); diff --git a/src/auction/IAuction.sol b/src/auction/IAuction.sol index 7b08313..a0c540e 100644 --- a/src/auction/IAuction.sol +++ b/src/auction/IAuction.sol @@ -94,6 +94,9 @@ interface IAuction is IUUPS, IOwnable, IPausable { /// @dev Thrown if the auction creation failed error AUCTION_CREATE_FAILED_TO_LAUNCH(); + /// @dev Reverts if caller is not the token owner + error INVALID_REWARDS_CONFIG(); + /// @dev Thrown if the rewards total is greater than 100% error INVALID_REWARD_TOTAL(); diff --git a/src/deployers/L2MigrationDeployer.sol b/src/deployers/L2MigrationDeployer.sol index 45778b8..8c416a6 100644 --- a/src/deployers/L2MigrationDeployer.sol +++ b/src/deployers/L2MigrationDeployer.sol @@ -36,6 +36,9 @@ contract L2MigrationDeployer { /// @dev Transfer failed error TRANSFER_FAILED(); + /// @dev Metadata call failed + error METADATA_CALL_FAILED(); + /// /// /// IMMUTABLES /// /// /// @@ -124,39 +127,29 @@ contract L2MigrationDeployer { return (_token); } - ///@notice Adds metadata properties to the migrated DAO - /// @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, - IPropertyIPFSMetadataRenderer.ItemParam[] calldata _items, - IPropertyIPFSMetadataRenderer.IPFSGroup calldata _ipfsGroup - ) external onlyCrossDomainMessenger { - (, address metadata, , , ) = _getDAOAddressesFromSender(); - IPropertyIPFSMetadataRenderer(metadata).addProperties(_names, _items, _ipfsGroup); - } - - ///@notice Called once all metadata properties are added to set ownership of migrated DAO contracts to treasury - function finalize() external onlyCrossDomainMessenger { - (address token, , address auction, address treasury, ) = _getDAOAddressesFromSender(); - - // Transfer ownership of token contract - Ownable(token).transferOwnership(treasury); - - // Transfer ownership of auction contract - Ownable(auction).transferOwnership(treasury); - } - ///@notice Resets the stored deployment if L1 DAO wants to redeploy function resetDeployment() external onlyCrossDomainMessenger { _resetTokenDeployer(); } /// /// - /// DEPOSIT /// + /// HELPER FUNCTIONS /// /// /// + ///@notice Helper method to pass a call along to the deployed metadata renderer + /// @param _data The names of the properties to add + function callMetadataRenderer(bytes memory _data) external onlyCrossDomainMessenger { + (, address metadata, , , ) = _getDAOAddressesFromSender(); + + // Call the metadata renderer + (bool success, ) = metadata.call(_data); + + // Revert if metadata call fails + if (!success) { + revert METADATA_CALL_FAILED(); + } + } + ///@notice Helper method to deposit ether from L1 DAO treasury to L2 DAO treasury function depositToTreasury() external payable onlyCrossDomainMessenger { (, , , address treasury, ) = _getDAOAddressesFromSender(); @@ -170,6 +163,17 @@ contract L2MigrationDeployer { } } + ///@notice Transfers ownership of migrated DAO contracts to treasury + function renounceOwnership() external onlyCrossDomainMessenger { + (address token, , address auction, address treasury, ) = _getDAOAddressesFromSender(); + + // Transfer ownership of token contract + Ownable(token).transferOwnership(treasury); + + // Transfer ownership of auction contract + Ownable(auction).transferOwnership(treasury); + } + /// /// /// PRIVATE /// /// /// diff --git a/src/manager/IManager.sol b/src/manager/IManager.sol index 4d8097b..4a49d24 100644 --- a/src/manager/IManager.sol +++ b/src/manager/IManager.sol @@ -46,6 +46,9 @@ interface IManager is IUUPS, IOwnable { /// @dev Reverts if caller is not the token owner error ONLY_TOKEN_OWNER(); + /// @dev Reverts if caller is not the token owner + error INVALID_REWARDS_CONFIG(); + /// /// /// STRUCTS /// /// /// diff --git a/src/manager/Manager.sol b/src/manager/Manager.sol index 98fec5d..92f46d7 100644 --- a/src/manager/Manager.sol +++ b/src/manager/Manager.sol @@ -23,6 +23,13 @@ import { IVersionedContract } from "../lib/interfaces/IVersionedContract.sol"; /// @custom:repo github.com/ourzora/nouns-protocol /// @notice The DAO deployer and upgrade manager contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1, ManagerStorageV2 { + /// /// + /// CONSTANTS /// + /// /// + + /// @notice The maximum combined BPS for referral and builder rewards + uint256 private constant MAX_REWARDS_BPS = 2_000; + /// /// /// IMMUTABLES /// /// /// @@ -243,9 +250,15 @@ contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 emit UpgradeRemoved(_baseImpl, _upgradeImpl); } - /// @notice Function to set the reward percentages + /// @notice Set the global reward configuration /// @param _rewards The reward to be paid to the referrer in BPS function setRewardConfig(RewardConfig calldata _rewards) external onlyOwner { + // Ensure the rewards are valid + if (_rewards.referralBps + _rewards.builderBps > MAX_REWARDS_BPS) { + revert INVALID_REWARDS_CONFIG(); + } + + // Set the rewards rewards = _rewards; } diff --git a/test/L2MigrationDeployer.t.sol b/test/L2MigrationDeployer.t.sol index 42077dd..0000284 100644 --- a/test/L2MigrationDeployer.t.sol +++ b/test/L2MigrationDeployer.t.sol @@ -42,7 +42,7 @@ contract L2MigrationDeployerTest is NounsBuilderTest { addMetadataProperties(); - deployer.finalize(); + deployer.renounceOwnership(); vm.stopPrank(); @@ -90,7 +90,8 @@ contract L2MigrationDeployerTest is NounsBuilderTest { MetadataRendererTypesV1.IPFSGroup memory ipfsGroup = MetadataRendererTypesV1.IPFSGroup({ baseUri: "BASE_URI", extension: "EXTENSION" }); - deployer.addProperties(names, items, ipfsGroup); + bytes memory data = abi.encodeWithSignature("addProperties(string[],(uint256,string,bool)[],(string,string))", names, items, ipfsGroup); + deployer.callMetadataRenderer(data); } function setMinterParams() internal { @@ -182,7 +183,7 @@ contract L2MigrationDeployerTest is NounsBuilderTest { addMetadataProperties(); vm.expectRevert(abi.encodeWithSignature("NOT_CROSS_DOMAIN_MESSENGER()")); - deployer.finalize(); + deployer.renounceOwnership(); vm.expectRevert(abi.encodeWithSignature("NOT_CROSS_DOMAIN_MESSENGER()")); deployer.resetDeployment(); @@ -195,7 +196,7 @@ contract L2MigrationDeployerTest is NounsBuilderTest { addMetadataProperties(); vm.expectRevert(abi.encodeWithSignature("NO_DAO_DEPLOYED()")); - deployer.finalize(); + deployer.renounceOwnership(); vm.stopPrank(); }