From b50b96aa09974c6e7a42e451cb188eb6e93b9639 Mon Sep 17 00:00:00 2001 From: davidbrai Date: Fri, 10 Nov 2023 11:18:43 +0000 Subject: [PATCH] exploratory code for protocol rewards because we hit the contract size limit, this commit has a POC for reducing it. all the admin functions are replaced with a fallback function that delegate calls the admin lib. --- .../governance/NounsDAOInterfaces.sol | 2 + .../contracts/governance/NounsDAOLogicV3.sol | 516 ++++++------- .../contracts/governance/NounsDAOV3Admin.sol | 237 +++--- .../governance/NounsDAOV3Proposals.sol | 59 +- .../contracts/interfaces/INounsDAOLogicV3.sol | 708 ++++++++++++++++++ .../contracts/test/NounsDAOLogicV3Harness.sol | 8 +- .../NounsDAOLogicV3BaseTest.sol | 3 +- .../NounsDAOLogicV3/NounsDAOV3Admin.t.sol | 7 +- .../NounsDAOLogicV3/UpgradeToDAOV3.t.sol | 24 - .../governance/fork/ForkingEndToEnd.t.sol | 5 +- .../governance/fork/NounsDAOLogicV1Fork.t.sol | 3 +- .../test/foundry/helpers/DeployUtilsFork.sol | 3 +- .../test/foundry/helpers/DeployUtilsV3.sol | 5 +- 13 files changed, 1141 insertions(+), 439 deletions(-) create mode 100644 packages/nouns-contracts/contracts/interfaces/INounsDAOLogicV3.sol diff --git a/packages/nouns-contracts/contracts/governance/NounsDAOInterfaces.sol b/packages/nouns-contracts/contracts/governance/NounsDAOInterfaces.sol index 8fb0b4d3d4..a123412734 100644 --- a/packages/nouns-contracts/contracts/governance/NounsDAOInterfaces.sol +++ b/packages/nouns-contracts/contracts/governance/NounsDAOInterfaces.sol @@ -767,6 +767,8 @@ contract NounsDAOStorageV3 { address[] signers; /// @notice When true, a proposal would be executed on timelockV1 instead of the current timelock bool executeOnTimelockV1; + // TODO bitpack and natspec + uint16 client; } /// @notice Ballot receipt record for a voter diff --git a/packages/nouns-contracts/contracts/governance/NounsDAOLogicV3.sol b/packages/nouns-contracts/contracts/governance/NounsDAOLogicV3.sol index 00c5ccdc3b..aba4af5270 100644 --- a/packages/nouns-contracts/contracts/governance/NounsDAOLogicV3.sol +++ b/packages/nouns-contracts/contracts/governance/NounsDAOLogicV3.sol @@ -61,6 +61,7 @@ import { NounsDAOV3DynamicQuorum } from './NounsDAOV3DynamicQuorum.sol'; import { NounsDAOV3Votes } from './NounsDAOV3Votes.sol'; import { NounsDAOV3Proposals } from './NounsDAOV3Proposals.sol'; import { NounsDAOV3Fork } from './fork/NounsDAOV3Fork.sol'; +import { Address } from '@openzeppelin/contracts/utils/Address.sol'; contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { using NounsDAOV3Admin for StorageV3; @@ -152,23 +153,23 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { if (timelock_ == address(0)) revert InvalidTimelockAddress(); if (nouns_ == address(0)) revert InvalidNounsAddress(); - ds._setVotingPeriod(daoParams_.votingPeriod); - ds._setVotingDelay(daoParams_.votingDelay); - ds._setProposalThresholdBPS(daoParams_.proposalThresholdBPS); + NounsDAOV3Admin._setVotingPeriod(daoParams_.votingPeriod); + NounsDAOV3Admin._setVotingDelay(daoParams_.votingDelay); + NounsDAOV3Admin._setProposalThresholdBPS(daoParams_.proposalThresholdBPS); ds.timelock = INounsDAOExecutorV2(timelock_); ds.nouns = NounsTokenLike(nouns_); ds.forkEscrow = INounsDAOForkEscrow(forkEscrow_); ds.forkDAODeployer = IForkDAODeployer(forkDAODeployer_); ds.vetoer = vetoer_; - _setDynamicQuorumParams( + NounsDAOV3Admin._setDynamicQuorumParams( dynamicQuorumParams_.minQuorumVotesBPS, dynamicQuorumParams_.maxQuorumVotesBPS, dynamicQuorumParams_.quorumCoefficient ); - ds._setLastMinuteWindowInBlocks(daoParams_.lastMinuteWindowInBlocks); - ds._setObjectionPeriodDurationInBlocks(daoParams_.objectionPeriodDurationInBlocks); - ds._setProposalUpdatablePeriodInBlocks(daoParams_.proposalUpdatablePeriodInBlocks); + NounsDAOV3Admin._setLastMinuteWindowInBlocks(daoParams_.lastMinuteWindowInBlocks); + NounsDAOV3Admin._setObjectionPeriodDurationInBlocks(daoParams_.objectionPeriodDurationInBlocks); + NounsDAOV3Admin._setProposalUpdatablePeriodInBlocks(daoParams_.proposalUpdatablePeriodInBlocks); } /** @@ -192,8 +193,20 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { string[] memory signatures, bytes[] memory calldatas, string memory description + ) external returns (uint256) { + return propose(targets, values, signatures, calldatas, description, 0); + } + + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description, + uint16 clientId ) public returns (uint256) { - return ds.propose(NounsDAOV3Proposals.ProposalTxs(targets, values, signatures, calldatas), description); + return + ds.propose(NounsDAOV3Proposals.ProposalTxs(targets, values, signatures, calldatas), description, clientId); } /** @@ -217,7 +230,26 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { return ds.proposeOnTimelockV1( NounsDAOV3Proposals.ProposalTxs(targets, values, signatures, calldatas), - description + description, + 0 + ); + } + + function proposeBySigs( + ProposerSignature[] memory proposerSignatures, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description, + uint16 clientId + ) public returns (uint256) { + return + ds.proposeBySigs( + proposerSignatures, + NounsDAOV3Proposals.ProposalTxs(targets, values, signatures, calldatas), + description, + clientId ); } @@ -241,12 +273,7 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { bytes[] memory calldatas, string memory description ) external returns (uint256) { - return - ds.proposeBySigs( - proposerSignatures, - NounsDAOV3Proposals.ProposalTxs(targets, values, signatures, calldatas), - description - ); + return proposeBySigs(proposerSignatures, targets, values, signatures, calldatas, description, 0); } /** @@ -370,15 +397,6 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { ds.execute(proposalId); } - /** - * @notice Executes a queued proposal on timelockV1 if eta has passed - * This is only required for proposal that were queued on timelockV1, but before the upgrade to DAO V3. - * These proposals will not have the `executeOnTimelockV1` bool turned on. - */ - function executeOnTimelockV1(uint256 proposalId) external { - ds.executeOnTimelockV1(proposalId); - } - /** * @notice Cancels a proposal only if sender is the proposer or a signer, or proposer & signers voting power * dropped below proposal threshold @@ -454,7 +472,7 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { * Differs from `GovernerBravo` which uses fixed amount */ function proposalThreshold() public view returns (uint256) { - return ds.proposalThreshold(ds.adjustedTotalSupply()); + return ds.proposalThreshold(adjustedTotalSupply()); } /** @@ -536,7 +554,7 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { * escrow after it has closed. * This is used when calculating proposal threshold, quorum, fork threshold & treasury split. */ - function adjustedTotalSupply() external view returns (uint256) { + function adjustedTotalSupply() public view returns (uint256) { return ds.adjustedTotalSupply(); } @@ -606,7 +624,7 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { uint256 proposalId, uint8 support, string calldata reason - ) external { + ) public { ds.castRefundableVoteWithReason(proposalId, support, reason); } @@ -644,230 +662,226 @@ contract NounsDAOLogicV3 is NounsDAOStorageV3, NounsDAOEventsV3 { * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ */ - /** - * @notice Admin function for setting the voting delay. Best to set voting delay to at least a few days, to give - * voters time to make sense of proposals, e.g. 21,600 blocks which should be at least 3 days. - * @param newVotingDelay new voting delay, in blocks - */ - function _setVotingDelay(uint256 newVotingDelay) external { - ds._setVotingDelay(newVotingDelay); - } - - /** - * @notice Admin function for setting the voting period - * @param newVotingPeriod new voting period, in blocks - */ - function _setVotingPeriod(uint256 newVotingPeriod) external { - ds._setVotingPeriod(newVotingPeriod); - } - - /** - * @notice Admin function for setting the proposal threshold basis points - * @dev newProposalThresholdBPS must be in [`MIN_PROPOSAL_THRESHOLD_BPS`,`MAX_PROPOSAL_THRESHOLD_BPS`] - * @param newProposalThresholdBPS new proposal threshold - */ - function _setProposalThresholdBPS(uint256 newProposalThresholdBPS) external { - ds._setProposalThresholdBPS(newProposalThresholdBPS); - } - - /** - * @notice Admin function for setting the objection period duration - * @param newObjectionPeriodDurationInBlocks new objection period duration, in blocks - */ - function _setObjectionPeriodDurationInBlocks(uint32 newObjectionPeriodDurationInBlocks) external { - ds._setObjectionPeriodDurationInBlocks(newObjectionPeriodDurationInBlocks); - } - - /** - * @notice Admin function for setting the objection period last minute window - * @param newLastMinuteWindowInBlocks new objection period last minute window, in blocks - */ - function _setLastMinuteWindowInBlocks(uint32 newLastMinuteWindowInBlocks) external { - ds._setLastMinuteWindowInBlocks(newLastMinuteWindowInBlocks); - } - - /** - * @notice Admin function for setting the proposal updatable period - * @param newProposalUpdatablePeriodInBlocks the new proposal updatable period, in blocks - */ - function _setProposalUpdatablePeriodInBlocks(uint32 newProposalUpdatablePeriodInBlocks) external { - ds._setProposalUpdatablePeriodInBlocks(newProposalUpdatablePeriodInBlocks); - } - - /** - * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. - * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. - * @param newPendingAdmin New pending admin. - */ - function _setPendingAdmin(address newPendingAdmin) external { - ds._setPendingAdmin(newPendingAdmin); - } - - /** - * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin - * @dev Admin function for pending admin to accept role and update admin - */ - function _acceptAdmin() external { - ds._acceptAdmin(); - } - - /** - * @notice Begins transition of vetoer. The newPendingVetoer must call _acceptVetoer to finalize the transfer. - * @param newPendingVetoer New Pending Vetoer - */ - function _setPendingVetoer(address newPendingVetoer) public { - ds._setPendingVetoer(newPendingVetoer); - } - - /** - * @notice Called by the pendingVetoer to accept role and update vetoer - */ - function _acceptVetoer() external { - ds._acceptVetoer(); - } - - /** - * @notice Burns veto priviledges - * @dev Vetoer function destroying veto power forever - */ - function _burnVetoPower() public { - ds._burnVetoPower(); - } - - /** - * @notice Admin function for setting the minimum quorum votes bps - * @param newMinQuorumVotesBPS minimum quorum votes bps - * Must be between `MIN_QUORUM_VOTES_BPS_LOWER_BOUND` and `MIN_QUORUM_VOTES_BPS_UPPER_BOUND` - * Must be lower than or equal to maxQuorumVotesBPS - */ - function _setMinQuorumVotesBPS(uint16 newMinQuorumVotesBPS) external { - ds._setMinQuorumVotesBPS(newMinQuorumVotesBPS); - } - - /** - * @notice Admin function for setting the maximum quorum votes bps - * @param newMaxQuorumVotesBPS maximum quorum votes bps - * Must be lower than `MAX_QUORUM_VOTES_BPS_UPPER_BOUND` - * Must be higher than or equal to minQuorumVotesBPS - */ - function _setMaxQuorumVotesBPS(uint16 newMaxQuorumVotesBPS) external { - ds._setMaxQuorumVotesBPS(newMaxQuorumVotesBPS); - } - - /** - * @notice Admin function for setting the dynamic quorum coefficient - * @param newQuorumCoefficient the new coefficient, as a fixed point integer with 6 decimals - */ - function _setQuorumCoefficient(uint32 newQuorumCoefficient) external { - ds._setQuorumCoefficient(newQuorumCoefficient); - } - - /** - * @notice Admin function for setting all the dynamic quorum parameters - * @param newMinQuorumVotesBPS minimum quorum votes bps - * Must be between `MIN_QUORUM_VOTES_BPS_LOWER_BOUND` and `MIN_QUORUM_VOTES_BPS_UPPER_BOUND` - * Must be lower than or equal to maxQuorumVotesBPS - * @param newMaxQuorumVotesBPS maximum quorum votes bps - * Must be lower than `MAX_QUORUM_VOTES_BPS_UPPER_BOUND` - * Must be higher than or equal to minQuorumVotesBPS - * @param newQuorumCoefficient the new coefficient, as a fixed point integer with 6 decimals - */ - function _setDynamicQuorumParams( - uint16 newMinQuorumVotesBPS, - uint16 newMaxQuorumVotesBPS, - uint32 newQuorumCoefficient - ) public { - ds._setDynamicQuorumParams(newMinQuorumVotesBPS, newMaxQuorumVotesBPS, newQuorumCoefficient); - } - - /** - * @notice Withdraws all the ETH in the contract. This is callable only by the admin (timelock). - */ - function _withdraw() external returns (uint256, bool) { - return ds._withdraw(); - } - - /** - * @notice Admin function for setting the fork period - * @param newForkPeriod the new fork proposal period, in seconds - */ - function _setForkPeriod(uint256 newForkPeriod) external { - ds._setForkPeriod(newForkPeriod); - } - - /** - * @notice Admin function for setting the fork threshold - * @param newForkThresholdBPS the new fork proposal threshold, in basis points - */ - function _setForkThresholdBPS(uint256 newForkThresholdBPS) external { - ds._setForkThresholdBPS(newForkThresholdBPS); - } - - /** - * @notice Admin function for setting the proposal id at which vote snapshots start using the voting start block - * instead of the proposal creation block. - * Sets it to the next proposal id. - */ - function _setVoteSnapshotBlockSwitchProposalId() external { - ds._setVoteSnapshotBlockSwitchProposalId(); - } - - /** - * @notice Admin function for setting the fork DAO deployer contract - */ - function _setForkDAODeployer(address newForkDAODeployer) external { - ds._setForkDAODeployer(newForkDAODeployer); - } - - /** - * @notice Admin function for setting the ERC20 tokens that are used when splitting funds to a fork - */ - function _setErc20TokensToIncludeInFork(address[] calldata erc20tokens) external { - ds._setErc20TokensToIncludeInFork(erc20tokens); - } - - /** - * @notice Admin function for setting the fork escrow contract - */ - function _setForkEscrow(address newForkEscrow) external { - ds._setForkEscrow(newForkEscrow); - } - - /** - * @notice Admin function for setting the fork related parameters - * @param forkEscrow_ the fork escrow contract - * @param forkDAODeployer_ the fork dao deployer contract - * @param erc20TokensToIncludeInFork_ the ERC20 tokens used when splitting funds to a fork - * @param forkPeriod_ the period during which it's possible to join a fork after exeuction - * @param forkThresholdBPS_ the threshold required of escrowed nouns in order to execute a fork - */ - function _setForkParams( - address forkEscrow_, - address forkDAODeployer_, - address[] calldata erc20TokensToIncludeInFork_, - uint256 forkPeriod_, - uint256 forkThresholdBPS_ - ) external { - ds._setForkEscrow(forkEscrow_); - ds._setForkDAODeployer(forkDAODeployer_); - ds._setErc20TokensToIncludeInFork(erc20TokensToIncludeInFork_); - ds._setForkPeriod(forkPeriod_); - ds._setForkThresholdBPS(forkThresholdBPS_); - } - - /** - * @notice Admin function for setting the timelocks and admin - * @param newTimelock the new timelock contract - * @param newTimelockV1 the new timelockV1 contract - * @param newAdmin the new admin address - */ - function _setTimelocksAndAdmin( - address newTimelock, - address newTimelockV1, - address newAdmin - ) external { - ds._setTimelocksAndAdmin(newTimelock, newTimelockV1, newAdmin); - } + // /** + // * @notice Admin function for setting the voting delay. Best to set voting delay to at least a few days, to give + // * voters time to make sense of proposals, e.g. 21,600 blocks which should be at least 3 days. + // * @param newVotingDelay new voting delay, in blocks + // */ + // function _setVotingDelay(uint256 newVotingDelay) external { + // ds._setVotingDelay(newVotingDelay); + // } + + // /** + // * @notice Admin function for setting the voting period + // * @param newVotingPeriod new voting period, in blocks + // */ + // function _setVotingPeriod(uint256 newVotingPeriod) external { + // ds._setVotingPeriod(newVotingPeriod); + // } + + // /** + // * @notice Admin function for setting the proposal threshold basis points + // * @dev newProposalThresholdBPS must be in [`MIN_PROPOSAL_THRESHOLD_BPS`,`MAX_PROPOSAL_THRESHOLD_BPS`] + // * @param newProposalThresholdBPS new proposal threshold + // */ + // function _setProposalThresholdBPS(uint256 newProposalThresholdBPS) external { + // ds._setProposalThresholdBPS(newProposalThresholdBPS); + // } + + fallback() external payable { + Address.functionDelegateCall(address(NounsDAOV3Admin), msg.data); + } + + // /** + // * @notice Admin function for setting the objection period last minute window + // * @param newLastMinuteWindowInBlocks new objection period last minute window, in blocks + // */ + // function _setLastMinuteWindowInBlocks(uint32 newLastMinuteWindowInBlocks) external { + // ds._setLastMinuteWindowInBlocks(newLastMinuteWindowInBlocks); + // } + + // /** + // * @notice Admin function for setting the proposal updatable period + // * @param newProposalUpdatablePeriodInBlocks the new proposal updatable period, in blocks + // */ + // function _setProposalUpdatablePeriodInBlocks(uint32 newProposalUpdatablePeriodInBlocks) external { + // ds._setProposalUpdatablePeriodInBlocks(newProposalUpdatablePeriodInBlocks); + // } + + // /** + // * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + // * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + // * @param newPendingAdmin New pending admin. + // */ + // function _setPendingAdmin(address newPendingAdmin) external { + // ds._setPendingAdmin(newPendingAdmin); + // } + + // /** + // * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin + // * @dev Admin function for pending admin to accept role and update admin + // */ + // function _acceptAdmin() external { + // ds._acceptAdmin(); + // } + + // /** + // * @notice Begins transition of vetoer. The newPendingVetoer must call _acceptVetoer to finalize the transfer. + // * @param newPendingVetoer New Pending Vetoer + // */ + // function _setPendingVetoer(address newPendingVetoer) public { + // ds._setPendingVetoer(newPendingVetoer); + // } + + // /** + // * @notice Called by the pendingVetoer to accept role and update vetoer + // */ + // function _acceptVetoer() external { + // ds._acceptVetoer(); + // } + + // /** + // * @notice Burns veto priviledges + // * @dev Vetoer function destroying veto power forever + // */ + // function _burnVetoPower() public { + // ds._burnVetoPower(); + // } + + // /** + // * @notice Admin function for setting the minimum quorum votes bps + // * @param newMinQuorumVotesBPS minimum quorum votes bps + // * Must be between `MIN_QUORUM_VOTES_BPS_LOWER_BOUND` and `MIN_QUORUM_VOTES_BPS_UPPER_BOUND` + // * Must be lower than or equal to maxQuorumVotesBPS + // */ + // function _setMinQuorumVotesBPS(uint16 newMinQuorumVotesBPS) external { + // ds._setMinQuorumVotesBPS(newMinQuorumVotesBPS); + // } + + // /** + // * @notice Admin function for setting the maximum quorum votes bps + // * @param newMaxQuorumVotesBPS maximum quorum votes bps + // * Must be lower than `MAX_QUORUM_VOTES_BPS_UPPER_BOUND` + // * Must be higher than or equal to minQuorumVotesBPS + // */ + // function _setMaxQuorumVotesBPS(uint16 newMaxQuorumVotesBPS) external { + // ds._setMaxQuorumVotesBPS(newMaxQuorumVotesBPS); + // } + + // /** + // * @notice Admin function for setting the dynamic quorum coefficient + // * @param newQuorumCoefficient the new coefficient, as a fixed point integer with 6 decimals + // */ + // function _setQuorumCoefficient(uint32 newQuorumCoefficient) external { + // ds._setQuorumCoefficient(newQuorumCoefficient); + // } + + // /** + // * @notice Admin function for setting all the dynamic quorum parameters + // * @param newMinQuorumVotesBPS minimum quorum votes bps + // * Must be between `MIN_QUORUM_VOTES_BPS_LOWER_BOUND` and `MIN_QUORUM_VOTES_BPS_UPPER_BOUND` + // * Must be lower than or equal to maxQuorumVotesBPS + // * @param newMaxQuorumVotesBPS maximum quorum votes bps + // * Must be lower than `MAX_QUORUM_VOTES_BPS_UPPER_BOUND` + // * Must be higher than or equal to minQuorumVotesBPS + // * @param newQuorumCoefficient the new coefficient, as a fixed point integer with 6 decimals + // */ + // function _setDynamicQuorumParams( + // uint16 newMinQuorumVotesBPS, + // uint16 newMaxQuorumVotesBPS, + // uint32 newQuorumCoefficient + // ) public { + // ds._setDynamicQuorumParams(newMinQuorumVotesBPS, newMaxQuorumVotesBPS, newQuorumCoefficient); + // } + + // /** + // * @notice Withdraws all the ETH in the contract. This is callable only by the admin (timelock). + // */ + // function _withdraw() external returns (uint256, bool) { + // return ds._withdraw(); + // } + + // /** + // * @notice Admin function for setting the fork period + // * @param newForkPeriod the new fork proposal period, in seconds + // */ + // function _setForkPeriod(uint256 newForkPeriod) external { + // ds._setForkPeriod(newForkPeriod); + // } + + // /** + // * @notice Admin function for setting the fork threshold + // * @param newForkThresholdBPS the new fork proposal threshold, in basis points + // */ + // function _setForkThresholdBPS(uint256 newForkThresholdBPS) external { + // ds._setForkThresholdBPS(newForkThresholdBPS); + // } + + // /** + // * @notice Admin function for setting the proposal id at which vote snapshots start using the voting start block + // * instead of the proposal creation block. + // * Sets it to the next proposal id. + // */ + // function _setVoteSnapshotBlockSwitchProposalId() external { + // ds._setVoteSnapshotBlockSwitchProposalId(); + // } + + // /** + // * @notice Admin function for setting the fork DAO deployer contract + // */ + // function _setForkDAODeployer(address newForkDAODeployer) external { + // ds._setForkDAODeployer(newForkDAODeployer); + // } + + // /** + // * @notice Admin function for setting the ERC20 tokens that are used when splitting funds to a fork + // */ + // function _setErc20TokensToIncludeInFork(address[] calldata erc20tokens) external { + // ds._setErc20TokensToIncludeInFork(erc20tokens); + // } + + // /** + // * @notice Admin function for setting the fork escrow contract + // */ + // function _setForkEscrow(address newForkEscrow) external { + // ds._setForkEscrow(newForkEscrow); + // } + + // /** + // * @notice Admin function for setting the fork related parameters + // * @param forkEscrow_ the fork escrow contract + // * @param forkDAODeployer_ the fork dao deployer contract + // * @param erc20TokensToIncludeInFork_ the ERC20 tokens used when splitting funds to a fork + // * @param forkPeriod_ the period during which it's possible to join a fork after exeuction + // * @param forkThresholdBPS_ the threshold required of escrowed nouns in order to execute a fork + // */ + // function _setForkParams( + // address forkEscrow_, + // address forkDAODeployer_, + // address[] calldata erc20TokensToIncludeInFork_, + // uint256 forkPeriod_, + // uint256 forkThresholdBPS_ + // ) external { + // ds._setForkEscrow(forkEscrow_); + // ds._setForkDAODeployer(forkDAODeployer_); + // ds._setErc20TokensToIncludeInFork(erc20TokensToIncludeInFork_); + // ds._setForkPeriod(forkPeriod_); + // ds._setForkThresholdBPS(forkThresholdBPS_); + // } + + // /** + // * @notice Admin function for setting the timelocks and admin + // * @param newTimelock the new timelock contract + // * @param newTimelockV1 the new timelockV1 contract + // * @param newAdmin the new admin address + // */ + // function _setTimelocksAndAdmin( + // address newTimelock, + // address newTimelockV1, + // address newAdmin + // ) external { + // ds._setTimelocksAndAdmin(newTimelock, newTimelockV1, newAdmin); + // } /** * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ diff --git a/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol b/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol index 6102986b77..c25f01773a 100644 --- a/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol +++ b/packages/nouns-contracts/contracts/governance/NounsDAOV3Admin.sol @@ -147,8 +147,8 @@ library NounsDAOV3Admin { /// @notice Upper bound for proposal updatable period duration in blocks. uint256 public constant MAX_UPDATABLE_PERIOD_BLOCKS = 7 days / 12; - modifier onlyAdmin(NounsDAOStorageV3.StorageV3 storage ds) { - if (msg.sender != ds.admin) { + modifier onlyAdmin() { + if (msg.sender != ds().admin) { revert AdminOnly(); } _; @@ -159,13 +159,13 @@ library NounsDAOV3Admin { * voters time to make sense of proposals, e.g. 21,600 blocks which should be at least 3 days. * @param newVotingDelay new voting delay, in blocks */ - function _setVotingDelay(NounsDAOStorageV3.StorageV3 storage ds, uint256 newVotingDelay) external onlyAdmin(ds) { + function _setVotingDelay(uint256 newVotingDelay) external onlyAdmin { require( newVotingDelay >= MIN_VOTING_DELAY_BLOCKS && newVotingDelay <= MAX_VOTING_DELAY_BLOCKS, 'NounsDAO::_setVotingDelay: invalid voting delay' ); - uint256 oldVotingDelay = ds.votingDelay; - ds.votingDelay = newVotingDelay; + uint256 oldVotingDelay = ds().votingDelay; + ds().votingDelay = newVotingDelay; emit VotingDelaySet(oldVotingDelay, newVotingDelay); } @@ -174,13 +174,13 @@ library NounsDAOV3Admin { * @notice Admin function for setting the voting period * @param newVotingPeriod new voting period, in blocks */ - function _setVotingPeriod(NounsDAOStorageV3.StorageV3 storage ds, uint256 newVotingPeriod) external onlyAdmin(ds) { + function _setVotingPeriod(uint256 newVotingPeriod) external onlyAdmin { require( newVotingPeriod >= MIN_VOTING_PERIOD_BLOCKS && newVotingPeriod <= MAX_VOTING_PERIOD_BLOCKS, 'NounsDAO::_setVotingPeriod: invalid voting period' ); - uint256 oldVotingPeriod = ds.votingPeriod; - ds.votingPeriod = newVotingPeriod; + uint256 oldVotingPeriod = ds().votingPeriod; + ds().votingPeriod = newVotingPeriod; emit VotingPeriodSet(oldVotingPeriod, newVotingPeriod); } @@ -190,17 +190,14 @@ library NounsDAOV3Admin { * @dev newProposalThresholdBPS must be in [`MIN_PROPOSAL_THRESHOLD_BPS`,`MAX_PROPOSAL_THRESHOLD_BPS`] * @param newProposalThresholdBPS new proposal threshold */ - function _setProposalThresholdBPS(NounsDAOStorageV3.StorageV3 storage ds, uint256 newProposalThresholdBPS) - external - onlyAdmin(ds) - { + function _setProposalThresholdBPS(uint256 newProposalThresholdBPS) external onlyAdmin { require( newProposalThresholdBPS >= MIN_PROPOSAL_THRESHOLD_BPS && newProposalThresholdBPS <= MAX_PROPOSAL_THRESHOLD_BPS, 'NounsDAO::_setProposalThreshold: invalid proposal threshold bps' ); - uint256 oldProposalThresholdBPS = ds.proposalThresholdBPS; - ds.proposalThresholdBPS = newProposalThresholdBPS; + uint256 oldProposalThresholdBPS = ds().proposalThresholdBPS; + ds().proposalThresholdBPS = newProposalThresholdBPS; emit ProposalThresholdBPSSet(oldProposalThresholdBPS, newProposalThresholdBPS); } @@ -209,29 +206,29 @@ library NounsDAOV3Admin { * @notice Admin function for setting the objection period duration * @param newObjectionPeriodDurationInBlocks new objection period duration, in blocks */ - function _setObjectionPeriodDurationInBlocks( - NounsDAOStorageV3.StorageV3 storage ds, - uint32 newObjectionPeriodDurationInBlocks - ) external onlyAdmin(ds) { + function _setObjectionPeriodDurationInBlocks(uint32 newObjectionPeriodDurationInBlocks) external onlyAdmin { if (newObjectionPeriodDurationInBlocks > MAX_OBJECTION_PERIOD_BLOCKS) revert InvalidObjectionPeriodDurationInBlocks(); - uint32 oldObjectionPeriodDurationInBlocks = ds.objectionPeriodDurationInBlocks; - ds.objectionPeriodDurationInBlocks = newObjectionPeriodDurationInBlocks; + uint32 oldObjectionPeriodDurationInBlocks = ds().objectionPeriodDurationInBlocks; + ds().objectionPeriodDurationInBlocks = newObjectionPeriodDurationInBlocks; emit ObjectionPeriodDurationSet(oldObjectionPeriodDurationInBlocks, newObjectionPeriodDurationInBlocks); } + function ds() internal pure returns (NounsDAOStorageV3.StorageV3 storage ds_) { + assembly { + ds_.slot := 0 + } + } + /** * @notice Admin function for setting the objection period last minute window * @param newLastMinuteWindowInBlocks new objection period last minute window, in blocks */ - function _setLastMinuteWindowInBlocks(NounsDAOStorageV3.StorageV3 storage ds, uint32 newLastMinuteWindowInBlocks) - external - onlyAdmin(ds) - { - uint32 oldLastMinuteWindowInBlocks = ds.lastMinuteWindowInBlocks; - ds.lastMinuteWindowInBlocks = newLastMinuteWindowInBlocks; + function _setLastMinuteWindowInBlocks(uint32 newLastMinuteWindowInBlocks) external onlyAdmin { + uint32 oldLastMinuteWindowInBlocks = ds().lastMinuteWindowInBlocks; + ds().lastMinuteWindowInBlocks = newLastMinuteWindowInBlocks; emit LastMinuteWindowSet(oldLastMinuteWindowInBlocks, newLastMinuteWindowInBlocks); } @@ -240,15 +237,12 @@ library NounsDAOV3Admin { * @notice Admin function for setting the proposal updatable period * @param newProposalUpdatablePeriodInBlocks the new proposal updatable period, in blocks */ - function _setProposalUpdatablePeriodInBlocks( - NounsDAOStorageV3.StorageV3 storage ds, - uint32 newProposalUpdatablePeriodInBlocks - ) external onlyAdmin(ds) { + function _setProposalUpdatablePeriodInBlocks(uint32 newProposalUpdatablePeriodInBlocks) external onlyAdmin { if (newProposalUpdatablePeriodInBlocks > MAX_UPDATABLE_PERIOD_BLOCKS) revert InvalidProposalUpdatablePeriodInBlocks(); - uint32 oldProposalUpdatablePeriodInBlocks = ds.proposalUpdatablePeriodInBlocks; - ds.proposalUpdatablePeriodInBlocks = newProposalUpdatablePeriodInBlocks; + uint32 oldProposalUpdatablePeriodInBlocks = ds().proposalUpdatablePeriodInBlocks; + ds().proposalUpdatablePeriodInBlocks = newProposalUpdatablePeriodInBlocks; emit ProposalUpdatablePeriodSet(oldProposalUpdatablePeriodInBlocks, newProposalUpdatablePeriodInBlocks); } @@ -258,12 +252,12 @@ library NounsDAOV3Admin { * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. * @param newPendingAdmin New pending admin. */ - function _setPendingAdmin(NounsDAOStorageV3.StorageV3 storage ds, address newPendingAdmin) external onlyAdmin(ds) { + function _setPendingAdmin(address newPendingAdmin) external onlyAdmin { // Save current value, if any, for inclusion in log - address oldPendingAdmin = ds.pendingAdmin; + address oldPendingAdmin = ds().pendingAdmin; // Store pendingAdmin with value newPendingAdmin - ds.pendingAdmin = newPendingAdmin; + ds().pendingAdmin = newPendingAdmin; // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin) emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin); @@ -273,24 +267,24 @@ library NounsDAOV3Admin { * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin * @dev Admin function for pending admin to accept role and update admin */ - function _acceptAdmin(NounsDAOStorageV3.StorageV3 storage ds) external { + function _acceptAdmin() external { // Check caller is pendingAdmin and pendingAdmin ≠ address(0) require( - msg.sender == ds.pendingAdmin && msg.sender != address(0), + msg.sender == ds().pendingAdmin && msg.sender != address(0), 'NounsDAO::_acceptAdmin: pending admin only' ); // Save current values for inclusion in log - address oldAdmin = ds.admin; - address oldPendingAdmin = ds.pendingAdmin; + address oldAdmin = ds().admin; + address oldPendingAdmin = ds().pendingAdmin; // Store admin with value pendingAdmin - ds.admin = ds.pendingAdmin; + ds().admin = ds().pendingAdmin; // Clear the pending value - ds.pendingAdmin = address(0); + ds().pendingAdmin = address(0); - emit NewAdmin(oldAdmin, ds.admin); + emit NewAdmin(oldAdmin, ds().admin); emit NewPendingAdmin(oldPendingAdmin, address(0)); } @@ -298,48 +292,48 @@ library NounsDAOV3Admin { * @notice Begins transition of vetoer. The newPendingVetoer must call _acceptVetoer to finalize the transfer. * @param newPendingVetoer New Pending Vetoer */ - function _setPendingVetoer(NounsDAOStorageV3.StorageV3 storage ds, address newPendingVetoer) public { - if (msg.sender != ds.vetoer) { + function _setPendingVetoer(address newPendingVetoer) public { + if (msg.sender != ds().vetoer) { revert VetoerOnly(); } - emit NewPendingVetoer(ds.pendingVetoer, newPendingVetoer); + emit NewPendingVetoer(ds().pendingVetoer, newPendingVetoer); - ds.pendingVetoer = newPendingVetoer; + ds().pendingVetoer = newPendingVetoer; } /** * @notice Called by the pendingVetoer to accept role and update vetoer */ - function _acceptVetoer(NounsDAOStorageV3.StorageV3 storage ds) external { - if (msg.sender != ds.pendingVetoer) { + function _acceptVetoer() external { + if (msg.sender != ds().pendingVetoer) { revert PendingVetoerOnly(); } // Update vetoer - emit NewVetoer(ds.vetoer, ds.pendingVetoer); - ds.vetoer = ds.pendingVetoer; + emit NewVetoer(ds().vetoer, ds().pendingVetoer); + ds().vetoer = ds().pendingVetoer; // Clear the pending value - emit NewPendingVetoer(ds.pendingVetoer, address(0)); - ds.pendingVetoer = address(0); + emit NewPendingVetoer(ds().pendingVetoer, address(0)); + ds().pendingVetoer = address(0); } /** * @notice Burns veto priviledges * @dev Vetoer function destroying veto power forever */ - function _burnVetoPower(NounsDAOStorageV3.StorageV3 storage ds) public { + function _burnVetoPower() public { // Check caller is vetoer - require(msg.sender == ds.vetoer, 'NounsDAO::_burnVetoPower: vetoer only'); + require(msg.sender == ds().vetoer, 'NounsDAO::_burnVetoPower: vetoer only'); // Update vetoer to 0x0 - emit NewVetoer(ds.vetoer, address(0)); - ds.vetoer = address(0); + emit NewVetoer(ds().vetoer, address(0)); + ds().vetoer = address(0); // Clear the pending value - emit NewPendingVetoer(ds.pendingVetoer, address(0)); - ds.pendingVetoer = address(0); + emit NewPendingVetoer(ds().pendingVetoer, address(0)); + ds().pendingVetoer = address(0); } /** @@ -348,11 +342,8 @@ library NounsDAOV3Admin { * Must be between `MIN_QUORUM_VOTES_BPS_LOWER_BOUND` and `MIN_QUORUM_VOTES_BPS_UPPER_BOUND` * Must be lower than or equal to maxQuorumVotesBPS */ - function _setMinQuorumVotesBPS(NounsDAOStorageV3.StorageV3 storage ds, uint16 newMinQuorumVotesBPS) - external - onlyAdmin(ds) - { - NounsDAOStorageV3.DynamicQuorumParams memory params = ds.getDynamicQuorumParamsAt(block.number); + function _setMinQuorumVotesBPS(uint16 newMinQuorumVotesBPS) external onlyAdmin { + NounsDAOStorageV3.DynamicQuorumParams memory params = ds().getDynamicQuorumParamsAt(block.number); require( newMinQuorumVotesBPS >= MIN_QUORUM_VOTES_BPS_LOWER_BOUND && @@ -367,7 +358,7 @@ library NounsDAOV3Admin { uint16 oldMinQuorumVotesBPS = params.minQuorumVotesBPS; params.minQuorumVotesBPS = newMinQuorumVotesBPS; - _writeQuorumParamsCheckpoint(ds, params); + _writeQuorumParamsCheckpoint(params); emit MinQuorumVotesBPSSet(oldMinQuorumVotesBPS, newMinQuorumVotesBPS); } @@ -378,11 +369,8 @@ library NounsDAOV3Admin { * Must be lower than `MAX_QUORUM_VOTES_BPS_UPPER_BOUND` * Must be higher than or equal to minQuorumVotesBPS */ - function _setMaxQuorumVotesBPS(NounsDAOStorageV3.StorageV3 storage ds, uint16 newMaxQuorumVotesBPS) - external - onlyAdmin(ds) - { - NounsDAOStorageV3.DynamicQuorumParams memory params = ds.getDynamicQuorumParamsAt(block.number); + function _setMaxQuorumVotesBPS(uint16 newMaxQuorumVotesBPS) external onlyAdmin { + NounsDAOStorageV3.DynamicQuorumParams memory params = ds().getDynamicQuorumParamsAt(block.number); require( newMaxQuorumVotesBPS <= MAX_QUORUM_VOTES_BPS_UPPER_BOUND, @@ -396,7 +384,7 @@ library NounsDAOV3Admin { uint16 oldMaxQuorumVotesBPS = params.maxQuorumVotesBPS; params.maxQuorumVotesBPS = newMaxQuorumVotesBPS; - _writeQuorumParamsCheckpoint(ds, params); + _writeQuorumParamsCheckpoint(params); emit MaxQuorumVotesBPSSet(oldMaxQuorumVotesBPS, newMaxQuorumVotesBPS); } @@ -405,16 +393,13 @@ library NounsDAOV3Admin { * @notice Admin function for setting the dynamic quorum coefficient * @param newQuorumCoefficient the new coefficient, as a fixed point integer with 6 decimals */ - function _setQuorumCoefficient(NounsDAOStorageV3.StorageV3 storage ds, uint32 newQuorumCoefficient) - external - onlyAdmin(ds) - { - NounsDAOStorageV3.DynamicQuorumParams memory params = ds.getDynamicQuorumParamsAt(block.number); + function _setQuorumCoefficient(uint32 newQuorumCoefficient) external onlyAdmin { + NounsDAOStorageV3.DynamicQuorumParams memory params = ds().getDynamicQuorumParamsAt(block.number); uint32 oldQuorumCoefficient = params.quorumCoefficient; params.quorumCoefficient = newQuorumCoefficient; - _writeQuorumParamsCheckpoint(ds, params); + _writeQuorumParamsCheckpoint(params); emit QuorumCoefficientSet(oldQuorumCoefficient, newQuorumCoefficient); } @@ -430,11 +415,10 @@ library NounsDAOV3Admin { * @param newQuorumCoefficient the new coefficient, as a fixed point integer with 6 decimals */ function _setDynamicQuorumParams( - NounsDAOStorageV3.StorageV3 storage ds, uint16 newMinQuorumVotesBPS, uint16 newMaxQuorumVotesBPS, uint32 newQuorumCoefficient - ) public onlyAdmin(ds) { + ) public onlyAdmin { if ( newMinQuorumVotesBPS < MIN_QUORUM_VOTES_BPS_LOWER_BOUND || newMinQuorumVotesBPS > MIN_QUORUM_VOTES_BPS_UPPER_BOUND @@ -448,14 +432,14 @@ library NounsDAOV3Admin { revert MinQuorumBPSGreaterThanMaxQuorumBPS(); } - NounsDAOStorageV3.DynamicQuorumParams memory oldParams = ds.getDynamicQuorumParamsAt(block.number); + NounsDAOStorageV3.DynamicQuorumParams memory oldParams = ds().getDynamicQuorumParamsAt(block.number); NounsDAOStorageV3.DynamicQuorumParams memory params = NounsDAOStorageV3.DynamicQuorumParams({ minQuorumVotesBPS: newMinQuorumVotesBPS, maxQuorumVotesBPS: newMaxQuorumVotesBPS, quorumCoefficient: newQuorumCoefficient }); - _writeQuorumParamsCheckpoint(ds, params); + _writeQuorumParamsCheckpoint(params); emit MinQuorumVotesBPSSet(oldParams.minQuorumVotesBPS, params.minQuorumVotesBPS); emit MaxQuorumVotesBPSSet(oldParams.maxQuorumVotesBPS, params.maxQuorumVotesBPS); @@ -465,7 +449,7 @@ library NounsDAOV3Admin { /** * @notice Withdraws all the ETH in the contract. This is callable only by the admin (timelock). */ - function _withdraw(NounsDAOStorageV3.StorageV3 storage ds) external onlyAdmin(ds) returns (uint256, bool) { + function _withdraw() external onlyAdmin returns (uint256, bool) { uint256 amount = address(this).balance; (bool sent, ) = msg.sender.call{ value: amount }(''); @@ -479,14 +463,14 @@ library NounsDAOV3Admin { * instead of the proposal creation block. * Sets it to the next proposal id. */ - function _setVoteSnapshotBlockSwitchProposalId(NounsDAOStorageV3.StorageV3 storage ds) external onlyAdmin(ds) { - uint256 oldVoteSnapshotBlockSwitchProposalId = ds.voteSnapshotBlockSwitchProposalId; + function _setVoteSnapshotBlockSwitchProposalId() external onlyAdmin { + uint256 oldVoteSnapshotBlockSwitchProposalId = ds().voteSnapshotBlockSwitchProposalId; if (oldVoteSnapshotBlockSwitchProposalId > 0) { revert VoteSnapshotSwitchAlreadySet(); } - uint256 newVoteSnapshotBlockSwitchProposalId = ds.proposalCount + 1; - ds.voteSnapshotBlockSwitchProposalId = newVoteSnapshotBlockSwitchProposalId; + uint256 newVoteSnapshotBlockSwitchProposalId = ds().proposalCount + 1; + ds().voteSnapshotBlockSwitchProposalId = newVoteSnapshotBlockSwitchProposalId; emit VoteSnapshotBlockSwitchProposalIdSet( oldVoteSnapshotBlockSwitchProposalId, @@ -497,12 +481,9 @@ library NounsDAOV3Admin { /** * @notice Admin function for setting the fork DAO deployer contract */ - function _setForkDAODeployer(NounsDAOStorageV3.StorageV3 storage ds, address newForkDAODeployer) - external - onlyAdmin(ds) - { - address oldForkDAODeployer = address(ds.forkDAODeployer); - ds.forkDAODeployer = IForkDAODeployer(newForkDAODeployer); + function _setForkDAODeployer(address newForkDAODeployer) public onlyAdmin { + address oldForkDAODeployer = address(ds().forkDAODeployer); + ds().forkDAODeployer = IForkDAODeployer(newForkDAODeployer); emit ForkDAODeployerSet(oldForkDAODeployer, newForkDAODeployer); } @@ -510,27 +491,24 @@ library NounsDAOV3Admin { /** * @notice Admin function for setting the ERC20 tokens that are used when splitting funds to a fork */ - function _setErc20TokensToIncludeInFork(NounsDAOStorageV3.StorageV3 storage ds, address[] calldata erc20tokens) - external - onlyAdmin(ds) - { + function _setErc20TokensToIncludeInFork(address[] calldata erc20tokens) public onlyAdmin { checkForDuplicates(erc20tokens); - emit ERC20TokensToIncludeInForkSet(ds.erc20TokensToIncludeInFork, erc20tokens); + emit ERC20TokensToIncludeInForkSet(ds().erc20TokensToIncludeInFork, erc20tokens); - ds.erc20TokensToIncludeInFork = erc20tokens; + ds().erc20TokensToIncludeInFork = erc20tokens; } /** * @notice Admin function for setting the fork escrow contract */ - function _setForkEscrow(NounsDAOStorageV3.StorageV3 storage ds, address newForkEscrow) external onlyAdmin(ds) { - emit ForkEscrowSet(address(ds.forkEscrow), newForkEscrow); + function _setForkEscrow(address newForkEscrow) public onlyAdmin { + emit ForkEscrowSet(address(ds().forkEscrow), newForkEscrow); - ds.forkEscrow = INounsDAOForkEscrow(newForkEscrow); + ds().forkEscrow = INounsDAOForkEscrow(newForkEscrow); } - function _setForkPeriod(NounsDAOStorageV3.StorageV3 storage ds, uint256 newForkPeriod) external onlyAdmin(ds) { + function _setForkPeriod(uint256 newForkPeriod) public onlyAdmin { if (newForkPeriod > MAX_FORK_PERIOD) { revert ForkPeriodTooLong(); } @@ -539,22 +517,41 @@ library NounsDAOV3Admin { revert ForkPeriodTooShort(); } - emit ForkPeriodSet(ds.forkPeriod, newForkPeriod); + emit ForkPeriodSet(ds().forkPeriod, newForkPeriod); - ds.forkPeriod = newForkPeriod; + ds().forkPeriod = newForkPeriod; } /** * @notice Admin function for setting the fork threshold * @param newForkThresholdBPS the new fork proposal threshold, in basis points */ - function _setForkThresholdBPS(NounsDAOStorageV3.StorageV3 storage ds, uint256 newForkThresholdBPS) - external - onlyAdmin(ds) - { - emit ForkThresholdSet(ds.forkThresholdBPS, newForkThresholdBPS); + function _setForkThresholdBPS(uint256 newForkThresholdBPS) public onlyAdmin { + emit ForkThresholdSet(ds().forkThresholdBPS, newForkThresholdBPS); - ds.forkThresholdBPS = newForkThresholdBPS; + ds().forkThresholdBPS = newForkThresholdBPS; + } + + /** + * @notice Admin function for setting the fork related parameters + * @param forkEscrow_ the fork escrow contract + * @param forkDAODeployer_ the fork dao deployer contract + * @param erc20TokensToIncludeInFork_ the ERC20 tokens used when splitting funds to a fork + * @param forkPeriod_ the period during which it's possible to join a fork after exeuction + * @param forkThresholdBPS_ the threshold required of escrowed nouns in order to execute a fork + */ + function _setForkParams( + address forkEscrow_, + address forkDAODeployer_, + address[] calldata erc20TokensToIncludeInFork_, + uint256 forkPeriod_, + uint256 forkThresholdBPS_ + ) external { + _setForkEscrow(forkEscrow_); + _setForkDAODeployer(forkDAODeployer_); + _setErc20TokensToIncludeInFork(erc20TokensToIncludeInFork_); + _setForkPeriod(forkPeriod_); + _setForkThresholdBPS(forkThresholdBPS_); } /** @@ -564,28 +561,24 @@ library NounsDAOV3Admin { * @param admin the new admin address */ function _setTimelocksAndAdmin( - NounsDAOStorageV3.StorageV3 storage ds, address timelock, address timelockV1, address admin - ) external onlyAdmin(ds) { - ds.timelock = INounsDAOExecutorV2(timelock); - ds.timelockV1 = INounsDAOExecutor(timelockV1); - ds.admin = admin; + ) external onlyAdmin { + ds().timelock = INounsDAOExecutorV2(timelock); + ds().timelockV1 = INounsDAOExecutor(timelockV1); + ds().admin = admin; emit TimelocksAndAdminSet(timelock, timelockV1, admin); } - function _writeQuorumParamsCheckpoint( - NounsDAOStorageV3.StorageV3 storage ds, - NounsDAOStorageV3.DynamicQuorumParams memory params - ) internal { + function _writeQuorumParamsCheckpoint(NounsDAOStorageV3.DynamicQuorumParams memory params) internal { uint32 blockNumber = safe32(block.number, 'block number exceeds 32 bits'); - uint256 pos = ds.quorumParamsCheckpoints.length; - if (pos > 0 && ds.quorumParamsCheckpoints[pos - 1].fromBlock == blockNumber) { - ds.quorumParamsCheckpoints[pos - 1].params = params; + uint256 pos = ds().quorumParamsCheckpoints.length; + if (pos > 0 && ds().quorumParamsCheckpoints[pos - 1].fromBlock == blockNumber) { + ds().quorumParamsCheckpoints[pos - 1].params = params; } else { - ds.quorumParamsCheckpoints.push( + ds().quorumParamsCheckpoints.push( NounsDAOStorageV3.DynamicQuorumParamsCheckpoint({ fromBlock: blockNumber, params: params }) ); } diff --git a/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol b/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol index 7de3a3f4c8..55d4fdfd21 100644 --- a/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol +++ b/packages/nouns-contracts/contracts/governance/NounsDAOV3Proposals.sol @@ -160,7 +160,8 @@ library NounsDAOV3Proposals { function propose( NounsDAOStorageV3.StorageV3 storage ds, ProposalTxs memory txs, - string memory description + string memory description, + uint16 client ) internal returns (uint256) { uint256 adjustedTotalSupply = ds.adjustedTotalSupply(); uint256 proposalThreshold_ = checkPropThreshold( @@ -177,7 +178,8 @@ library NounsDAOV3Proposals { proposalId, proposalThreshold_, adjustedTotalSupply, - txs + txs, + client ); ds.latestProposalIds[msg.sender] = proposalId; @@ -197,9 +199,10 @@ library NounsDAOV3Proposals { function proposeOnTimelockV1( NounsDAOStorageV3.StorageV3 storage ds, ProposalTxs memory txs, - string memory description + string memory description, + uint16 client ) internal returns (uint256) { - uint256 newProposalId = propose(ds, txs, description); + uint256 newProposalId = propose(ds, txs, description, client); NounsDAOStorageV3.Proposal storage newProposal = ds._proposals[newProposalId]; newProposal.executeOnTimelockV1 = true; @@ -209,6 +212,12 @@ library NounsDAOV3Proposals { return newProposalId; } + struct ProposalTemp { + uint256 proposalId; + uint256 adjustedTotalSupply; + uint256 propThreshold; + } + /** * @notice Function used to propose a new proposal. Sender and signers must have delegates above the proposal threshold * @param proposerSignatures Array of signers who have signed the proposal and their signatures. @@ -221,22 +230,24 @@ library NounsDAOV3Proposals { NounsDAOStorageV3.StorageV3 storage ds, NounsDAOStorageV3.ProposerSignature[] memory proposerSignatures, ProposalTxs memory txs, - string memory description + string memory description, + uint16 client ) external returns (uint256) { if (proposerSignatures.length == 0) revert MustProvideSignatures(); checkProposalTxs(txs); - uint256 proposalId = ds.proposalCount = ds.proposalCount + 1; - - uint256 adjustedTotalSupply = ds.adjustedTotalSupply(); - uint256 propThreshold = proposalThreshold(ds, adjustedTotalSupply); + ProposalTemp memory temp; + temp.proposalId = ds.proposalCount = ds.proposalCount + 1; + temp.adjustedTotalSupply = ds.adjustedTotalSupply(); + temp.propThreshold = proposalThreshold(ds, temp.adjustedTotalSupply); NounsDAOStorageV3.Proposal storage newProposal = createNewProposal( ds, - proposalId, - propThreshold, - adjustedTotalSupply, - txs + temp.proposalId, + temp.propThreshold, + temp.adjustedTotalSupply, + txs, + client ); // important that the proposal is created before the verification call in order to ensure @@ -246,16 +257,16 @@ library NounsDAOV3Proposals { proposerSignatures, txs, description, - proposalId + temp.proposalId ); if (signers.length == 0) revert MustProvideSignatures(); - if (votes <= propThreshold) revert VotesBelowProposalThreshold(); + if (votes <= temp.propThreshold) revert VotesBelowProposalThreshold(); newProposal.signers = signers; - emitNewPropEvents(newProposal, signers, ds.minQuorumVotes(adjustedTotalSupply), txs, description); + emitNewPropEvents(newProposal, signers, ds.minQuorumVotes(temp.adjustedTotalSupply), txs, description); - return proposalId; + return temp.proposalId; } /** @@ -482,16 +493,6 @@ library NounsDAOV3Proposals { executeInternal(ds, proposal, timelock); } - /** - * @notice Executes a queued proposal on timelockV1 if eta has passed - * This is only required for proposal that were queued on timelockV1, but before the upgrade to DAO V3. - * These proposals will not have the `executeOnTimelockV1` bool turned on. - */ - function executeOnTimelockV1(NounsDAOStorageV3.StorageV3 storage ds, uint256 proposalId) external { - NounsDAOStorageV3.Proposal storage proposal = ds._proposals[proposalId]; - executeInternal(ds, proposal, ds.timelockV1); - } - function executeInternal( NounsDAOStorageV3.StorageV3 storage ds, NounsDAOStorageV3.Proposal storage proposal, @@ -893,7 +894,8 @@ library NounsDAOV3Proposals { uint256 proposalId, uint256 proposalThreshold_, uint256 adjustedTotalSupply, - ProposalTxs memory txs + ProposalTxs memory txs, + uint16 client ) internal returns (NounsDAOStorageV3.Proposal storage newProposal) { uint64 updatePeriodEndBlock = SafeCast.toUint64(block.number + ds.proposalUpdatablePeriodInBlocks); uint256 startBlock = updatePeriodEndBlock + ds.votingDelay; @@ -912,6 +914,7 @@ library NounsDAOV3Proposals { newProposal.totalSupply = adjustedTotalSupply; newProposal.creationBlock = SafeCast.toUint64(block.number); newProposal.updatePeriodEndBlock = updatePeriodEndBlock; + newProposal.client = client; } function emitNewPropEvents( diff --git a/packages/nouns-contracts/contracts/interfaces/INounsDAOLogicV3.sol b/packages/nouns-contracts/contracts/interfaces/INounsDAOLogicV3.sol new file mode 100644 index 0000000000..59aa68fdc2 --- /dev/null +++ b/packages/nouns-contracts/contracts/interfaces/INounsDAOLogicV3.sol @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-3.0 + +/// @title Interface for Noun Auction Houses + +/********************************* + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ * + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ * + * ░░░░░░█████████░░█████████░░░ * + * ░░░░░░██░░░████░░██░░░████░░░ * + * ░░██████░░░████████░░░████░░░ * + * ░░██░░██░░░████░░██░░░████░░░ * + * ░░██░░██░░░████░░██░░░████░░░ * + * ░░░░░░█████████░░█████████░░░ * + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ * + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ * + *********************************/ + +pragma solidity ^0.8.6; + +import '../governance/NounsDAOInterfaces.sol'; + +interface INounsDAOLogicV3 { + /** + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + * PROPOSALS + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + */ + /** + * @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold + * @param targets Target addresses for proposal calls + * @param values Eth values for proposal calls + * @param signatures Function signatures for proposal calls + * @param calldatas Calldatas for proposal calls + * @param description String description of the proposal + * @return uint256 Proposal id of new proposal + */ + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); + + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description, + uint16 clientId + ) external returns (uint256); + + /** + * @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold. + * This proposal would be executed via the timelockV1 contract. This is meant to be used in case timelockV1 + * is still holding funds or has special permissions to execute on certain contracts. + * @param targets Target addresses for proposal calls + * @param values Eth values for proposal calls + * @param signatures Function signatures for proposal calls + * @param calldatas Calldatas for proposal calls + * @param description String description of the proposal + * @return uint256 Proposal id of new proposal + */ + function proposeOnTimelockV1( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); + + function proposeBySigs( + NounsDAOStorageV3.ProposerSignature[] memory proposerSignatures, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description, + uint16 clientId + ) external returns (uint256); + + /** + * @notice Function used to propose a new proposal. Sender and signers must have delegates above the proposal threshold + * Signers are regarded as co-proposers, and therefore have the ability to cancel the proposal at any time. + * @param proposerSignatures Array of signers who have signed the proposal and their signatures. + * @dev The signatures follow EIP-712. See `PROPOSAL_TYPEHASH` in NounsDAOV3Proposals.sol + * @param targets Target addresses for proposal calls + * @param values Eth values for proposal calls + * @param signatures Function signatures for proposal calls + * @param calldatas Calldatas for proposal calls + * @param description String description of the proposal + * @return uint256 Proposal id of new proposal + */ + function proposeBySigs( + NounsDAOStorageV3.ProposerSignature[] memory proposerSignatures, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); + + /** + * @notice Invalidates a signature that may be used for signing a new proposal. + * Once a signature is canceled, the sender can no longer use it again. + * If the sender changes their mind and want to sign the proposal, they can change the expiry timestamp + * in order to produce a new signature. + * The signature will only be invalidated when used by the sender. If used by a different account, it will + * not be invalidated. + * Cancelling a signature for an existing proposal will have no effect. Signers have the ability to cancel + * a proposal they signed if necessary. + * @param sig The signature to cancel + */ + function cancelSig(bytes calldata sig) external; + + /** + * @notice Update a proposal transactions and description. + * Only the proposer can update it, and only during the updateable period. + * @param proposalId Proposal's id + * @param targets Updated target addresses for proposal calls + * @param values Updated eth values for proposal calls + * @param signatures Updated function signatures for proposal calls + * @param calldatas Updated calldatas for proposal calls + * @param description Updated description of the proposal + * @param updateMessage Short message to explain the update + */ + function updateProposal( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description, + string memory updateMessage + ) external; + + /** + * @notice Updates the proposal's description. Only the proposer can update it, and only during the updateable period. + * @param proposalId Proposal's id + * @param description Updated description of the proposal + * @param updateMessage Short message to explain the update + */ + function updateProposalDescription( + uint256 proposalId, + string calldata description, + string calldata updateMessage + ) external; + + /** + * @notice Updates the proposal's transactions. Only the proposer can update it, and only during the updateable period. + * @param proposalId Proposal's id + * @param targets Updated target addresses for proposal calls + * @param values Updated eth values for proposal calls + * @param signatures Updated function signatures for proposal calls + * @param calldatas Updated calldatas for proposal calls + * @param updateMessage Short message to explain the update + */ + function updateProposalTransactions( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory updateMessage + ) external; + + /** + * @notice Update a proposal's transactions and description that was created with proposeBySigs. + * Only the proposer can update it, during the updateable period. + * Requires the original signers to sign the update. + * @param proposalId Proposal's id + * @param proposerSignatures Array of signers who have signed the proposal and their signatures. + * @dev The signatures follow EIP-712. See `UPDATE_PROPOSAL_TYPEHASH` in NounsDAOV3Proposals.sol + * @param targets Updated target addresses for proposal calls + * @param values Updated eth values for proposal calls + * @param signatures Updated function signatures for proposal calls + * @param calldatas Updated calldatas for proposal calls + * @param description Updated description of the proposal + * @param updateMessage Short message to explain the update + */ + function updateProposalBySigs( + uint256 proposalId, + NounsDAOStorageV3.ProposerSignature[] memory proposerSignatures, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description, + string memory updateMessage + ) external; + + /** + * @notice Queues a proposal of state succeeded + * @param proposalId The id of the proposal to queue + */ + function queue(uint256 proposalId) external; + + /** + * @notice Executes a queued proposal if eta has passed + * @param proposalId The id of the proposal to execute + */ + function execute(uint256 proposalId) external; + + /** + * @notice Cancels a proposal only if sender is the proposer or a signer, or proposer & signers voting power + * dropped below proposal threshold + * @param proposalId The id of the proposal to cancel + */ + function cancel(uint256 proposalId) external; + + /** + * @notice Gets the state of a proposal + * @param proposalId The id of the proposal + * @return Proposal state + */ + function state(uint256 proposalId) external view returns (NounsDAOStorageV3.ProposalState); + + /** + * @notice Gets actions of a proposal + * @param proposalId the id of the proposal + * @return targets + * @return values + * @return signatures + * @return calldatas + */ + function getActions(uint256 proposalId) + external + view + returns ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ); + + /** + * @notice Gets the receipt for a voter on a given proposal + * @param proposalId the id of proposal + * @param voter The address of the voter + * @return The voting receipt + */ + function getReceipt(uint256 proposalId, address voter) external view returns (NounsDAOStorageV3.Receipt memory); + + /** + * @notice Returns the proposal details given a proposal id. + * The `quorumVotes` member holds the *current* quorum, given the current votes. + * @param proposalId the proposal id to get the data for + * @return A `ProposalCondensed` struct with the proposal data, backwards compatible with V1 and V2 + */ + function proposals(uint256 proposalId) external view returns (NounsDAOStorageV2.ProposalCondensed memory); + + /** + * @notice Returns the proposal details given a proposal id. + * The `quorumVotes` member holds the *current* quorum, given the current votes. + * @param proposalId the proposal id to get the data for + * @return A `ProposalCondensed` struct with the proposal data, not backwards compatible as it contains additional values + * like `objectionPeriodEndBlock` and `signers` + */ + function proposalsV3(uint256 proposalId) external view returns (NounsDAOStorageV3.ProposalCondensed memory); + + /** + * @notice Current proposal threshold using Noun Total Supply + * Differs from `GovernerBravo` which uses fixed amount + */ + function proposalThreshold() external view returns (uint256); + + /** + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + * ADMIN + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + */ + + /** + * @notice Admin function for setting the voting delay. Best to set voting delay to at least a few days, to give + * voters time to make sense of proposals, e.g. 21,600 blocks which should be at least 3 days. + * @param newVotingDelay new voting delay, in blocks + */ + function _setVotingDelay(uint256 newVotingDelay) external; + + /** + * @notice Admin function for setting the voting period + * @param newVotingPeriod new voting period, in blocks + */ + function _setVotingPeriod(uint256 newVotingPeriod) external; + + /** + * @notice Admin function for setting the proposal threshold basis points + * @dev newProposalThresholdBPS must be in [`MIN_PROPOSAL_THRESHOLD_BPS`,`MAX_PROPOSAL_THRESHOLD_BPS`] + * @param newProposalThresholdBPS new proposal threshold + */ + function _setProposalThresholdBPS(uint256 newProposalThresholdBPS) external; + + /** + * @notice Admin function for setting the objection period duration + * @param newObjectionPeriodDurationInBlocks new objection period duration, in blocks + */ + function _setObjectionPeriodDurationInBlocks(uint32 newObjectionPeriodDurationInBlocks) external; + + /** + * @notice Admin function for setting the objection period last minute window + * @param newLastMinuteWindowInBlocks new objection period last minute window, in blocks + */ + function _setLastMinuteWindowInBlocks(uint32 newLastMinuteWindowInBlocks) external; + + /** + * @notice Admin function for setting the proposal updatable period + * @param newProposalUpdatablePeriodInBlocks the new proposal updatable period, in blocks + */ + function _setProposalUpdatablePeriodInBlocks(uint32 newProposalUpdatablePeriodInBlocks) external; + + /** + * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer. + * @param newPendingAdmin New pending admin. + */ + function _setPendingAdmin(address newPendingAdmin) external; + + /** + * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin + * @dev Admin function for pending admin to accept role and update admin + */ + function _acceptAdmin() external; + + /** + * @notice Begins transition of vetoer. The newPendingVetoer must call _acceptVetoer to finalize the transfer. + * @param newPendingVetoer New Pending Vetoer + */ + function _setPendingVetoer(address newPendingVetoer) external; + + /** + * @notice Called by the pendingVetoer to accept role and update vetoer + */ + function _acceptVetoer() external; + + /** + * @notice Burns veto priviledges + * @dev Vetoer function destroying veto power forever + */ + function _burnVetoPower() external; + + /** + * @notice Admin function for setting the minimum quorum votes bps + * @param newMinQuorumVotesBPS minimum quorum votes bps + * Must be between `MIN_QUORUM_VOTES_BPS_LOWER_BOUND` and `MIN_QUORUM_VOTES_BPS_UPPER_BOUND` + * Must be lower than or equal to maxQuorumVotesBPS + */ + function _setMinQuorumVotesBPS(uint16 newMinQuorumVotesBPS) external; + + /** + * @notice Admin function for setting the maximum quorum votes bps + * @param newMaxQuorumVotesBPS maximum quorum votes bps + * Must be lower than `MAX_QUORUM_VOTES_BPS_UPPER_BOUND` + * Must be higher than or equal to minQuorumVotesBPS + */ + function _setMaxQuorumVotesBPS(uint16 newMaxQuorumVotesBPS) external; + + /** + * @notice Admin function for setting the dynamic quorum coefficient + * @param newQuorumCoefficient the new coefficient, as a fixed point integer with 6 decimals + */ + function _setQuorumCoefficient(uint32 newQuorumCoefficient) external; + + /** + * @notice Admin function for setting all the dynamic quorum parameters + * @param newMinQuorumVotesBPS minimum quorum votes bps + * Must be between `MIN_QUORUM_VOTES_BPS_LOWER_BOUND` and `MIN_QUORUM_VOTES_BPS_UPPER_BOUND` + * Must be lower than or equal to maxQuorumVotesBPS + * @param newMaxQuorumVotesBPS maximum quorum votes bps + * Must be lower than `MAX_QUORUM_VOTES_BPS_UPPER_BOUND` + * Must be higher than or equal to minQuorumVotesBPS + * @param newQuorumCoefficient the new coefficient, as a fixed point integer with 6 decimals + */ + function _setDynamicQuorumParams( + uint16 newMinQuorumVotesBPS, + uint16 newMaxQuorumVotesBPS, + uint32 newQuorumCoefficient + ) external; + + /** + * @notice Withdraws all the ETH in the contract. This is callable only by the admin (timelock). + */ + function _withdraw() external returns (uint256, bool); + + /** + * @notice Admin function for setting the fork period + * @param newForkPeriod the new fork proposal period, in seconds + */ + function _setForkPeriod(uint256 newForkPeriod) external; + + /** + * @notice Admin function for setting the fork threshold + * @param newForkThresholdBPS the new fork proposal threshold, in basis points + */ + function _setForkThresholdBPS(uint256 newForkThresholdBPS) external; + + /** + * @notice Admin function for setting the proposal id at which vote snapshots start using the voting start block + * instead of the proposal creation block. + * Sets it to the next proposal id. + */ + function _setVoteSnapshotBlockSwitchProposalId() external; + + /** + * @notice Admin function for setting the fork DAO deployer contract + */ + function _setForkDAODeployer(address newForkDAODeployer) external; + + /** + * @notice Admin function for setting the ERC20 tokens that are used when splitting funds to a fork + */ + function _setErc20TokensToIncludeInFork(address[] calldata erc20tokens) external; + + /** + * @notice Admin function for setting the fork escrow contract + */ + function _setForkEscrow(address newForkEscrow) external; + + /** + * @notice Admin function for setting the fork related parameters + * @param forkEscrow_ the fork escrow contract + * @param forkDAODeployer_ the fork dao deployer contract + * @param erc20TokensToIncludeInFork_ the ERC20 tokens used when splitting funds to a fork + * @param forkPeriod_ the period during which it's possible to join a fork after exeuction + * @param forkThresholdBPS_ the threshold required of escrowed nouns in order to execute a fork + */ + function _setForkParams( + address forkEscrow_, + address forkDAODeployer_, + address[] calldata erc20TokensToIncludeInFork_, + uint256 forkPeriod_, + uint256 forkThresholdBPS_ + ) external; + + /** + * @notice Admin function for setting the timelocks and admin + * @param newTimelock the new timelock contract + * @param newTimelockV1 the new timelockV1 contract + * @param newAdmin the new admin address + */ + function _setTimelocksAndAdmin( + address newTimelock, + address newTimelockV1, + address newAdmin + ) external; + + /** + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + * DYNAMIC QUORUM + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + */ + /** + * @notice Quorum votes required for a specific proposal to succeed + * Differs from `GovernerBravo` which uses fixed amount + */ + function quorumVotes(uint256 proposalId) external view returns (uint256); + + /** + * @notice Calculates the required quorum of for-votes based on the amount of against-votes + * The more against-votes there are for a proposal, the higher the required quorum is. + * The quorum BPS is between `params.minQuorumVotesBPS` and params.maxQuorumVotesBPS. + * The additional quorum is calculated as: + * quorumCoefficient * againstVotesBPS + * @dev Note the coefficient is a fixed point integer with 6 decimals + * @param againstVotes Number of against-votes in the proposal + * @param adjustedTotalSupply_ The adjusted total supply of Nouns at the time of proposal creation + * @param params Configurable parameters for calculating the quorum based on againstVotes. See `DynamicQuorumParams` definition for additional details. + * @return quorumVotes The required quorum + */ + function dynamicQuorumVotes( + uint256 againstVotes, + uint256 adjustedTotalSupply_, + NounsDAOStorageV3.DynamicQuorumParams memory params + ) external pure returns (uint256); + + /** + * @notice returns the dynamic quorum parameters values at a certain block number + * @dev The checkpoints array must not be empty, and the block number must be higher than or equal to + * the block of the first checkpoint + * @param blockNumber_ the block number to get the params at + * @return The dynamic quorum parameters that were set at the given block number + */ + function getDynamicQuorumParamsAt(uint256 blockNumber_) + external + view + returns (NounsDAOStorageV3.DynamicQuorumParams memory); + + /** + * @notice Current min quorum votes using Nouns adjusted total supply + */ + function minQuorumVotes() external view returns (uint256); + + /** + * @notice Current max quorum votes using Nouns adjusted total supply + */ + function maxQuorumVotes() external view returns (uint256); + + /** + * @notice Get all quorum params checkpoints + */ + function quorumParamsCheckpoints() external view returns (NounsDAOStorageV3.DynamicQuorumParamsCheckpoint[] memory); + + /** + * @notice Get a quorum params checkpoint by its index + */ + function quorumParamsCheckpoints(uint256 index) + external + view + returns (NounsDAOStorageV3.DynamicQuorumParamsCheckpoint memory); + + /** + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + * DAO FORK + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + */ + + /** + * @notice Escrow Nouns to contribute to the fork threshold + * @dev Requires approving the tokenIds or the entire noun token to the DAO contract + * @param tokenIds the tokenIds to escrow. They will be sent to the DAO once the fork threshold is reached and the escrow is closed. + * @param proposalIds array of proposal ids which are the reason for wanting to fork. This will only be used to emit event. + * @param reason the reason for want to fork. This will only be used to emit event. + */ + function escrowToFork( + uint256[] calldata tokenIds, + uint256[] calldata proposalIds, + string calldata reason + ) external; + + /** + * @notice Withdraw Nouns from the fork escrow. Only possible if the fork has not been executed. + * Only allowed to withdraw tokens that the sender has escrowed. + * @param tokenIds the tokenIds to withdraw + */ + function withdrawFromForkEscrow(uint256[] calldata tokenIds) external; + + /** + * @notice Execute the fork. Only possible if the fork threshold has been met. + * This will deploy a new DAO and send part of the treasury to the new DAO's treasury. + * This will also close the active escrow and all nouns in the escrow belong to the original DAO. + * @return forkTreasury The address of the new DAO's treasury + * @return forkToken The address of the new DAO's token + */ + function executeFork() external returns (address forkTreasury, address forkToken); + + /** + * @notice Joins a fork while a fork is active + * @param tokenIds the tokenIds to send to the DAO in exchange for joining the fork + * @param proposalIds array of proposal ids which are the reason for wanting to fork. This will only be used to emit event. + * @param reason the reason for want to fork. This will only be used to emit event. + */ + function joinFork( + uint256[] calldata tokenIds, + uint256[] calldata proposalIds, + string calldata reason + ) external; + + /** + * @notice Withdraws nouns from the fork escrow to the treasury after the fork has been executed + * @dev Only the DAO can call this function + * @param tokenIds the tokenIds to withdraw + */ + function withdrawDAONounsFromEscrowToTreasury(uint256[] calldata tokenIds) external; + + /** + * @notice Withdraws nouns from the fork escrow after the fork has been executed to an address other than the treasury + * @dev Only the DAO can call this function + * @param tokenIds the tokenIds to withdraw + * @param to the address to send the nouns to + */ + function withdrawDAONounsFromEscrowIncreasingTotalSupply(uint256[] calldata tokenIds, address to) external; + + /** + * @notice Returns the number of nouns in supply minus nouns owned by the DAO, i.e. held in the treasury or in an + * escrow after it has closed. + * This is used when calculating proposal threshold, quorum, fork threshold & treasury split. + */ + function adjustedTotalSupply() external view returns (uint256); + + /** + * @notice returns the required number of tokens to escrow to trigger a fork + */ + function forkThreshold() external view returns (uint256); + + /** + * @notice Returns the number of tokens currently in escrow, contributing to the fork threshold + */ + function numTokensInForkEscrow() external view returns (uint256); + + /** + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + * VOTES + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + */ + + /** + * @notice Vetoes a proposal only if sender is the vetoer and the proposal has not been executed. + * @param proposalId The id of the proposal to veto + */ + function veto(uint256 proposalId) external; + + /** + * @notice Cast a vote for a proposal + * @param proposalId The id of the proposal to vote on + * @param support The support value for the vote. 0=against, 1=for, 2=abstain + */ + function castVote(uint256 proposalId, uint8 support) external; + + /** + * @notice Cast a vote for a proposal, asking the DAO to refund gas costs. + * Users with > 0 votes receive refunds. Refunds are partial when using a gas priority fee higher than the DAO's cap. + * Refunds are partial when the DAO's balance is insufficient. + * No refund is sent when the DAO's balance is empty. No refund is sent to users with no votes. + * Voting takes place regardless of refund success. + * @param proposalId The id of the proposal to vote on + * @param support The support value for the vote. 0=against, 1=for, 2=abstain + * @dev Reentrancy is defended against in `castVoteInternal` at the `receipt.hasVoted == false` require statement. + */ + function castRefundableVote(uint256 proposalId, uint8 support) external; + + /** + * @notice Cast a vote for a proposal, asking the DAO to refund gas costs. + * Users with > 0 votes receive refunds. Refunds are partial when using a gas priority fee higher than the DAO's cap. + * Refunds are partial when the DAO's balance is insufficient. + * No refund is sent when the DAO's balance is empty. No refund is sent to users with no votes. + * Voting takes place regardless of refund success. + * @param proposalId The id of the proposal to vote on + * @param support The support value for the vote. 0=against, 1=for, 2=abstain + * @param reason The reason given for the vote by the voter + * @dev Reentrancy is defended against in `castVoteInternal` at the `receipt.hasVoted == false` require statement. + */ + function castRefundableVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) external; + + /** + * @notice Cast a vote for a proposal with a reason + * @param proposalId The id of the proposal to vote on + * @param support The support value for the vote. 0=against, 1=for, 2=abstain + * @param reason The reason given for the vote by the voter + */ + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) external; + + /** + * @notice Cast a vote for a proposal by signature + * @dev External function that accepts EIP-712 signatures for voting on proposals. + */ + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + /** + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + * STATE VARIABLE GETTERS + * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ + */ + function vetoer() external view returns (address); + + function pendingVetoer() external view returns (address); + + function votingDelay() external view returns (uint256); + + function votingPeriod() external view returns (uint256); + + function proposalThresholdBPS() external view returns (uint256); + + function quorumVotesBPS() external view returns (uint256); + + function proposalCount() external view returns (uint256); + + function timelock() external view returns (INounsDAOExecutor); + + function nouns() external view returns (NounsTokenLike); + + function latestProposalIds(address account) external view returns (uint256); + + function lastMinuteWindowInBlocks() external view returns (uint256); + + function objectionPeriodDurationInBlocks() external view returns (uint256); + + function erc20TokensToIncludeInFork() external view returns (address[] memory); + + function forkEscrow() external view returns (INounsDAOForkEscrow); + + function forkDAODeployer() external view returns (IForkDAODeployer); + + function forkEndTimestamp() external view returns (uint256); + + function forkPeriod() external view returns (uint256); + + function forkThresholdBPS() external view returns (uint256); + + function proposalUpdatablePeriodInBlocks() external view returns (uint256); + + function timelockV1() external view returns (address); + + function voteSnapshotBlockSwitchProposalId() external view returns (uint256); +} diff --git a/packages/nouns-contracts/contracts/test/NounsDAOLogicV3Harness.sol b/packages/nouns-contracts/contracts/test/NounsDAOLogicV3Harness.sol index 42a7901a45..435dde8ecf 100644 --- a/packages/nouns-contracts/contracts/test/NounsDAOLogicV3Harness.sol +++ b/packages/nouns-contracts/contracts/test/NounsDAOLogicV3Harness.sol @@ -34,13 +34,13 @@ contract NounsDAOLogicV3Harness is NounsDAOLogicV3 { ds.forkEscrow = INounsDAOForkEscrow(forkEscrow_); ds.forkDAODeployer = IForkDAODeployer(forkDAODeployer_); ds.vetoer = vetoer_; - _setDynamicQuorumParams( + NounsDAOV3Admin._setDynamicQuorumParams( dynamicQuorumParams_.minQuorumVotesBPS, dynamicQuorumParams_.maxQuorumVotesBPS, dynamicQuorumParams_.quorumCoefficient ); - ds._setLastMinuteWindowInBlocks(daoParams_.lastMinuteWindowInBlocks); - ds._setObjectionPeriodDurationInBlocks(daoParams_.objectionPeriodDurationInBlocks); - ds._setProposalUpdatablePeriodInBlocks(daoParams_.proposalUpdatablePeriodInBlocks); + NounsDAOV3Admin._setLastMinuteWindowInBlocks(daoParams_.lastMinuteWindowInBlocks); + NounsDAOV3Admin._setObjectionPeriodDurationInBlocks(daoParams_.objectionPeriodDurationInBlocks); + NounsDAOV3Admin._setProposalUpdatablePeriodInBlocks(daoParams_.proposalUpdatablePeriodInBlocks); } } diff --git a/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/NounsDAOLogicV3BaseTest.sol b/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/NounsDAOLogicV3BaseTest.sol index 0aba1f9931..2fdc5449a3 100644 --- a/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/NounsDAOLogicV3BaseTest.sol +++ b/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/NounsDAOLogicV3BaseTest.sol @@ -14,6 +14,7 @@ import { NounsSeeder } from '../../../contracts/NounsSeeder.sol'; import { IProxyRegistry } from '../../../contracts/external/opensea/IProxyRegistry.sol'; import { NounsDAOExecutorV2 } from '../../../contracts/governance/NounsDAOExecutorV2.sol'; import { NounsDAOForkEscrow } from '../../../contracts/governance/fork/NounsDAOForkEscrow.sol'; +import { INounsDAOLogicV3 } from '../../../contracts/interfaces/INounsDAOLogicV3.sol'; abstract contract NounsDAOLogicV3BaseTest is Test, DeployUtilsV3, SigUtils { event ProposalUpdated( @@ -73,7 +74,7 @@ abstract contract NounsDAOLogicV3BaseTest is Test, DeployUtilsV3, SigUtils { ); NounsToken nounsToken; - NounsDAOLogicV3 dao; + INounsDAOLogicV3 dao; NounsDAOExecutorV2 timelock; address noundersDAO = makeAddr('nounders'); diff --git a/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/NounsDAOV3Admin.t.sol b/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/NounsDAOV3Admin.t.sol index e7eea7275a..cc6ca9bdf0 100644 --- a/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/NounsDAOV3Admin.t.sol +++ b/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/NounsDAOV3Admin.t.sol @@ -5,6 +5,7 @@ import 'forge-std/Test.sol'; import { NounsDAOLogicV3BaseTest } from './NounsDAOLogicV3BaseTest.sol'; import { NounsDAOV3Admin } from '../../../contracts/governance/NounsDAOV3Admin.sol'; import { NounsDAOProxy } from '../../../contracts/governance/NounsDAOProxy.sol'; +import { INounsDAOLogicV3 } from '../../../contracts/interfaces/INounsDAOLogicV3.sol'; contract NounsDAOLogicV3AdminTest is NounsDAOLogicV3BaseTest { event ForkPeriodSet(uint256 oldForkPeriod, uint256 newForkPeriod); @@ -160,7 +161,7 @@ contract NounsDAOLogicV3AdminTest is NounsDAOLogicV3BaseTest { function test_setObjectionPeriodDurationInBlocks_onlyAdmin() public { vm.expectRevert(NounsDAOV3Admin.AdminOnly.selector); - dao._setObjectionPeriodDurationInBlocks(3 days / 12); + INounsDAOLogicV3(address(dao))._setObjectionPeriodDurationInBlocks(3 days / 12); } function test_setObjectionPeriodDurationInBlocks_worksForAdmin() public { @@ -169,7 +170,7 @@ contract NounsDAOLogicV3AdminTest is NounsDAOLogicV3BaseTest { emit ObjectionPeriodDurationSet(10, blocks); vm.prank(address(dao.timelock())); - dao._setObjectionPeriodDurationInBlocks(blocks); + INounsDAOLogicV3(address(dao))._setObjectionPeriodDurationInBlocks(blocks); assertEq(dao.objectionPeriodDurationInBlocks(), blocks); } @@ -179,7 +180,7 @@ contract NounsDAOLogicV3AdminTest is NounsDAOLogicV3BaseTest { vm.prank(address(dao.timelock())); vm.expectRevert(NounsDAOV3Admin.InvalidObjectionPeriodDurationInBlocks.selector); - dao._setObjectionPeriodDurationInBlocks(blocks); + INounsDAOLogicV3(address(dao))._setObjectionPeriodDurationInBlocks(blocks); } function test_setProposalUpdatablePeriodInBlocks_onlyAdmin() public { diff --git a/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/UpgradeToDAOV3.t.sol b/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/UpgradeToDAOV3.t.sol index 073e1fa914..0c9d9e6055 100644 --- a/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/UpgradeToDAOV3.t.sol +++ b/packages/nouns-contracts/test/foundry/NounsDAOLogicV3/UpgradeToDAOV3.t.sol @@ -97,30 +97,6 @@ contract UpgradeToDAOV3Test is DeployUtils { assertEq(proposer2.balance, 100 ether); } - function test_proposalQueuedBeforeUpgrade_executeRevertsButExecuteOnV1Works() public { - uint256 proposalId = deployContractsAndProposeUpgradeToDAOV3(address(daoProxy.timelock()), 500 ether); - - uint256 proposalId2 = proposeToSendETH(proposer2, proposer2, 100 ether); - - rollAndCastVote(proposer, proposalId, 1); - - vm.prank(proposer2); - daoProxy.castVote(proposalId2, 1); - - vm.roll(block.number + daoProxy.votingPeriod() + 1); - daoProxy.queue(proposalId); - daoProxy.queue(proposalId2); - - vm.warp(block.timestamp + daoProxy.timelock().delay()); - daoProxy.execute(proposalId); - - vm.expectRevert("NounsDAOExecutor::executeTransaction: Transaction hasn't been queued."); - daoProxy.execute(proposalId2); - - NounsDAOLogicV3(payable(address(daoProxy))).executeOnTimelockV1(proposalId2); - assertEq(proposer2.balance, 100 ether); - } - function test_proposalWasQueuedAfterUpgrade() public { uint256 proposalId = deployContractsAndProposeUpgradeToDAOV3(address(daoProxy.timelock()), 500 ether); diff --git a/packages/nouns-contracts/test/foundry/governance/fork/ForkingEndToEnd.t.sol b/packages/nouns-contracts/test/foundry/governance/fork/ForkingEndToEnd.t.sol index 081a10cb68..c292dffefa 100644 --- a/packages/nouns-contracts/test/foundry/governance/fork/ForkingEndToEnd.t.sol +++ b/packages/nouns-contracts/test/foundry/governance/fork/ForkingEndToEnd.t.sol @@ -12,10 +12,11 @@ import { NounsDAOLogicV1Fork } from '../../../../contracts/governance/fork/newda import { NounsAuctionHouseFork } from '../../../../contracts/governance/fork/newdao/NounsAuctionHouseFork.sol'; import { NounsTokenLike } from '../../../../contracts/governance/NounsDAOInterfaces.sol'; import { INounsAuctionHouse } from '../../../../contracts/interfaces/INounsAuctionHouse.sol'; +import { INounsDAOLogicV3 } from '../../../../contracts/interfaces/INounsDAOLogicV3.sol'; contract ForkingHappyFlowTest is DeployUtilsFork { address minter; - NounsDAOLogicV3 daoV3; + INounsDAOLogicV3 daoV3; NounsToken ogToken; NounsTokenFork forkToken; NounsDAOExecutorV2 forkTreasury; @@ -170,7 +171,7 @@ contract ForkingHappyFlowTest is DeployUtilsFork { } abstract contract ForkDAOBase is DeployUtilsFork { - NounsDAOLogicV3 originalDAO; + INounsDAOLogicV3 originalDAO; NounsTokenLike originalToken; NounsDAOLogicV1Fork forkDAO; NounsDAOExecutorV2 forkTreasury; diff --git a/packages/nouns-contracts/test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol b/packages/nouns-contracts/test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol index d0de7a734e..4611ab81b7 100644 --- a/packages/nouns-contracts/test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol +++ b/packages/nouns-contracts/test/foundry/governance/fork/NounsDAOLogicV1Fork.t.sol @@ -18,6 +18,7 @@ import { ERC20Mock, IERC20Receiver } from '../../helpers/ERC20Mock.sol'; import { MaliciousForkDAOQuitter } from '../../helpers/MaliciousForkDAOQuitter.sol'; import { NounsAuctionHouse } from '../../../../contracts/NounsAuctionHouse.sol'; import { INounsAuctionHouse } from '../../../../contracts/interfaces/INounsAuctionHouse.sol'; +import { INounsDAOLogicV3 } from '../../../../contracts/interfaces/INounsDAOLogicV3.sol'; abstract contract NounsDAOLogicV1ForkBase is DeployUtilsFork { NounsDAOLogicV1Fork dao; @@ -203,7 +204,7 @@ contract NounsDAOLogicV1Fork_cancelProposalUnderThresholdBugFix_Test is NounsDAO abstract contract ForkWithEscrow is NounsDAOLogicV1ForkBase { NounsDAOForkEscrowMock escrow; NounsTokenLike originalToken; - NounsDAOLogicV3 originalDAO; + INounsDAOLogicV3 originalDAO; address owner1 = makeAddr('owner1'); diff --git a/packages/nouns-contracts/test/foundry/helpers/DeployUtilsFork.sol b/packages/nouns-contracts/test/foundry/helpers/DeployUtilsFork.sol index 890cc11ee9..a1cf38dcdf 100644 --- a/packages/nouns-contracts/test/foundry/helpers/DeployUtilsFork.sol +++ b/packages/nouns-contracts/test/foundry/helpers/DeployUtilsFork.sol @@ -10,6 +10,7 @@ import { NounsTokenFork } from '../../../contracts/governance/fork/newdao/token/ import { NounsAuctionHouseFork } from '../../../contracts/governance/fork/newdao/NounsAuctionHouseFork.sol'; import { NounsDAOLogicV1Fork } from '../../../contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol'; import { INounsDAOForkEscrow } from '../../../contracts/governance/NounsDAOInterfaces.sol'; +import { INounsDAOLogicV3 } from '../../../contracts/interfaces/INounsDAOLogicV3.sol'; abstract contract DeployUtilsFork is DeployUtilsV3 { function _deployForkDAO(INounsDAOForkEscrow escrow) @@ -44,7 +45,7 @@ abstract contract DeployUtilsFork is DeployUtilsV3 { address dao ) { - NounsDAOLogicV3 originalDAO = _deployDAOV3(); + INounsDAOLogicV3 originalDAO = _deployDAOV3(); return _deployForkDAO(originalDAO.forkEscrow()); } } diff --git a/packages/nouns-contracts/test/foundry/helpers/DeployUtilsV3.sol b/packages/nouns-contracts/test/foundry/helpers/DeployUtilsV3.sol index 0c2176d682..7661fd4e64 100644 --- a/packages/nouns-contracts/test/foundry/helpers/DeployUtilsV3.sol +++ b/packages/nouns-contracts/test/foundry/helpers/DeployUtilsV3.sol @@ -20,6 +20,7 @@ import { NounsTokenFork } from '../../../contracts/governance/fork/newdao/token/ import { NounsAuctionHouseFork } from '../../../contracts/governance/fork/newdao/NounsAuctionHouseFork.sol'; import { NounsDAOLogicV1Fork } from '../../../contracts/governance/fork/newdao/governance/NounsDAOLogicV1Fork.sol'; import { NounsDAOStorageV3 } from '../../../contracts/governance/NounsDAOInterfaces.sol'; +import { INounsDAOLogicV3 } from '../../../contracts/interfaces/INounsDAOLogicV3.sol'; abstract contract DeployUtilsV3 is DeployUtils { function _createDAOV3Proxy( @@ -58,7 +59,7 @@ abstract contract DeployUtilsV3 is DeployUtils { address(new NounsDAOForkEscrow(address(dao), address(nounsToken))); } - function _deployDAOV3() internal returns (NounsDAOLogicV3) { + function _deployDAOV3() internal returns (INounsDAOLogicV3) { address noundersDAO = makeAddr('noundersDAO'); address vetoer = makeAddr('vetoer'); @@ -101,7 +102,7 @@ abstract contract DeployUtilsV3 is DeployUtils { FORK_DAO_QUORUM_VOTES_BPS ); - NounsDAOLogicV3 dao = NounsDAOLogicV3( + INounsDAOLogicV3 dao = INounsDAOLogicV3( payable( new NounsDAOProxyV3( address(timelock),