diff --git a/test/FlexVotingClient.t.sol b/test/FlexVotingClient.t.sol index dbbdef1..3ad0ea0 100644 --- a/test/FlexVotingClient.t.sol +++ b/test/FlexVotingClient.t.sol @@ -69,15 +69,15 @@ contract FlexVotingClientTest is Test { assertEq(uint8(governor.state(proposalId)), uint8(IGovernor.ProposalState.Active)); } - function _commonFuzzerAssumptions(address _address, uint208 _voteWeight) + function _assumeSafeVoteParams(address _address, uint208 _voteWeight) public view returns (uint208) { - return _commonFuzzerAssumptions(_address, _voteWeight, uint8(GCF.VoteType.Against)); + return _assumeSafeVoteParams(_address, _voteWeight, uint8(GCF.VoteType.Against)); } - function _commonFuzzerAssumptions(address _address, uint208 _voteWeight, uint8 _supportType) + function _assumeSafeVoteParams(address _address, uint208 _voteWeight, uint8 _supportType) public view returns (uint208) @@ -102,6 +102,252 @@ contract Deployment is FlexVotingClientTest { } } +contract Constructor is FlexVotingClientTest { + function test_SetsGovernor() public view { + assertEq(address(flexClient.GOVERNOR()), address(governor)); + } + + function test_SelfDelegates() public view { + assertEq(token.delegates(address(flexClient)), address(flexClient)); + } +} + +// Contract name has a leading underscore for scopelint spec support. +contract _RawBalanceOf is FlexVotingClientTest { + function _assumeSafeUser(address _user) internal view { + vm.assume(_user != address(flexClient)); + vm.assume(_user != address(0)); + } + + function testFuzz_ReturnsZeroForNonDepositors(address _user) public view { + _assumeSafeUser(_user); + assertEq(flexClient.exposed_rawBalanceOf(_user), 0); + } + + function testFuzz_IncreasesOnDeposit(address _user, uint208 _amount) public { + _assumeSafeUser(_user); + _amount = uint208(bound(_amount, 1, type(uint128).max)); + + // Deposit some gov. + _mintGovAndDepositIntoFlexClient(_user, _amount); + + assertEq(flexClient.exposed_rawBalanceOf(_user), _amount); + } + + function testFuzz_DecreasesOnWithdrawal(address _user, uint208 _amount) public { + _assumeSafeUser(_user); + _amount = uint208(bound(_amount, 1, type(uint128).max)); + + // Deposit some gov. + _mintGovAndDepositIntoFlexClient(_user, _amount); + + assertEq(flexClient.exposed_rawBalanceOf(_user), _amount); + + vm.prank(_user); + flexClient.withdraw(_amount); + assertEq(flexClient.exposed_rawBalanceOf(_user), 0); + } + + function testFuzz_UnaffectedByBorrow(address _user, uint208 _deposit, uint208 _borrow) public { + _assumeSafeUser(_user); + _deposit = uint208(bound(_deposit, 1, type(uint128).max)); + _borrow = uint208(bound(_borrow, 1, _deposit)); + + // Deposit some gov. + _mintGovAndDepositIntoFlexClient(_user, _deposit); + + assertEq(flexClient.exposed_rawBalanceOf(_user), _deposit); + + vm.prank(_user); + flexClient.borrow(_borrow); + + // Raw balance is unchanged. + assertEq(flexClient.exposed_rawBalanceOf(_user), _deposit); + } +} + +// Contract name has a leading underscore for scopelint spec support. +contract _CastVoteReasonString is FlexVotingClientTest { + function test_ReturnsDescriptiveString() public { + assertEq( + flexClient.exposed_castVoteReasonString(), "rolled-up vote from governance token holders" + ); + } +} + +// Contract name has a leading underscore for scopelint spec support. +contract _SelfDelegate is FlexVotingClientTest { + function testFuzz_SetsClientAsTheDelegate(address _delegatee) public { + vm.assume(_delegatee != address(0)); + vm.assume(_delegatee != address(flexClient)); + + // We self-delegate in the constructor, so we need to first un-delegate for + // this test to be meaningful. + vm.prank(address(flexClient)); + token.delegate(_delegatee); + assertEq(token.delegates(address(flexClient)), _delegatee); + + flexClient.exposed_selfDelegate(); + assertEq(token.delegates(address(flexClient)), address(flexClient)); + } +} + +// Contract name has a leading underscore for scopelint spec support. +contract _CheckpointRawBalanceOf is FlexVotingClientTest { + function testFuzz_StoresTheRawBalanceWithTheBlockNumber( + address _user, + uint208 _amount, + uint48 _blockNum + ) public { + vm.assume(_user != address(flexClient)); + _blockNum = uint48(bound(_blockNum, block.number + 1, type(uint48).max)); + _amount = uint208(bound(_amount, 1, type(uint128).max)); + + flexClient.exposed_setDeposits(_user, _amount); + assertEq(flexClient.getPastRawBalance(_user, _blockNum), 0); + + vm.roll(_blockNum); + flexClient.exposed_checkpointRawBalanceOf(_user); + assertEq(flexClient.getPastRawBalance(_user, _blockNum), _amount); + } +} + +contract GetPastRawBalance is FlexVotingClientTest { + function testFuzz_ReturnsZeroForUsersWithoutDeposits( + address _depositor, + address _nonDepositor, + uint208 _amount + ) public { + vm.assume(_depositor != address(flexClient)); + vm.assume(_nonDepositor != address(flexClient)); + vm.assume(_nonDepositor != _depositor); + _amount = uint208(bound(_amount, 1, type(uint128).max)); + + vm.roll(block.number + 1); + assertEq(flexClient.getPastRawBalance(_depositor, 0), 0); + assertEq(flexClient.getPastRawBalance(_nonDepositor, 0), 0); + + _mintGovAndDepositIntoFlexClient(_depositor, _amount); + vm.roll(block.number + 1); + + assertEq(flexClient.getPastRawBalance(_depositor, block.number - 1), _amount); + assertEq(flexClient.getPastRawBalance(_nonDepositor, block.number - 1), 0); + } + + function testFuzz_ReturnsCurrentValueForFutureBlocks( + address _user, + uint208 _amount, + uint48 _blockNum + ) public { + vm.assume(_user != address(flexClient)); + _blockNum = uint48(bound(_blockNum, block.number + 1, type(uint48).max)); + _amount = uint208(bound(_amount, 1, type(uint128).max)); + + _mintGovAndDepositIntoFlexClient(_user, _amount); + + assertEq(flexClient.getPastRawBalance(_user, block.number), _amount); + assertEq(flexClient.getPastRawBalance(_user, _blockNum), _amount); + vm.roll(_blockNum); + assertEq(flexClient.getPastRawBalance(_user, block.number), _amount); + } + + function testFuzz_ReturnsUserBalanceAtAGivenBlock( + address _user, + uint208 _amountA, + uint208 _amountB, + uint48 _blockNum + ) public { + vm.assume(_user != address(flexClient)); + _blockNum = uint48(bound(_blockNum, block.number + 1, type(uint48).max)); + _amountA = uint208(bound(_amountA, 1, type(uint128).max)); + _amountB = uint208(bound(_amountB, 0, type(uint128).max - _amountA)); + + _mintGovAndDepositIntoFlexClient(_user, _amountA); + vm.roll(_blockNum); + _mintGovAndDepositIntoFlexClient(_user, _amountB); + vm.roll(block.number + 1); + + uint48 _zeroBlock = 0; + uint48 _initBlock = 1; + assertEq(flexClient.getPastRawBalance(_user, _zeroBlock), 0); + assertEq(flexClient.getPastRawBalance(_user, _initBlock), _amountA); + assertEq(flexClient.getPastRawBalance(_user, _blockNum), _amountA + _amountB); + } +} + +contract GetPastTotalBalance is FlexVotingClientTest { + function test_ReturnsZeroWithoutDeposits() public view { + uint48 _zeroBlock = 0; + uint48 _futureBlock = uint48(block.number) + 42; + assertEq(flexClient.getPastTotalBalance(_zeroBlock), 0); + assertEq(flexClient.getPastTotalBalance(_futureBlock), 0); + } + + function testFuzz_ReturnsCurrentValueForFutureBlocks( + address _user, + uint208 _amount, + uint48 _blockNum + ) public { + vm.assume(_user != address(flexClient)); + _blockNum = uint48(bound(_blockNum, block.number + 1, type(uint48).max)); + _amount = uint208(bound(_amount, 1, type(uint128).max)); + + _mintGovAndDepositIntoFlexClient(_user, _amount); + + assertEq(flexClient.getPastTotalBalance(block.number), _amount); + assertEq(flexClient.getPastTotalBalance(_blockNum), _amount); + vm.roll(_blockNum); + assertEq(flexClient.getPastTotalBalance(block.number), _amount); + } + + function testFuzz_SumsAllUserDeposits( + address _userA, + uint208 _amountA, + address _userB, + uint208 _amountB + ) public { + vm.assume(_userA != address(flexClient)); + vm.assume(_userB != address(flexClient)); + vm.assume(_userA != _userB); + + _amountA = uint208(bound(_amountA, 1, type(uint128).max)); + _amountB = uint208(bound(_amountB, 0, type(uint128).max - _amountA)); + + _mintGovAndDepositIntoFlexClient(_userA, _amountA); + _mintGovAndDepositIntoFlexClient(_userB, _amountB); + + vm.roll(block.number + 1); + + assertEq(flexClient.getPastTotalBalance(block.number), _amountA + _amountB); + } + + function testFuzz_ReturnsTotalDepositsAtAGivenBlock( + address _userA, + uint208 _amountA, + address _userB, + uint208 _amountB, + uint48 _blockNum + ) public { + vm.assume(_userA != address(flexClient)); + vm.assume(_userB != address(flexClient)); + vm.assume(_userA != _userB); + _blockNum = uint48(bound(_blockNum, block.number + 1, type(uint48).max)); + + _amountA = uint208(bound(_amountA, 1, type(uint128).max)); + _amountB = uint208(bound(_amountB, 0, type(uint128).max - _amountA)); + + assertEq(flexClient.getPastTotalBalance(block.number), 0); + + _mintGovAndDepositIntoFlexClient(_userA, _amountA); + vm.roll(_blockNum); + _mintGovAndDepositIntoFlexClient(_userB, _amountB); + + assertEq(flexClient.getPastTotalBalance(block.number - _blockNum + 1), _amountA); + assertEq(flexClient.getPastTotalBalance(block.number), _amountA + _amountB); + } + // parallel of last test for getPastRawBalance +} + contract Withdraw is FlexVotingClientTest { function testFuzz_UserCanWithdrawGovTokens(address _lender, address _borrower, uint208 _amount) public @@ -158,7 +404,7 @@ contract Deposit is FlexVotingClientTest { uint24 _depositDelay ) public { _amountA = uint208(bound(_amountA, 1, type(uint128).max)); - _amountB = uint208(bound(_amountB, 1, type(uint128).max)); + _amountB = uint208(bound(_amountB, 0, type(uint128).max - _amountA)); // Deposit some gov. _mintGovAndDepositIntoFlexClient(_user, _amountA); @@ -196,9 +442,13 @@ contract Deposit is FlexVotingClientTest { } } -contract Vote is FlexVotingClientTest { - function testFuzz_UserCanCastVotes(address _user, uint208 _voteWeight, uint8 _supportType) public { - _voteWeight = _commonFuzzerAssumptions(_user, _voteWeight, _supportType); +contract ExpressVote is FlexVotingClientTest { + function testFuzz_IncrementsInternalAccouting( + address _user, + uint208 _voteWeight, + uint8 _supportType + ) public { + _voteWeight = _assumeSafeVoteParams(_user, _voteWeight, _supportType); // Deposit some funds. _mintGovAndDepositIntoFlexClient(_user, _voteWeight); @@ -221,52 +471,34 @@ contract Vote is FlexVotingClientTest { assertEq(_forVotes, 0); assertEq(_againstVotes, 0); assertEq(_abstainVotes, 0); - - // submit votes on behalf of the flexClient - flexClient.castVote(_proposalId); - - // governor should now record votes from the flexClient - (_againstVotes, _forVotes, _abstainVotes) = governor.proposalVotes(_proposalId); - assertEq(_forVotes, _forVotesExpressed); - assertEq(_againstVotes, _againstVotesExpressed); - assertEq(_abstainVotes, _abstainVotesExpressed); } - function testFuzz_RevertOn_CastVoteWithoutVotesToCast( + function testFuzz_RevertWhen_DepositingAfterProposal( address _user, uint208 _voteWeight, uint8 _supportType ) public { - _voteWeight = _commonFuzzerAssumptions(_user, _voteWeight, _supportType); + _voteWeight = _assumeSafeVoteParams(_user, _voteWeight, _supportType); - // Deposit some funds. - _mintGovAndDepositIntoFlexClient(_user, _voteWeight); - - // Create the proposal. + // Create the proposal *before* the user deposits anything. uint256 _proposalId = _createAndSubmitProposal(); - // No one has expressed, there are no votes to cast. - vm.expectRevert(bytes("no votes expressed")); - flexClient.castVote(_proposalId); + // Deposit some funds. + _mintGovAndDepositIntoFlexClient(_user, _voteWeight); - // _user expresses his/her vote on the proposal. + // Now try to express a voting preference on the proposal. + assertEq(flexClient.deposits(_user), _voteWeight); + vm.expectRevert(bytes("no weight")); vm.prank(_user); flexClient.expressVote(_proposalId, _supportType); - - // Submit votes on behalf of the flexClient. - flexClient.castVote(_proposalId); - - // All votes have been cast, there's nothing new to send to the governor. - vm.expectRevert(bytes("no votes expressed")); - flexClient.castVote(_proposalId); } - function testFuzz_UserCannotExpressVotesWithoutWeightInPool( + function testFuzz_RevertWhen_NoClientWeightButTokenWeight( address _user, uint208 _voteWeight, uint8 _supportType ) public { - _voteWeight = _commonFuzzerAssumptions(_user, _voteWeight, _supportType); + _voteWeight = _assumeSafeVoteParams(_user, _voteWeight, _supportType); // Mint gov but do not deposit. _mintGovAndApproveFlexClient(_user, _voteWeight); @@ -280,43 +512,24 @@ contract Vote is FlexVotingClientTest { vm.expectRevert(bytes("no weight")); vm.prank(_user); flexClient.expressVote(_proposalId, uint8(_supportType)); - } - - function testFuzz_UserCannotCastAfterVotingPeriod( - address _user, - uint208 _voteWeight, - uint8 _supportType - ) public { - _voteWeight = _commonFuzzerAssumptions(_user, _voteWeight, _supportType); - // Deposit some funds. - _mintGovAndDepositIntoFlexClient(_user, _voteWeight); - - // Create the proposal. - uint256 _proposalId = _createAndSubmitProposal(); - - // Express vote preference. + // Deposit into the client. vm.prank(_user); - flexClient.expressVote(_proposalId, _supportType); - - // Jump ahead so that we're outside of the proposal's voting period. - vm.roll(governor.proposalDeadline(_proposalId) + 1); - IGovernor.ProposalState status = IGovernor.ProposalState(uint32(governor.state(_proposalId))); + flexClient.deposit(_voteWeight); + assertEq(flexClient.deposits(_user), _voteWeight); - // We should not be able to castVote at this point. - vm.expectRevert( - abi.encodeWithSelector( - IGovernor.GovernorUnexpectedProposalState.selector, - _proposalId, - status, - bytes32(1 << uint8(IGovernor.ProposalState.Active)) - ) - ); - flexClient.castVote(_proposalId); + // _user should still NOT be able to express his/her vote on the proposal. + // Despite having a deposit balance, he/she didn't have a balance at the + // proposal snapshot. + vm.expectRevert(bytes("no weight")); + vm.prank(_user); + flexClient.expressVote(_proposalId, uint8(_supportType)); } - function testFuzz_NoDoubleVoting(address _user, uint208 _voteWeight, uint8 _supportType) public { - _voteWeight = _commonFuzzerAssumptions(_user, _voteWeight, _supportType); + function testFuzz_RevertOn_DoubleVotes(address _user, uint208 _voteWeight, uint8 _supportType) + public + { + _voteWeight = _assumeSafeVoteParams(_user, _voteWeight, _supportType); // Deposit some funds. _mintGovAndDepositIntoFlexClient(_user, _voteWeight); @@ -354,37 +567,34 @@ contract Vote is FlexVotingClientTest { assertEq(_abstainVotesExpressed, _abstainVotesExpressedInit); } - function testFuzz_UsersCannotExpressVotesPriorToDepositing( - address _user, - uint208 _voteWeight, - uint8 _supportType - ) public { - _voteWeight = _commonFuzzerAssumptions(_user, _voteWeight, _supportType); + function testFuzz_RevertOn_UnknownVoteType(address _user, uint208 _voteWeight, uint8 _supportType) + public + { + // Force vote type to be unrecognized. + vm.assume(_supportType > uint8(GCF.VoteType.Abstain)); - // Create the proposal *before* the user deposits anything. - uint256 _proposalId = _createAndSubmitProposal(); + vm.assume(_user != address(flexClient)); + // This max is a limitation of the fractional governance protocol storage. + _voteWeight = uint208(bound(_voteWeight, 1, type(uint128).max)); // Deposit some funds. _mintGovAndDepositIntoFlexClient(_user, _voteWeight); - // Now try to express a voting preference on the proposal. - assertEq(flexClient.deposits(_user), _voteWeight); - vm.expectRevert(bytes("no weight")); + // Create the proposal. + uint256 _proposalId = _createAndSubmitProposal(); + + // Now try to express a voting preference with a bogus support type. + vm.expectRevert(bytes("invalid support value, must be included in VoteType enum")); vm.prank(_user); flexClient.expressVote(_proposalId, _supportType); } +} - function testFuzz_UsersMustExpressWithKnownVoteType( - address _user, - uint208 _voteWeight, - uint8 _supportType - ) public { - // Force vote type to be unrecognized. - vm.assume(_supportType > uint8(GCF.VoteType.Abstain)); - - vm.assume(_user != address(flexClient)); - // This max is a limitation of the fractional governance protocol storage. - _voteWeight = uint208(bound(_voteWeight, 1, type(uint128).max)); +contract CastVote is FlexVotingClientTest { + function testFuzz_SubmitsVotesToGovernor(address _user, uint208 _voteWeight, uint8 _supportType) + public + { + _voteWeight = _assumeSafeVoteParams(_user, _voteWeight, _supportType); // Deposit some funds. _mintGovAndDepositIntoFlexClient(_user, _voteWeight); @@ -392,20 +602,40 @@ contract Vote is FlexVotingClientTest { // Create the proposal. uint256 _proposalId = _createAndSubmitProposal(); - // Now try to express a voting preference with a bogus support type. - vm.expectRevert(bytes("invalid support value, must be included in VoteType enum")); + // _user should now be able to express his/her vote on the proposal. vm.prank(_user); flexClient.expressVote(_proposalId, _supportType); + (uint256 _againstVotesExpressed, uint256 _forVotesExpressed, uint256 _abstainVotesExpressed) = + flexClient.proposalVotes(_proposalId); + assertEq(_forVotesExpressed, _supportType == uint8(GCF.VoteType.For) ? _voteWeight : 0); + assertEq(_againstVotesExpressed, _supportType == uint8(GCF.VoteType.Against) ? _voteWeight : 0); + assertEq(_abstainVotesExpressed, _supportType == uint8(GCF.VoteType.Abstain) ? _voteWeight : 0); + + // No votes have been cast yet. + (uint256 _againstVotes, uint256 _forVotes, uint256 _abstainVotes) = + governor.proposalVotes(_proposalId); + assertEq(_forVotes, 0); + assertEq(_againstVotes, 0); + assertEq(_abstainVotes, 0); + + // Submit votes on behalf of the flexClient. + flexClient.castVote(_proposalId); + + // Governor should now record votes from the flexClient. + (_againstVotes, _forVotes, _abstainVotes) = governor.proposalVotes(_proposalId); + assertEq(_forVotes, _forVotesExpressed); + assertEq(_againstVotes, _againstVotesExpressed); + assertEq(_abstainVotes, _abstainVotesExpressed); } - function testFuzz_VotingWeightIsSnapshotDependent( + function testFuzz_WeightIsSnapshotDependent( address _user, uint208 _voteWeightA, uint208 _voteWeightB, uint8 _supportType ) public { - _voteWeightA = _commonFuzzerAssumptions(_user, _voteWeightA, _supportType); - _voteWeightB = _commonFuzzerAssumptions(_user, _voteWeightB, _supportType); + _voteWeightA = _assumeSafeVoteParams(_user, _voteWeightA, _supportType); + _voteWeightB = _assumeSafeVoteParams(_user, _voteWeightB, _supportType); // Deposit some funds. _mintGovAndDepositIntoFlexClient(_user, _voteWeightA); @@ -438,7 +668,7 @@ contract Vote is FlexVotingClientTest { assertEq(_abstainVotes, _supportType == uint8(GCF.VoteType.Abstain) ? _voteWeightA : 0); } - function testFuzz_MultipleUsersCanCastVotes( + function testFuzz_TracksMultipleUsersVotes( address _userA, address _userB, uint208 _voteWeightA, @@ -501,7 +731,7 @@ contract Vote is FlexVotingClientTest { uint208 borrowAmountD; } - function testFuzz_VoteWeightIsScaledBasedOnPoolBalance(VoteWeightIsScaledTestVars memory _vars) + function testFuzz_ScalesVoteWeightBasedOnPoolBalance(VoteWeightIsScaledTestVars memory _vars) public { _vars.userA = address(0xbeef); @@ -602,7 +832,7 @@ contract Vote is FlexVotingClientTest { // This is important because it ensures you can't *gain* voting weight by // getting other people to not vote. - function testFuzz_VotingWeightIsAbandonedIfSomeoneDoesntExpress( + function testFuzz_AbandonsUnexpressedVotingWeight( uint208 _voteWeightA, uint208 _voteWeightB, uint8 _supportTypeA, @@ -615,9 +845,9 @@ contract Vote is FlexVotingClientTest { address(0xbabe), // userB address(0xf005ba11) // userC ]; - _voteWeightA = _commonFuzzerAssumptions(_users[0], _voteWeightA, _supportTypeA); - _voteWeightB = _commonFuzzerAssumptions(_users[1], _voteWeightB); - _borrowAmount = _commonFuzzerAssumptions(_users[2], _borrowAmount); + _voteWeightA = _assumeSafeVoteParams(_users[0], _voteWeightA, _supportTypeA); + _voteWeightB = _assumeSafeVoteParams(_users[1], _voteWeightB); + _borrowAmount = _assumeSafeVoteParams(_users[2], _borrowAmount); _voteWeightA = uint208(bound(_voteWeightA, 0, type(uint128).max)); _voteWeightB = uint208(bound(_voteWeightB, 0, type(uint128).max - _voteWeightA)); @@ -696,8 +926,8 @@ contract Vote is FlexVotingClientTest { address(0xbabe), // userB address(0xf005ba11) // userC ]; - _voteWeightA = _commonFuzzerAssumptions(_users[0], _voteWeightA, _supportTypeA); - _voteWeightB = _commonFuzzerAssumptions(_users[1], _voteWeightB); + _voteWeightA = _assumeSafeVoteParams(_users[0], _voteWeightA, _supportTypeA); + _voteWeightB = _assumeSafeVoteParams(_users[1], _voteWeightB); vm.assume(_voteWeightA + _voteWeightB < type(uint128).max); @@ -734,7 +964,7 @@ contract Vote is FlexVotingClientTest { if (_supportTypeA == uint8(GCF.VoteType.Abstain)) assertEq(_abstainVotes, _voteWeightA); } - function testFuzz_CanCastVotesMultipleTimesForTheSameProposal( + function testFuzz_CanCallMultipleTimesForTheSameProposal( address _userA, address _userB, uint208 _voteWeightA, @@ -792,6 +1022,66 @@ contract Vote is FlexVotingClientTest { assertEq(_againstVotes, _voteWeightA); // This should be unchanged! assertEq(_abstainVotes, _voteWeightB); // Second user's votes are now in. } + + function testFuzz_RevertWhen_NoVotesToCast(address _user, uint208 _voteWeight, uint8 _supportType) + public + { + _voteWeight = _assumeSafeVoteParams(_user, _voteWeight, _supportType); + + // Deposit some funds. + _mintGovAndDepositIntoFlexClient(_user, _voteWeight); + + // Create the proposal. + uint256 _proposalId = _createAndSubmitProposal(); + + // No one has expressed, there are no votes to cast. + vm.expectRevert(bytes("no votes expressed")); + flexClient.castVote(_proposalId); + + // _user expresses his/her vote on the proposal. + vm.prank(_user); + flexClient.expressVote(_proposalId, _supportType); + + // Submit votes on behalf of the flexClient. + flexClient.castVote(_proposalId); + + // All votes have been cast, there's nothing new to send to the governor. + vm.expectRevert(bytes("no votes expressed")); + flexClient.castVote(_proposalId); + } + + function testFuzz_RevertWhen_AfterVotingPeriod( + address _user, + uint208 _voteWeight, + uint8 _supportType + ) public { + _voteWeight = _assumeSafeVoteParams(_user, _voteWeight, _supportType); + + // Deposit some funds. + _mintGovAndDepositIntoFlexClient(_user, _voteWeight); + + // Create the proposal. + uint256 _proposalId = _createAndSubmitProposal(); + + // Express vote preference. + vm.prank(_user); + flexClient.expressVote(_proposalId, _supportType); + + // Jump ahead so that we're outside of the proposal's voting period. + vm.roll(governor.proposalDeadline(_proposalId) + 1); + IGovernor.ProposalState status = IGovernor.ProposalState(uint32(governor.state(_proposalId))); + + // We should not be able to castVote at this point. + vm.expectRevert( + abi.encodeWithSelector( + IGovernor.GovernorUnexpectedProposalState.selector, + _proposalId, + status, + bytes32(1 << uint8(IGovernor.ProposalState.Active)) + ) + ); + flexClient.castVote(_proposalId); + } } contract Borrow is FlexVotingClientTest { @@ -802,8 +1092,8 @@ contract Borrow is FlexVotingClientTest { uint208 _borrowAmount ) public { vm.assume(_borrower != address(0)); - _depositAmount = _commonFuzzerAssumptions(_depositer, _depositAmount); - _borrowAmount = _commonFuzzerAssumptions(_borrower, _borrowAmount); + _depositAmount = _assumeSafeVoteParams(_depositer, _depositAmount); + _borrowAmount = _assumeSafeVoteParams(_borrower, _borrowAmount); vm.assume(_depositAmount > _borrowAmount); // Deposit some funds. diff --git a/test/MockFlexVotingClient.sol b/test/MockFlexVotingClient.sol index dfa03fd..9de70e7 100644 --- a/test/MockFlexVotingClient.sol +++ b/test/MockFlexVotingClient.sol @@ -28,6 +28,26 @@ contract MockFlexVotingClient is FlexVotingClient { return deposits[_user]; } + function exposed_rawBalanceOf(address _user) external view returns (uint208) { + return _rawBalanceOf(_user); + } + + function exposed_castVoteReasonString() external returns (string memory) { + return _castVoteReasonString(); + } + + function exposed_selfDelegate() external { + return _selfDelegate(); + } + + function exposed_setDeposits(address _user, uint208 _amount) external { + deposits[_user] = _amount; + } + + function exposed_checkpointRawBalanceOf(address _user) external { + return _checkpointRawBalanceOf(_user); + } + /// @notice Allow a holder of the governance token to deposit it into the pool. /// @param _amount The amount to be deposited. function deposit(uint208 _amount) public {