diff --git a/src/Governance.sol b/src/Governance.sol index b9f426fc..9502fa37 100644 --- a/src/Governance.sol +++ b/src/Governance.sol @@ -248,8 +248,19 @@ contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, IGovernance } /// @inheritdoc IGovernance - function calculateVotingThreshold() public view returns (uint256) { - uint256 snapshotVotes = votesSnapshot.votes; + function getLatestVotingThreshold() public view returns (uint256) { + uint256 snapshotVotes = votesSnapshot.votes; /// @audit technically can be out of synch + + return calculateVotingThreshold(snapshotVotes); + } + + function calculateVotingThreshold() public returns (uint256) { + (VoteSnapshot memory snapshot, ) = _snapshotVotes(); + + return calculateVotingThreshold(snapshot.votes); + } + + function calculateVotingThreshold(uint256 snapshotVotes) public view returns (uint256) { if (snapshotVotes == 0) return 0; uint256 minVotes; // to reach MIN_CLAIM: snapshotVotes * MIN_CLAIM / boldAccrued @@ -359,6 +370,10 @@ contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, IGovernance (VoteSnapshot memory votesSnapshot_,) = _snapshotVotes(); (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) = _snapshotVotesForInitiative(_initiative); + return getInitiativeState(_initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState); + } + + function getInitiativeState(address _initiative, VoteSnapshot memory votesSnapshot_, InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) public view returns (InitiativeStatus status, uint16 lastEpochClaim, uint256 claimableAmount) { lastEpochClaim = initiativeStates[_initiative].lastEpochClaim; // == Already Claimed Condition == // @@ -368,12 +383,13 @@ contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, IGovernance } // == Disabled Condition == // - // TODO: If a initiative is disabled, we return false and the last epoch claim + // If a initiative is disabled, we return false and the last epoch claim if(registeredInitiatives[_initiative] == UNREGISTERED_INITIATIVE) { - return (InitiativeStatus.DISABLED, lastEpochClaim, 0); /// @audit By definition it must have zero rewards + return (InitiativeStatus.DISABLED, lastEpochClaim, 0); /// By definition it has zero rewards } - uint256 votingTheshold = calculateVotingThreshold(); + // NOTE: Pass the snapshot value so we get accurate result + uint256 votingTheshold = calculateVotingThreshold(votesSnapshot_.votes); // If it's voted and can get rewards // Votes > calculateVotingThreshold @@ -450,7 +466,6 @@ contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, IGovernance (, GlobalState memory state) = _snapshotVotes(); - uint256 votingThreshold = calculateVotingThreshold(); uint16 currentEpoch = epoch(); UserState memory userState = userStates[msg.sender]; diff --git a/src/interfaces/IGovernance.sol b/src/interfaces/IGovernance.sol index 5e0592ee..e8c3c9c1 100644 --- a/src/interfaces/IGovernance.sol +++ b/src/interfaces/IGovernance.sol @@ -223,7 +223,7 @@ interface IGovernance { /// - 4% of the total voting LQTY in the previous epoch /// - or the minimum number of votes necessary to claim at least MIN_CLAIM BOLD /// @return votingThreshold Voting threshold - function calculateVotingThreshold() external view returns (uint256 votingThreshold); + function getLatestVotingThreshold() external view returns (uint256 votingThreshold); /// @notice Snapshots votes for the previous epoch and accrues funds for the current epoch /// @param _initiative Address of the initiative diff --git a/test/Governance.t.sol b/test/Governance.t.sol index 84846c8f..f3a922c3 100644 --- a/test/Governance.t.sol +++ b/test/Governance.t.sol @@ -388,7 +388,7 @@ contract GovernanceTest is Test { governance.lqtyToVotes(_lqtyAmount, _currentTimestamp, _averageTimestamp); } - function test_calculateVotingThreshold() public { + function test_getLatestVotingThreshold() public { governance = new Governance( address(lqty), address(lusd), @@ -411,7 +411,7 @@ contract GovernanceTest is Test { ); // is 0 when the previous epochs votes are 0 - assertEq(governance.calculateVotingThreshold(), 0); + assertEq(governance.getLatestVotingThreshold(), 0); // check that votingThreshold is is high enough such that MIN_CLAIM is met IGovernance.VoteSnapshot memory snapshot = IGovernance.VoteSnapshot(1e18, 1); @@ -428,7 +428,7 @@ contract GovernanceTest is Test { vm.store(address(governance), bytes32(uint256(1)), bytes32(abi.encode(boldAccrued))); assertEq(governance.boldAccrued(), 1000e18); - assertEq(governance.calculateVotingThreshold(), MIN_CLAIM / 1000); + assertEq(governance.getLatestVotingThreshold(), MIN_CLAIM / 1000); // check that votingThreshold is 4% of votes of previous epoch governance = new Governance( @@ -466,7 +466,7 @@ contract GovernanceTest is Test { vm.store(address(governance), bytes32(uint256(1)), bytes32(abi.encode(boldAccrued))); assertEq(governance.boldAccrued(), 1000e18); - assertEq(governance.calculateVotingThreshold(), 10000e18 * 0.04); + assertEq(governance.getLatestVotingThreshold(), 10000e18 * 0.04); } // should not revert under any state @@ -512,7 +512,7 @@ contract GovernanceTest is Test { vm.store(address(governance), bytes32(uint256(1)), bytes32(abi.encode(_boldAccrued))); assertEq(governance.boldAccrued(), _boldAccrued); - governance.calculateVotingThreshold(); + governance.getLatestVotingThreshold(); } function test_registerInitiative() public { @@ -715,7 +715,7 @@ contract GovernanceTest is Test { (IGovernance.VoteSnapshot memory snapshot, IGovernance.InitiativeVoteSnapshot memory initiativeVoteSnapshot1) = governance.snapshotVotesForInitiative(baseInitiative1); (, IGovernance.InitiativeVoteSnapshot memory initiativeVoteSnapshot2) = governance.snapshotVotesForInitiative(baseInitiative2); - uint256 threshold = governance.calculateVotingThreshold(); + uint256 threshold = governance.getLatestVotingThreshold(); assertLt(initiativeVoteSnapshot1.votes, threshold, "it didn't get rewards"); uint256 votingPowerWithProjection = governance.lqtyToVotes(voteLQTY1, governance.epochStart() + governance.EPOCH_DURATION(), averageStakingTimestampVoteLQTY1); @@ -758,7 +758,7 @@ contract GovernanceTest is Test { (IGovernance.VoteSnapshot memory snapshot, IGovernance.InitiativeVoteSnapshot memory initiativeVoteSnapshot1) = governance.snapshotVotesForInitiative(baseInitiative1); - uint256 threshold = governance.calculateVotingThreshold(); + uint256 threshold = governance.getLatestVotingThreshold(); assertLt(initiativeVoteSnapshot1.votes, threshold, "it didn't get rewards"); } @@ -1157,7 +1157,7 @@ contract GovernanceTest is Test { console.log("snapshot votes", votes); console.log("snapshot vetos", vetos); - console.log("governance.calculateVotingThreshold()", governance.calculateVotingThreshold()); + console.log("governance.getLatestVotingThreshold()", governance.getLatestVotingThreshold()); assertEq(governance.claimForInitiative(baseInitiative2), 0, "zero 2"); assertEq(governance.claimForInitiative(baseInitiative2), 0, "zero 3"); diff --git a/test/TEST.md b/test/TEST.md index 05f23f21..e0095552 100644 --- a/test/TEST.md +++ b/test/TEST.md @@ -40,7 +40,7 @@ Governance: - should return the correct number of seconds elapsed within an epoch for a given block.timestamp - lqtyToVotes() - should not revert under any input -- calculateVotingThreshold() +- getLatestVotingThreshold() - should return a votingThreshold that's either - high enough such that MIN_CLAIM is met - 4% of the votes from the previous epoch