diff --git a/.forge-snapshots/ProposeSigComp.snap b/.forge-snapshots/ProposeSigComp.snap index fc8c4643..ebc089ee 100644 --- a/.forge-snapshots/ProposeSigComp.snap +++ b/.forge-snapshots/ProposeSigComp.snap @@ -1 +1 @@ -149858 \ No newline at end of file +148841 \ No newline at end of file diff --git a/.forge-snapshots/VoteSigComp.snap b/.forge-snapshots/VoteSigComp.snap index ce82b738..5c2ab0fd 100644 --- a/.forge-snapshots/VoteSigComp.snap +++ b/.forge-snapshots/VoteSigComp.snap @@ -1 +1 @@ -50570 \ No newline at end of file +50322 \ No newline at end of file diff --git a/.forge-snapshots/VoteSigCompMetadata.snap b/.forge-snapshots/VoteSigCompMetadata.snap index 923ad9bd..38f1a7f0 100644 --- a/.forge-snapshots/VoteSigCompMetadata.snap +++ b/.forge-snapshots/VoteSigCompMetadata.snap @@ -1 +1 @@ -52113 \ No newline at end of file +51865 \ No newline at end of file diff --git a/.forge-snapshots/VoteTxComp.snap b/.forge-snapshots/VoteTxComp.snap index 481edfcc..7a8eb696 100644 --- a/.forge-snapshots/VoteTxComp.snap +++ b/.forge-snapshots/VoteTxComp.snap @@ -1 +1 @@ -44140 \ No newline at end of file +43892 \ No newline at end of file diff --git a/.forge-snapshots/VoteTxCompMetadata.snap b/.forge-snapshots/VoteTxCompMetadata.snap index e81fea92..2fd0b253 100644 --- a/.forge-snapshots/VoteTxCompMetadata.snap +++ b/.forge-snapshots/VoteTxCompMetadata.snap @@ -1 +1 @@ -45724 \ No newline at end of file +45476 \ No newline at end of file diff --git a/README.md b/README.md index 5f998060..30a54100 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,7 @@ src │ ├─ SXHash.sol - "Snapshot X Types Hashing Library" │ ├─ SXUtils.sol - "Snapshot X Types Utilities Library" │ ├─ SignatureVerifier.sol - "Verifies EIP712 Signatures for Snapshot X actions" -│ ├─ SpaceManager.sol - "Manages a whitelist of Spaces that have permissions to execute transactions" -│ └─ TimestampResolver.sol - "Resolves timestamps to block numbers" +│ └─ SpaceManager.sol - "Manages a whitelist of Spaces that have permissions to execute transactions" ├─ ProxyFactory.sol - "Handles the deployment and tracking of Space contracts" └─ Space.sol - "The base contract for each Snapshot X space" └─ types.sol - "Definitions for Snapshot X custom types" diff --git a/src/Space.sol b/src/Space.sol index ebbc4f08..5546c569 100644 --- a/src/Space.sol +++ b/src/Space.sol @@ -206,9 +206,6 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra Strategy calldata executionStrategy, bytes calldata userProposalValidationParams ) external override onlyAuthenticator { - // Casting to `uint32` is fine because this gives us until year ~2106. - uint32 snapshotTimestamp = uint32(block.timestamp); - if ( !IProposalValidationStrategy(proposalValidationStrategy.addr).validate( author, @@ -217,20 +214,20 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra ) ) revert FailedToPassProposalValidation(); - uint32 startTimestamp = snapshotTimestamp + votingDelay; - uint32 minEndTimestamp = startTimestamp + minVotingDuration; - uint32 maxEndTimestamp = startTimestamp + maxVotingDuration; + // Max block number of 2^32 - 1 = 4,294,967,295 + uint32 startBlockNumber = uint32(block.number) + votingDelay; + uint32 minEndBlockNumber = startBlockNumber + minVotingDuration; + uint32 maxEndBlockNumber = startBlockNumber + maxVotingDuration; // The execution payload is the params of the supplied execution strategy struct. bytes32 executionPayloadHash = keccak256(executionStrategy.params); Proposal memory proposal = Proposal( author, - snapshotTimestamp, - startTimestamp, + startBlockNumber, IExecutionStrategy(executionStrategy.addr), - minEndTimestamp, - maxEndTimestamp, + minEndBlockNumber, + maxEndBlockNumber, FinalizationStatus.Pending, executionPayloadHash, activeVotingStrategies @@ -252,8 +249,8 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra ) external override onlyAuthenticator { Proposal memory proposal = proposals[proposalId]; _assertProposalExists(proposal); - if (block.timestamp >= proposal.maxEndTimestamp) revert VotingPeriodHasEnded(); - if (block.timestamp < proposal.startTimestamp) revert VotingPeriodHasNotStarted(); + if (block.number >= proposal.maxEndBlockNumber) revert VotingPeriodHasEnded(); + if (block.number < proposal.startBlockNumber) revert VotingPeriodHasNotStarted(); if (proposal.finalizationStatus != FinalizationStatus.Pending) revert ProposalFinalized(); if (voteRegistry[proposalId][voter] == TRUE) revert UserAlreadyVoted(); @@ -261,7 +258,7 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra uint256 votingPower = _getCumulativePower( voter, - proposal.snapshotTimestamp, + proposal.startBlockNumber, userVotingStrategies, proposal.activeVotingStrategies ); @@ -317,7 +314,7 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra Proposal storage proposal = proposals[proposalId]; _assertProposalExists(proposal); if (author != proposal.author) revert InvalidCaller(); - if (block.timestamp >= proposal.startTimestamp) revert VotingDelayHasPassed(); + if (block.number >= proposal.startBlockNumber) revert VotingDelayHasPassed(); proposal.executionPayloadHash = keccak256(executionStrategy.params); proposal.executionStrategy = IExecutionStrategy(executionStrategy.addr); @@ -403,16 +400,14 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra /// @dev Reverts if a specified proposal does not exist. function _assertProposalExists(Proposal memory proposal) internal pure { - // startTimestamp cannot be set to 0 when a proposal is created, - // so if proposal.startTimestamp is 0 it means this proposal does not exist - // and hence `proposalId` is invalid. - if (proposal.startTimestamp == 0) revert InvalidProposal(); + // If a proposal exists, then its execution payload hash will be non-zero. + if (proposal.executionPayloadHash == 0) revert InvalidProposal(); } /// @dev Returns the cumulative voting power of a user over a set of voting strategies. function _getCumulativePower( address userAddress, - uint32 timestamp, + uint32 blockNumber, IndexedStrategy[] calldata userStrategies, uint256 allowedStrategies ) internal returns (uint256) { @@ -431,7 +426,7 @@ contract Space is ISpace, Initializable, IERC4824, UUPSUpgradeable, OwnableUpgra Strategy memory strategy = votingStrategies[strategyIndex]; totalVotingPower += IVotingStrategy(strategy.addr).getVotingPower( - timestamp, + blockNumber, userAddress, strategy.params, userStrategies[i].params diff --git a/src/execution-strategies/EmergencyQuorumExecutionStrategy.sol b/src/execution-strategies/EmergencyQuorumExecutionStrategy.sol index 7d50995d..50f5af23 100644 --- a/src/execution-strategies/EmergencyQuorumExecutionStrategy.sol +++ b/src/execution-strategies/EmergencyQuorumExecutionStrategy.sol @@ -56,12 +56,12 @@ abstract contract EmergencyQuorumExecutionStrategy is IExecutionStrategy, SpaceM return ProposalStatus.Cancelled; } else if (proposal.finalizationStatus == FinalizationStatus.Executed) { return ProposalStatus.Executed; - } else if (block.timestamp < proposal.startTimestamp) { + } else if (block.number < proposal.startBlockNumber) { return ProposalStatus.VotingDelay; } else if (emergencyQuorumReached) { if (_supported(votesFor, votesAgainst)) { // Proposal is supported - if (block.timestamp < proposal.maxEndTimestamp) { + if (block.number < proposal.maxEndBlockNumber) { // New votes can still come in so return `VotingPeriodAccepted`. return ProposalStatus.VotingPeriodAccepted; } else { @@ -70,7 +70,7 @@ abstract contract EmergencyQuorumExecutionStrategy is IExecutionStrategy, SpaceM } } else { // Proposal is not supported - if (block.timestamp < proposal.maxEndTimestamp) { + if (block.number < proposal.maxEndBlockNumber) { // New votes might still come in so return `VotingPeriod`. return ProposalStatus.VotingPeriod; } else { @@ -78,11 +78,11 @@ abstract contract EmergencyQuorumExecutionStrategy is IExecutionStrategy, SpaceM return ProposalStatus.Rejected; } } - } else if (block.timestamp < proposal.minEndTimestamp) { - // Proposal has not reached minEndTimestamp yet. + } else if (block.number < proposal.minEndBlockNumber) { + // Proposal has not reached minEndBlockNumber yet. return ProposalStatus.VotingPeriod; - } else if (block.timestamp < proposal.maxEndTimestamp) { - // Timestamp is between minEndTimestamp and maxEndTimestamp + } else if (block.number < proposal.maxEndBlockNumber) { + // block number is between minEndBlockNumber and maxEndBlockNumber if (accepted) { return ProposalStatus.VotingPeriodAccepted; } else { diff --git a/src/execution-strategies/OptimisticQuorumExecutionStrategy.sol b/src/execution-strategies/OptimisticQuorumExecutionStrategy.sol index 716c9faf..86d99cd5 100644 --- a/src/execution-strategies/OptimisticQuorumExecutionStrategy.sol +++ b/src/execution-strategies/OptimisticQuorumExecutionStrategy.sol @@ -48,19 +48,19 @@ abstract contract OptimisticQuorumExecutionStrategy is IExecutionStrategy, Space return ProposalStatus.Cancelled; } else if (proposal.finalizationStatus == FinalizationStatus.Executed) { return ProposalStatus.Executed; - } else if (block.timestamp < proposal.startTimestamp) { + } else if (block.number < proposal.startBlockNumber) { return ProposalStatus.VotingDelay; } else if (rejected) { // We're past the vote start. If it has been rejected, we can short-circuit and return Rejected. return ProposalStatus.Rejected; - } else if (block.timestamp < proposal.minEndTimestamp) { - // minEndTimestamp not reached, indicate we're still in the voting period. + } else if (block.number < proposal.minEndBlockNumber) { + // minEndBlockNumber not reached, indicate we're still in the voting period. return ProposalStatus.VotingPeriod; - } else if (block.timestamp < proposal.maxEndTimestamp) { - // minEndTimestamp < now < maxEndTimestamp ; if not `rejected`, we can indicate it can be `accepted`. + } else if (block.number < proposal.maxEndBlockNumber) { + // minEndBlockNumber < block.number < maxEndBlockNumber ; if not `rejected`, we can indicate it can be `accepted`. return ProposalStatus.VotingPeriodAccepted; } else { - // maxEndTimestamp < now ; proposal has not been `rejected` ; we can indicate it's `accepted`. + // maxEndBlockNumber < block.number ; proposal has not been `rejected` ; we can indicate it's `accepted`. return ProposalStatus.Accepted; } } diff --git a/src/execution-strategies/SimpleQuorumExecutionStrategy.sol b/src/execution-strategies/SimpleQuorumExecutionStrategy.sol index 7c1930c8..34274bdc 100644 --- a/src/execution-strategies/SimpleQuorumExecutionStrategy.sol +++ b/src/execution-strategies/SimpleQuorumExecutionStrategy.sol @@ -50,11 +50,11 @@ abstract contract SimpleQuorumExecutionStrategy is IExecutionStrategy, SpaceMana return ProposalStatus.Cancelled; } else if (proposal.finalizationStatus == FinalizationStatus.Executed) { return ProposalStatus.Executed; - } else if (block.timestamp < proposal.startTimestamp) { + } else if (block.number < proposal.startBlockNumber) { return ProposalStatus.VotingDelay; - } else if (block.timestamp < proposal.minEndTimestamp) { + } else if (block.number < proposal.minEndBlockNumber) { return ProposalStatus.VotingPeriod; - } else if (block.timestamp < proposal.maxEndTimestamp) { + } else if (block.number < proposal.maxEndBlockNumber) { if (accepted) { return ProposalStatus.VotingPeriodAccepted; } else { diff --git a/src/interfaces/IVotingStrategy.sol b/src/interfaces/IVotingStrategy.sol index e1124b65..899dbf49 100644 --- a/src/interfaces/IVotingStrategy.sol +++ b/src/interfaces/IVotingStrategy.sol @@ -4,19 +4,17 @@ pragma solidity ^0.8.18; /// @title Voting Strategy Interface interface IVotingStrategy { - /// @notice Gets the voting power of an address at a given timestamp. - /// @param timestamp The snapshot timestamp to get the voting power at. If a particular voting strategy - /// requires a block number instead of a timestamp, the strategy should resolve the - /// timestamp to a block number. + /// @notice Gets the voting power of an address at a given block number. + /// @param blockNumber The snapshot block number to get the voting power at. /// @param voter The address to get the voting power of. /// @param params The global parameters that can configure the voting strategy for a particular Space. /// @param userParams The user parameters that can be used in the voting strategy computation. - /// @return votingPower The voting power of the address at the given timestamp. If there is no voting power, + /// @return votingPower The voting power of the address at the given block number. If there is no voting power, /// return 0. function getVotingPower( - uint32 timestamp, + uint32 blockNumber, address voter, bytes calldata params, bytes calldata userParams - ) external returns (uint256 votingPower); + ) external view returns (uint256 votingPower); } diff --git a/src/interfaces/space/ISpaceState.sol b/src/interfaces/space/ISpaceState.sol index 5027063c..8841bd2e 100644 --- a/src/interfaces/space/ISpaceState.sol +++ b/src/interfaces/space/ISpaceState.sol @@ -55,12 +55,11 @@ interface ISpaceState { /// @dev Returns all zeros if the proposal does not exist. /// @param proposalId The ID of the proposal. /// @return author The address of the proposal author. - /// @return snapshotTimestamp The timestamp of the proposal snapshot. - /// All Voting Power will be calculated at this timestamp. - /// @return startTimestamp The timestamp of the start of the voting period. + /// @return startBlockNumber The block number of the start of the voting period. + /// This is also the snapshot block number where voting power is calculated at. /// @return executionStrategy The address of the execution strategy used in the proposal. - /// @return minEndTimestamp The timestamp of the minimum end of the voting period. - /// @return maxEndTimestamp The timestamp of the maximum end of the voting period. + /// @return minEndBlockNumber The block number of the minimum end of the voting period. + /// @return maxEndBlockNumber The block number of the maximum end of the voting period. /// @return finalizationStatus The finalization status of the proposal. See `FinalizationStatus`. /// @return executionPayloadHash The keccak256 hash of the execution payload. /// @return activeVotingStrategies The bit array of the active voting strategies for the proposal. @@ -71,11 +70,10 @@ interface ISpaceState { view returns ( address author, - uint32 snapshotTimestamp, - uint32 startTimestamp, + uint32 startBlockNumber, IExecutionStrategy executionStrategy, - uint32 minEndTimestamp, - uint32 maxEndTimestamp, + uint32 minEndBlockNumber, + uint32 maxEndBlockNumber, FinalizationStatus finalizationStatus, bytes32 executionPayloadHash, uint256 activeVotingStrategies diff --git a/src/proposal-validation-strategies/PropositionPowerAndActiveProposalsLimiterValidationStrategy.sol b/src/proposal-validation-strategies/PropositionPowerAndActiveProposalsLimiterValidationStrategy.sol index 7a464d4a..01ba0e83 100644 --- a/src/proposal-validation-strategies/PropositionPowerAndActiveProposalsLimiterValidationStrategy.sol +++ b/src/proposal-validation-strategies/PropositionPowerAndActiveProposalsLimiterValidationStrategy.sol @@ -16,7 +16,7 @@ contract PropositionPowerAndActiveProposalsLimiterValidationStrategy is IProposalValidationStrategy { /// @notice Validates an author by checking if the proposition power of the author exceeds a threshold over a set of - /// strategies and if the author has reached the maximum number of active proposals at the current timestamp. + /// strategies and if the author has reached the maximum number of active proposals at the current block. /// @param author Author of the proposal. /// @param params ABI encoded array that should contain the following: /// cooldown: Duration to wait before the proposal counter gets reset. diff --git a/src/types.sol b/src/types.sol index 2efd3887..bb0ccfc8 100644 --- a/src/types.sol +++ b/src/types.sol @@ -15,18 +15,17 @@ struct Proposal { // SLOT 1: // The address of the proposal creator. address author; - // The timestamp at which voting power for the proposal is calculated. - uint32 snapshotTimestamp; - // The timestamp at which the voting period starts. - uint32 startTimestamp; + // The block number at which the voting period starts. + // This is also the snapshot block number where voting power is calculated at. + uint32 startBlockNumber; // // SLOT 2: // The address of execution strategy used for the proposal. IExecutionStrategy executionStrategy; - // The minimum timestamp at which the proposal can be finalized. - uint32 minEndTimestamp; - // The maximum timestamp at which the proposal can be finalized. - uint32 maxEndTimestamp; + // The minimum block number at which the proposal can be finalized. + uint32 minEndBlockNumber; + // The maximum block number at which the proposal can be finalized. + uint32 maxEndBlockNumber; // An enum that stores whether a proposal is pending, executed, or cancelled. FinalizationStatus finalizationStatus; // diff --git a/src/utils/PropositionPower.sol b/src/utils/PropositionPower.sol index 1b04e0d8..4f8eda39 100644 --- a/src/utils/PropositionPower.sol +++ b/src/utils/PropositionPower.sol @@ -23,14 +23,14 @@ abstract contract PropositionPower { Strategy[] memory allowedStrategies, IndexedStrategy[] memory userStrategies ) internal returns (bool) { - uint256 votingPower = _getCumulativePower(author, uint32(block.timestamp), userStrategies, allowedStrategies); + uint256 votingPower = _getCumulativePower(author, uint32(block.number), userStrategies, allowedStrategies); return (votingPower >= proposalThreshold); } - /// @dev Computes the cumulative proposition power of an address at a given timestamp over a set of strategies. + /// @dev Computes the cumulative proposition power of an address at a given block number over a set of strategies. function _getCumulativePower( address userAddress, - uint32 timestamp, + uint32 blockNumber, IndexedStrategy[] memory userStrategies, Strategy[] memory allowedStrategies ) internal returns (uint256) { @@ -44,7 +44,7 @@ abstract contract PropositionPower { Strategy memory strategy = allowedStrategies[strategyIndex]; totalVotingPower += IVotingStrategy(strategy.addr).getVotingPower( - timestamp, + blockNumber, userAddress, strategy.params, userStrategies[i].params diff --git a/src/utils/TimestampResolver.sol b/src/utils/TimestampResolver.sol deleted file mode 100644 index 41b43306..00000000 --- a/src/utils/TimestampResolver.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.18; - -/// @title Timestamp Resolver -/// @notice The Space contract tracks time with timestamps but some strategies require block numbers, -/// this base contract can be inherited by strategies to resolve this conversion in a secure way. -abstract contract TimestampResolver { - /// @notice Emitted when a timestamp passed is in the future. - error TimestampInFuture(); - - /// @notice Emitted when the block number is 1, an edge case that cannot be resolved. - error InvalidBlockNumber(); - - mapping(uint32 timestamp => uint256 blockNumber) public timestampToBlockNumber; - - /// @notice Resolves a timestamp to a block number in such a way that the same timestamp always - /// resolves to the same block number. If the timestamp is in the future, it reverts. - /// @param timestamp The timestamp to resolve. - /// @return blockNumber The block number that the timestamp resolves to. - function resolveSnapshotTimestamp(uint32 timestamp) internal returns (uint256 blockNumber) { - if (timestamp > uint32(block.timestamp)) revert TimestampInFuture(); - if (block.number == 1) revert InvalidBlockNumber(); - - blockNumber = timestampToBlockNumber[timestamp]; - if (blockNumber != 0) { - // Timestamp already resolved, return the previously resolved block number. - return blockNumber; - } - // Timestamp not yet resolved, resolve it to the current block number - 1 and return it. - // We resolve to the current block number - 1 so that Comp style getPastVotes/getPriorVotes - // functions can be used in same block as when the resolution is made. - blockNumber = block.number - 1; - - timestampToBlockNumber[timestamp] = blockNumber; - return blockNumber; - } -} diff --git a/src/voting-strategies/CompVotingStrategy.sol b/src/voting-strategies/CompVotingStrategy.sol index 1c6eed13..39060cb9 100644 --- a/src/voting-strategies/CompVotingStrategy.sol +++ b/src/voting-strategies/CompVotingStrategy.sol @@ -4,27 +4,27 @@ pragma solidity ^0.8.18; import { IVotingStrategy } from "../interfaces/IVotingStrategy.sol"; import { IComp } from "../interfaces/IComp.sol"; -import { TimestampResolver } from "../utils/TimestampResolver.sol"; /// @title Comp Voting Strategy /// @notice Uses delegated balances of Comp style tokens to determine voting power. -contract CompVotingStrategy is IVotingStrategy, TimestampResolver { +contract CompVotingStrategy is IVotingStrategy { /// @notice Thrown when the byte array is not long enough to represent an address. error InvalidByteArray(); - /// @notice Returns the voting power of an address at a given timestamp. - /// @param timestamp The snapshot timestamp to get the voting power at. + /// @notice Returns the voting power of an address at a given block number. + /// @param blockNumber The block number to get the voting power at. /// @param voter The address to get the voting power of. /// @param params Parameter array containing the address of the Comp style token. function getVotingPower( - uint32 timestamp, + uint32 blockNumber, address voter, bytes calldata params, bytes calldata /* userParams */ - ) external override returns (uint256) { + ) external view override returns (uint256) { address tokenAddress = bytesToAddress(params, 0); - uint256 blockNumber = resolveSnapshotTimestamp(timestamp); - return uint256(IComp(tokenAddress).getPriorVotes(voter, blockNumber)); + // We subract 1 from the block number so that when blockNumber == block.number, + // getPriorVotes can still be called. + return uint256(IComp(tokenAddress).getPriorVotes(voter, blockNumber - 1)); } /// @dev Extracts an address from a byte array. diff --git a/src/voting-strategies/OZVotesVotingStrategy.sol b/src/voting-strategies/OZVotesVotingStrategy.sol index 98c81d96..01edf52b 100644 --- a/src/voting-strategies/OZVotesVotingStrategy.sol +++ b/src/voting-strategies/OZVotesVotingStrategy.sol @@ -1,29 +1,30 @@ // SPDX-License-Identifier: MIT + pragma solidity ^0.8.18; import { IVotingStrategy } from "../interfaces/IVotingStrategy.sol"; import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol"; -import { TimestampResolver } from "../utils/TimestampResolver.sol"; -/// @title Comp Voting Strategy +/// @title OZ Votes Voting Strategy /// @notice Uses delegated balances of OZ Votes style tokens to determine voting power. -contract OZVotesVotingStrategy is IVotingStrategy, TimestampResolver { +contract OZVotesVotingStrategy is IVotingStrategy { /// @notice Thrown when the byte array is not long enough to represent an address. error InvalidByteArray(); - /// @notice Returns the voting power of an address at a given timestamp. - /// @param timestamp The snapshot timestamp to get the voting power at. + /// @notice Returns the voting power of an address at a given block number. + /// @param blockNumber The block number to get the voting power at. /// @param voter The address to get the voting power of. - /// @param params Parameter array containing the address of the OZ Votes style token. + /// @param params Parameter array containing the address of the OZ Votes token. function getVotingPower( - uint32 timestamp, + uint32 blockNumber, address voter, bytes calldata params, bytes calldata /* userParams */ - ) external override returns (uint256) { + ) external view override returns (uint256) { address tokenAddress = bytesToAddress(params, 0); - uint256 blockNumber = resolveSnapshotTimestamp(timestamp); - return uint256(IVotes(tokenAddress).getPastVotes(voter, blockNumber)); + // We subract 1 from the block number so that when blockNumber == block.number, + // getPastVotes can still be called. + return uint256(IVotes(tokenAddress).getPastVotes(voter, blockNumber - 1)); } /// @dev Extracts an address from a byte array. diff --git a/src/voting-strategies/WhitelistVotingStrategy.sol b/src/voting-strategies/WhitelistVotingStrategy.sol index df070962..0879b1a7 100644 --- a/src/voting-strategies/WhitelistVotingStrategy.sol +++ b/src/voting-strategies/WhitelistVotingStrategy.sol @@ -19,14 +19,14 @@ contract WhitelistVotingStrategy is IVotingStrategy { uint96 vp; } - /// @notice Returns the voting power of an address at a given timestamp. + /// @notice Returns the voting power of an address. /// @param voter The address to get the voting power of. /// @param params Parameter array containing the encoded whitelist of addresses and their voting power. /// The array should be an ABI encoded array of Member structs sorted by ascending addresses. /// @param userParams Expected to contain a `uint256` corresponding to the voterIndex in the array provided by `params`. /// @return votingPower The voting power of the address if it exists in the whitelist, otherwise 0. function getVotingPower( - uint32 /* timestamp */, + uint32 /* blockNumber */, address voter, bytes calldata params, bytes calldata userParams diff --git a/test/AvatarExecutionStrategy.t.sol b/test/AvatarExecutionStrategy.t.sol index e1e7e6d3..a6e96ff8 100644 --- a/test/AvatarExecutionStrategy.t.sol +++ b/test/AvatarExecutionStrategy.t.sol @@ -47,7 +47,7 @@ abstract contract AvatarExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit ProposalExecuted(proposalId); @@ -67,7 +67,7 @@ abstract contract AvatarExecutionStrategyTest is SpaceTest { Strategy(address(avatarExecutionStrategy), abi.encode(transactions)), new bytes(0) ); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, abi.encode(transactions)); @@ -83,7 +83,7 @@ abstract contract AvatarExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); transactions[0] = MetaTransaction(recipient, 2, "", Enum.Operation.Call, 0); @@ -102,7 +102,7 @@ abstract contract AvatarExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(ExecutionFailed.selector); space.execute(proposalId, abi.encode(transactions)); @@ -126,7 +126,7 @@ abstract contract AvatarExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); assertEq(recipient.balance, 0); // sanity check assertEq(avatar.isModuleEnabled(address(0xbeef)), false); // sanity check @@ -153,7 +153,7 @@ abstract contract AvatarExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(ExecutionFailed.selector); space.execute(proposalId, abi.encode(transactions)); @@ -246,7 +246,7 @@ abstract contract AvatarExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(InvalidSpace.selector); space.execute(proposalId, abi.encode(transactions)); diff --git a/test/CompTimelockExecutionStrategy.t.sol b/test/CompTimelockExecutionStrategy.t.sol index 0454ada4..6b8d071e 100644 --- a/test/CompTimelockExecutionStrategy.t.sol +++ b/test/CompTimelockExecutionStrategy.t.sol @@ -90,7 +90,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -106,7 +106,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { Strategy(address(timelockExecutionStrategy), abi.encode(transactions)), new bytes(0) ); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, abi.encode(transactions)); @@ -122,7 +122,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -149,7 +149,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); _vote(author, proposalId2, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -178,7 +178,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); _vote(author, proposalId2, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -195,7 +195,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); transactions[0] = MetaTransaction(recipient, 2, "", Enum.Operation.Call, 0); @@ -213,7 +213,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -237,7 +237,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -257,7 +257,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -278,7 +278,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -298,7 +298,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(ProposalNotQueued.selector); timelockExecutionStrategy.executeQueuedProposal(abi.encode(transactions)); @@ -314,7 +314,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -347,7 +347,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(InvalidTransaction.selector); space.execute(proposalId, abi.encode(transactions)); @@ -363,7 +363,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -393,7 +393,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -457,7 +457,7 @@ abstract contract CompTimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); diff --git a/test/CompVotingStrategy.t.sol b/test/CompVotingStrategy.t.sol index 619164b1..dcda9f1c 100644 --- a/test/CompVotingStrategy.t.sol +++ b/test/CompVotingStrategy.t.sol @@ -25,7 +25,7 @@ contract CompVotingStrategyTest is Test { compToken.delegate(user); vm.roll(block.number + 1); assertEq( - compVotingStrategy.getVotingPower(uint32(block.timestamp), user, abi.encodePacked(address(compToken)), ""), + compVotingStrategy.getVotingPower(uint32(block.number), user, abi.encodePacked(address(compToken)), ""), 1 ); } @@ -35,7 +35,7 @@ contract CompVotingStrategyTest is Test { // No delegation, so voting power is zero vm.roll(block.number + 1); assertEq( - compVotingStrategy.getVotingPower(uint32(block.timestamp), user, abi.encodePacked(address(compToken)), ""), + compVotingStrategy.getVotingPower(uint32(block.number), user, abi.encodePacked(address(compToken)), ""), 0 ); } @@ -46,7 +46,7 @@ contract CompVotingStrategyTest is Test { vm.roll(block.number + 1); vm.expectRevert(); // Token address is set to zero - compVotingStrategy.getVotingPower(uint32(block.timestamp), user, abi.encodePacked(address(0)), ""); + compVotingStrategy.getVotingPower(uint32(block.number), user, abi.encodePacked(address(0)), ""); } function testGetVotingPowerInvalidParamsArray() public { @@ -55,6 +55,6 @@ contract CompVotingStrategyTest is Test { vm.roll(block.number + 1); vm.expectRevert(InvalidByteArray.selector); // Params array is too short - compVotingStrategy.getVotingPower(uint32(block.timestamp), user, abi.encodePacked("1234"), ""); + compVotingStrategy.getVotingPower(uint32(block.number), user, abi.encodePacked("1234"), ""); } } diff --git a/test/EmergencyQuorumExecutionStrategy.t.sol b/test/EmergencyQuorumExecutionStrategy.t.sol index 63480e5b..880b7c31 100644 --- a/test/EmergencyQuorumExecutionStrategy.t.sol +++ b/test/EmergencyQuorumExecutionStrategy.t.sol @@ -116,7 +116,7 @@ contract EmergencyQuorumTest is SpaceTest { ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); // 1 - vm.warp(block.timestamp + minVotingDuration); + vm.roll(block.number + minVotingDuration); vm.expectEmit(true, true, true, true); emit ProposalExecuted(proposalId); @@ -132,7 +132,7 @@ contract EmergencyQuorumTest is SpaceTest { ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); // 1 - vm.warp(block.timestamp + maxVotingDuration); + vm.roll(block.number + maxVotingDuration); vm.expectEmit(true, true, true, true); emit ProposalExecuted(proposalId); @@ -157,7 +157,7 @@ contract EmergencyQuorumTest is SpaceTest { space.execute(proposalId, emergencyStrategy.params); // Now forward to `maxEndTimestamp`, the proposal should be finalized and `Rejected`. - vm.warp(block.timestamp + maxVotingDuration); + vm.roll(block.number + maxVotingDuration); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, uint8(ProposalStatus.Rejected))); space.execute(proposalId, emergencyStrategy.params); @@ -176,7 +176,7 @@ contract EmergencyQuorumTest is SpaceTest { abi.encode(userVotingStrategies) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); // emergencyQuorum reached - vm.warp(block.timestamp + maxVotingDuration); + vm.roll(block.number + maxVotingDuration); vm.expectEmit(true, true, true, true); emit ProposalExecuted(proposalId); @@ -225,7 +225,7 @@ contract EmergencyQuorumTest is SpaceTest { ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); // 1 - vm.warp(block.timestamp + minVotingDuration); + vm.roll(block.number + minVotingDuration); space.execute(proposalId, emergencyStrategy.params); diff --git a/test/EthTxAuthenticator.t.sol b/test/EthTxAuthenticator.t.sol index a0c87a54..943892e0 100644 --- a/test/EthTxAuthenticator.t.sol +++ b/test/EthTxAuthenticator.t.sol @@ -145,7 +145,7 @@ contract EthTxAuthenticatorTest is SpaceTest { ); // Fast forward and ensure everything is still working correctly - vm.warp(block.timestamp + votingDelay); + vm.roll(block.number + votingDelay); vm.prank(voter); ethTxAuth.authenticate( address(space), diff --git a/test/Execute.t.sol b/test/Execute.t.sol index 0c8bf7df..2815202f 100644 --- a/test/Execute.t.sol +++ b/test/Execute.t.sol @@ -10,8 +10,7 @@ contract ExecuteTest is SpaceTest { function testExecute() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); - + vm.roll(block.number + space.maxVotingDuration() + 1000); vm.expectEmit(true, true, true, true); emit ProposalExecuted(proposalId); space.execute(proposalId, executionStrategy.params); @@ -23,7 +22,7 @@ contract ExecuteTest is SpaceTest { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); uint256 invalidProposalId = proposalId + 1; _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposal.selector)); space.execute(invalidProposalId, executionStrategy.params); @@ -32,7 +31,7 @@ contract ExecuteTest is SpaceTest { function testExecuteAlreadyExecuted() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, executionStrategy.params); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Executed)); @@ -62,7 +61,7 @@ contract ExecuteTest is SpaceTest { vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.VotingPeriod)); space.execute(proposalId, executionStrategy.params); - vm.warp(block.timestamp + space.minVotingDuration()); + vm.roll(block.number + space.minVotingDuration()); space.execute(proposalId, executionStrategy.params); } @@ -75,7 +74,7 @@ contract ExecuteTest is SpaceTest { function testExecuteQuorumNotReachedAtAll() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, executionStrategy.params); @@ -86,7 +85,7 @@ contract ExecuteTest is SpaceTest { function testExecuteWithAgainstVote() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); _vote(author, proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, executionStrategy.params); @@ -97,7 +96,7 @@ contract ExecuteTest is SpaceTest { function testExecuteWithAbstainVote() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); _vote(author, proposalId, Choice.Abstain, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, executionStrategy.params); @@ -116,7 +115,7 @@ contract ExecuteTest is SpaceTest { function testExecuteInvalidExecutionStrategy() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, Strategy(address(space), ""), new bytes(0)); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(); space.execute(proposalId, executionStrategy.params); diff --git a/test/OZVotesVotingStrategy.t.sol b/test/OZVotesVotingStrategy.t.sol index d0059910..20de705d 100644 --- a/test/OZVotesVotingStrategy.t.sol +++ b/test/OZVotesVotingStrategy.t.sol @@ -26,7 +26,7 @@ contract OZVotesVotingStrategyTest is Test { vm.roll(block.number + 1); assertEq( ozVotesVotingStrategy.getVotingPower( - uint32(block.timestamp), + uint32(block.number), user, abi.encodePacked(address(erc20VotesToken)), "" @@ -41,7 +41,7 @@ contract OZVotesVotingStrategyTest is Test { vm.roll(block.number + 1); assertEq( ozVotesVotingStrategy.getVotingPower( - uint32(block.timestamp), + uint32(block.number), user, abi.encodePacked(address(erc20VotesToken)), "" @@ -56,7 +56,7 @@ contract OZVotesVotingStrategyTest is Test { vm.roll(block.number + 1); vm.expectRevert(); // Token address is set to zero - ozVotesVotingStrategy.getVotingPower(uint32(block.timestamp), user, abi.encodePacked(address(0)), ""); + ozVotesVotingStrategy.getVotingPower(uint32(block.number), user, abi.encodePacked(address(0)), ""); } function testGetVotingPowerInvalidParamsArray() public { @@ -65,6 +65,6 @@ contract OZVotesVotingStrategyTest is Test { vm.roll(block.number + 1); vm.expectRevert(InvalidByteArray.selector); // Params array is too short - ozVotesVotingStrategy.getVotingPower(uint32(block.timestamp), user, abi.encodePacked("1234"), ""); + ozVotesVotingStrategy.getVotingPower(uint32(block.number), user, abi.encodePacked("1234"), ""); } } diff --git a/test/OptimisticQuorum.t.sol b/test/OptimisticQuorum.t.sol index d039b303..bb88b504 100644 --- a/test/OptimisticQuorum.t.sol +++ b/test/OptimisticQuorum.t.sol @@ -59,7 +59,7 @@ contract OptimisticTest is SpaceTest { function testOptimisticQuorumNoVotes() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit ProposalExecuted(proposalId); @@ -71,7 +71,7 @@ contract OptimisticTest is SpaceTest { function testOptimisticQuorumOneVote() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); _vote(author, proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit ProposalExecuted(proposalId); @@ -84,7 +84,7 @@ contract OptimisticTest is SpaceTest { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); _vote(author, proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); _vote(address(42), proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, executionStrategy.params); @@ -101,7 +101,7 @@ contract OptimisticTest is SpaceTest { _vote(address(11), proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); _vote(address(12), proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, executionStrategy.params); @@ -114,7 +114,7 @@ contract OptimisticTest is SpaceTest { _vote(address(11), proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); _vote(address(12), proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.minVotingDuration()); + vm.roll(block.number + space.minVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, executionStrategy.params); @@ -125,7 +125,7 @@ contract OptimisticTest is SpaceTest { function testOptimisticQuorumMinVotingPeriodAccepted() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); - vm.warp(block.timestamp + space.minVotingDuration()); + vm.roll(block.number + space.minVotingDuration()); space.execute(proposalId, executionStrategy.params); @@ -154,7 +154,7 @@ contract OptimisticTest is SpaceTest { _vote(address(i), proposalId, Choice.Against, userVotingStrategies, voteMetadataURI); } - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(InvalidProposalStatus.selector, ProposalStatus.Rejected)); space.execute(proposalId, executionStrategy.params); diff --git a/test/Propose.t.sol b/test/Propose.t.sol index 96e4ec82..58d2d4fd 100644 --- a/test/Propose.t.sol +++ b/test/Propose.t.sol @@ -19,11 +19,10 @@ contract ProposeTest is SpaceTest { // Expected content of the proposal struct Proposal memory proposal = Proposal( author, - uint32(block.timestamp), - uint32(block.timestamp + votingDelay), + uint32(block.number + votingDelay), IExecutionStrategy(executionStrategy.addr), - uint32(block.timestamp + votingDelay + minVotingDuration), - uint32(block.timestamp + votingDelay + maxVotingDuration), + uint32(block.number + votingDelay + minVotingDuration), + uint32(block.number + votingDelay + maxVotingDuration), FinalizationStatus.Pending, keccak256(abi.encodePacked(executionStrategy.params)), activeVotingStrategies @@ -37,7 +36,6 @@ contract ProposeTest is SpaceTest { // Actual content of the proposal struct ( address _author, - uint32 _snapshotTimestamp, uint32 _startTimestamp, IExecutionStrategy _executionStrategy, uint32 _minEndTimestamp, @@ -49,7 +47,6 @@ contract ProposeTest is SpaceTest { Proposal memory _proposal = Proposal( _author, - _snapshotTimestamp, _startTimestamp, IExecutionStrategy(_executionStrategy), _minEndTimestamp, @@ -59,8 +56,6 @@ contract ProposeTest is SpaceTest { _activeVotingStrategies ); - // Proposal memory _proposal = space.proposalRegistry(proposalId); - // Checking expectations and actual values match assertEq(keccak256(abi.encode(_proposal)), keccak256(abi.encode(proposal))); } diff --git a/test/TimelockExecutionStrategy.t.sol b/test/TimelockExecutionStrategy.t.sol index 937964d4..b5ce8554 100644 --- a/test/TimelockExecutionStrategy.t.sol +++ b/test/TimelockExecutionStrategy.t.sol @@ -57,7 +57,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(InvalidSpace.selector); space.execute(proposalId, abi.encode(transactions)); @@ -73,7 +73,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -89,7 +89,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { Strategy(address(timelockExecutionStrategy), abi.encode(transactions)), new bytes(0) ); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(); space.execute(proposalId, abi.encode(transactions)); @@ -105,7 +105,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -132,7 +132,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); _vote(author, proposalId2, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -161,7 +161,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); _vote(author, proposalId2, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -178,7 +178,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); transactions[0] = MetaTransaction(recipient, 2, "", Enum.Operation.Call, 0); @@ -196,7 +196,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -220,7 +220,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -240,7 +240,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -261,7 +261,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -281,7 +281,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(ProposalNotQueued.selector); timelockExecutionStrategy.executeQueuedProposal(abi.encode(transactions)); @@ -297,7 +297,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -330,7 +330,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); @@ -354,7 +354,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -384,7 +384,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); // Set veto guardian address vetoGuardian = address(0x7e20); @@ -405,7 +405,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); space.execute(proposalId, abi.encode(transactions)); @@ -468,7 +468,7 @@ abstract contract TimelockExecutionStrategyTest is SpaceTest { new bytes(0) ); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectEmit(true, true, true, true); emit TransactionQueued(transactions[0], block.timestamp + 1000); diff --git a/test/TimestampResolver.t.sol b/test/TimestampResolver.t.sol deleted file mode 100644 index 078e9499..00000000 --- a/test/TimestampResolver.t.sol +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import { Test } from "forge-std/Test.sol"; -import { CompVotingStrategy } from "../src/voting-strategies/CompVotingStrategy.sol"; -import { CompToken } from "./mocks/CompToken.sol"; - -contract TimestampResolverTest is Test { - error TimestampInFuture(); - error InvalidBlockNumber(); - error InvalidBytesArray(); - - CompVotingStrategy public compVotingStrategy; - CompToken public compToken; - - address public user = address(this); - address public user2 = address(1); - - function setUp() public { - compVotingStrategy = new CompVotingStrategy(); - compToken = new CompToken(); - } - - function testTimestampResolver() public { - compToken.mint(user, 1); - compToken.delegate(user); - vm.roll(block.number + 1); - - assertEq( - compVotingStrategy.getVotingPower(uint32(block.timestamp), user, abi.encodePacked(address(compToken)), ""), - 1 - ); // sanity check - - // The snapshot timestamp is resolved to the current block number - 1 - assertEq(compVotingStrategy.timestampToBlockNumber(uint32(block.timestamp)), block.number - 1); - - // // Increasing the timestamp - vm.warp(block.timestamp + 100); - - compToken.mint(user2, 1); - vm.prank(user2); - compToken.delegate(user2); - vm.roll(block.number + 1); - - // assertEq(compVotingStrategy.timestampToBlockNumber(snapshotTimestamp), snapshotBlockNumber); - - // user2 has no voting power at the snapshot timestamp because the delegation happened after - assertEq( - compVotingStrategy.getVotingPower( - uint32(block.timestamp - 100), - user2, - abi.encodePacked(address(compToken)), - "" - ), - 0 - ); - - // However at the current timestamp, the delegation is active - assertEq( - compVotingStrategy.getVotingPower(uint32(block.timestamp), user2, abi.encodePacked(address(compToken)), ""), - 1 - ); - } - - function testTimestampResolverInvalidTimestamp() public { - compToken.mint(user, 1); - compToken.delegate(user); - vm.roll(block.number + 1); - vm.expectRevert(TimestampInFuture.selector); - // Timestamp is set to the future - compVotingStrategy.getVotingPower(uint32(block.timestamp + 1), user, abi.encodePacked(address(compToken)), ""); - } - - function testTimestampResolverInvalidBlockNumber() public { - vm.expectRevert(InvalidBlockNumber.selector); - // Current block number is 1, which is invalid - compVotingStrategy.getVotingPower(uint32(block.timestamp), user, abi.encodePacked(address(compToken)), "0"); - } -} diff --git a/test/UpdateProposalMetadata.t.sol b/test/UpdateProposalMetadata.t.sol index 0627f1d2..de4dc6f6 100644 --- a/test/UpdateProposalMetadata.t.sol +++ b/test/UpdateProposalMetadata.t.sol @@ -58,14 +58,14 @@ contract UpdateProposalTest is SpaceTest { _updateProposal(author, proposalId, newStrategy, newMetadataURI); // Fast forward and finish the proposal to ensure everything is still working properly. - vm.warp(block.timestamp + votingDelay); + vm.roll(block.number + votingDelay); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); space.execute(proposalId, executionStrategy.params); } function testUpdateProposalAfterDelay() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); - vm.warp(block.timestamp + votingDelay); + vm.roll(block.number + votingDelay); vm.expectRevert(VotingDelayHasPassed.selector); // Try to update metadata. Should fail. diff --git a/test/Vote.t.sol b/test/Vote.t.sol index 3d8e9e38..703de18c 100644 --- a/test/Vote.t.sol +++ b/test/Vote.t.sol @@ -52,7 +52,7 @@ contract VoteTest is SpaceTest { function testVoteVotingPeriodHasEnded() public { uint256 proposalId = _createProposal(author, proposalMetadataURI, executionStrategy, new bytes(0)); - vm.warp(block.timestamp + space.maxVotingDuration()); + vm.roll(block.number + space.maxVotingDuration()); vm.expectRevert(abi.encodeWithSelector(VotingPeriodHasEnded.selector)); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); } @@ -79,7 +79,7 @@ contract VoteTest is SpaceTest { vm.expectRevert(abi.encodeWithSelector(VotingPeriodHasNotStarted.selector)); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - vm.warp(block.timestamp + space.votingDelay()); + vm.roll(block.number + space.votingDelay()); _vote(author, proposalId, Choice.For, userVotingStrategies, voteMetadataURI); }