From 01cbac16191f1603fc154940808930a9c43ca63a Mon Sep 17 00:00:00 2001 From: RickGriff Date: Mon, 9 Dec 2024 17:28:43 +0700 Subject: [PATCH] Remove precision scaling and fix tests --- src/BribeInitiative.sol | 6 +- src/Governance.sol | 37 +- test/Governance.t.sol | 353 ++++++++---------- test/VotingPower.t.sol | 212 +++++------ .../recon/properties/GovernanceProperties.sol | 19 +- test/recon/properties/RevertProperties.sol | 14 +- 6 files changed, 278 insertions(+), 363 deletions(-) diff --git a/src/BribeInitiative.sol b/src/BribeInitiative.sol index 4ec2cb9..31d64af 100644 --- a/src/BribeInitiative.sol +++ b/src/BribeInitiative.sol @@ -72,8 +72,6 @@ contract BribeInitiative is IInitiative, IBribeInitiative { bribeToken.safeTransferFrom(msg.sender, address(this), _bribeTokenAmount); } - uint256 constant TIMESTAMP_PRECISION = 1e26; - function _claimBribe( address _user, uint256 _epoch, @@ -104,9 +102,7 @@ contract BribeInitiative is IInitiative, IBribeInitiative { require(totalLQTYAllocation.lqty > 0, "BribeInitiative: total-lqty-allocation-zero"); // NOTE: SCALING!!! | The timestamp will work until type(uint32).max | After which the math will eventually overflow - uint256 scaledEpochEnd = ( - governance.EPOCH_START() + _epoch * governance.EPOCH_DURATION() - ) * TIMESTAMP_PRECISION; + uint256 scaledEpochEnd = governance.EPOCH_START() + _epoch * governance.EPOCH_DURATION(); uint256 totalVotes = governance.lqtyToVotes(totalLQTYAllocation.lqty, scaledEpochEnd, totalLQTYAllocation.offset); if (totalVotes != 0) { diff --git a/src/Governance.sol b/src/Governance.sol index f9bc878..a971762 100644 --- a/src/Governance.sol +++ b/src/Governance.sol @@ -75,9 +75,6 @@ contract Governance is MultiDelegateCall, UserProxyFactory, ReentrancyGuard, Own uint256 constant UNREGISTERED_INITIATIVE = type(uint256).max; - // 100 Million LQTY will be necessary to make the rounding error cause 1 second of loss per operation - uint256 public constant TIMESTAMP_PRECISION = 1e26; - constructor( address _lqty, address _lusd, @@ -146,7 +143,7 @@ contract Governance is MultiDelegateCall, UserProxyFactory, ReentrancyGuard, Own // Assert that we have resetted here // TODO: Remove, as now unecessary UserState memory userState = userStates[msg.sender]; - require(userState.unallocatedLQTY == 0, "Governance: must-be-zero-allocation"); + require(userState.allocatedLQTY == 0, "Governance: must-be-zero-allocation"); address userProxyAddress = deriveUserProxyAddress(msg.sender); @@ -318,7 +315,7 @@ contract Governance is MultiDelegateCall, UserProxyFactory, ReentrancyGuard, Own snapshot.votes = lqtyToVotes( state.countedVoteLQTY, - epochStart() * TIMESTAMP_PRECISION, + epochStart(), state.countedVoteOffset ); snapshot.forEpoch = currentEpoch - 1; @@ -359,12 +356,11 @@ contract Governance is MultiDelegateCall, UserProxyFactory, ReentrancyGuard, Own if (initiativeSnapshot.forEpoch < currentEpoch - 1) { shouldUpdate = true; - uint256 start = epochStart() * TIMESTAMP_PRECISION; + uint256 start = epochStart(); uint256 votes = lqtyToVotes(initiativeState.voteLQTY, start, initiativeState.voteOffset); uint256 vetos = lqtyToVotes(initiativeState.vetoLQTY, start, initiativeState.vetoOffset); - // NOTE: Upscaling to u224 is safe initiativeSnapshot.votes = votes; initiativeSnapshot.vetos = vetos; @@ -445,25 +441,10 @@ contract Governance is MultiDelegateCall, UserProxyFactory, ReentrancyGuard, Own // == Rewards Conditions (votes can be zero, logic is the same) == // // By definition if _votesForInitiativeSnapshot.votes > 0 then _votesSnapshot.votes > 0 + if (_votesForInitiativeSnapshot.votes > votingTheshold && _votesForInitiativeSnapshot.votes > _votesForInitiativeSnapshot.vetos) + { + uint256 claim = _votesForInitiativeSnapshot.votes * boldAccrued / _votesSnapshot.votes; - uint256 upscaledInitiativeVotes = uint256(_votesForInitiativeSnapshot.votes); - uint256 upscaledInitiativeVetos = uint256(_votesForInitiativeSnapshot.vetos); - uint256 upscaledTotalVotes = uint256(_votesSnapshot.votes); - - if (upscaledInitiativeVotes > votingTheshold && !(upscaledInitiativeVetos >= upscaledInitiativeVotes)) { - /// 2^208 means we only have 2^48 left - /// Therefore we need to scale the value down by 4 orders of magnitude to make it fit - assert(upscaledInitiativeVotes * 1e14 / (VOTING_THRESHOLD_FACTOR / 1e4) > upscaledTotalVotes); - - // 34 times when using 0.03e18 -> 33.3 + 1-> 33 + 1 = 34 - uint256 CUSTOM_PRECISION = WAD / VOTING_THRESHOLD_FACTOR + 1; - - /// Because of the updated timestamp, we can run into overflows if we multiply by `boldAccrued` - /// We use `CUSTOM_PRECISION` for this reason, a smaller multiplicative value - /// The change SHOULD be safe because we already check for `threshold` before getting into these lines - /// As an alternative, this line could be replaced by https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol - uint256 claim = - upscaledInitiativeVotes * CUSTOM_PRECISION / upscaledTotalVotes * boldAccrued / CUSTOM_PRECISION; return (InitiativeStatus.CLAIMABLE, lastEpochClaim, claim); } @@ -471,8 +452,8 @@ contract Governance is MultiDelegateCall, UserProxyFactory, ReentrancyGuard, Own // e.g. if `UNREGISTRATION_AFTER_EPOCHS` is 4, the 4th epoch flip that would result in SKIP, will result in the initiative being `UNREGISTERABLE` if ( (_initiativeState.lastEpochClaim + UNREGISTRATION_AFTER_EPOCHS < currentEpoch - 1) - || upscaledInitiativeVetos > upscaledInitiativeVotes - && upscaledInitiativeVetos > votingTheshold * UNREGISTRATION_THRESHOLD_FACTOR / WAD + || _votesForInitiativeSnapshot.vetos > _votesForInitiativeSnapshot.votes + && _votesForInitiativeSnapshot.vetos > votingTheshold * UNREGISTRATION_THRESHOLD_FACTOR / WAD ) { return (InitiativeStatus.UNREGISTERABLE, lastEpochClaim, 0); } @@ -506,7 +487,7 @@ contract Governance is MultiDelegateCall, UserProxyFactory, ReentrancyGuard, Own // Check against the user's total voting power, so include both allocated and unallocated LQTY lqtyToVotes( stakingV1.stakes(userProxyAddress), - epochStart() * TIMESTAMP_PRECISION, + epochStart(), totalUserOffset ) >= upscaledSnapshotVotes * REGISTRATION_THRESHOLD_FACTOR / WAD, "Governance: insufficient-lqty" diff --git a/test/Governance.t.sol b/test/Governance.t.sol index c82fb4a..90dbbc0 100644 --- a/test/Governance.t.sol +++ b/test/Governance.t.sol @@ -129,50 +129,58 @@ abstract contract GovernanceTest is Test { _expectInsufficientAllowanceAndBalance(); governance.depositLQTY(type(uint256).max); + uint256 lqtyDeposit = 2e18; + // should not revert if the user doesn't have a UserProxy deployed yet address userProxy = governance.deriveUserProxyAddress(user); - lqty.approve(address(userProxy), 1e18); + lqty.approve(address(userProxy), lqtyDeposit); // vm.expectEmit("DepositLQTY", abi.encode(user, 1e18)); - // deploy and deposit 1 LQTY - governance.depositLQTY(1e18); - assertEq(UserProxy(payable(userProxy)).staked(), 1e18); - (uint256 allocatedLQTY, uint256 averageStakingTimestamp) = governance.userStates(user); - assertEq(allocatedLQTY, 0); - // first deposit should have an averageStakingTimestamp if block.timestamp - assertEq(averageStakingTimestamp, block.timestamp * 1e26); + // deploy and deposit 2 LQTY + governance.depositLQTY(lqtyDeposit); + assertEq(UserProxy(payable(userProxy)).staked(), lqtyDeposit); + (uint256 unallocatedLQTY, uint256 unallocatedOffset,,) = governance.userStates(user); + assertEq(unallocatedLQTY, lqtyDeposit); + + uint256 expectedOffset1 = block.timestamp * lqtyDeposit; + // first deposit should have an unallocated offset of deposit * block.timestamp + assertEq(unallocatedOffset, expectedOffset1); vm.warp(block.timestamp + timeIncrease); - lqty.approve(address(userProxy), 1e18); - governance.depositLQTY(1e18); - assertEq(UserProxy(payable(userProxy)).staked(), 2e18); - (allocatedLQTY, averageStakingTimestamp) = governance.userStates(user); - assertEq(allocatedLQTY, 0); - // subsequent deposits should have a stake weighted average - assertEq(averageStakingTimestamp, (block.timestamp - timeIncrease / 2) * 1e26, "Avg ts"); + // Deposit again + lqty.approve(address(userProxy), lqtyDeposit); + governance.depositLQTY(lqtyDeposit); + assertEq(UserProxy(payable(userProxy)).staked(), lqtyDeposit * 2); + (unallocatedLQTY, unallocatedOffset,,) = governance.userStates(user); + assertEq(unallocatedLQTY, lqtyDeposit * 2); - // withdraw 0.5 half of LQTY + uint256 expectedOffset2 = expectedOffset1 + block.timestamp * lqtyDeposit; + // subsequent deposits should result in an increased unallocated offset + assertEq(unallocatedOffset, expectedOffset2, "unallocated offset"); + + // withdraw half of LQTY vm.warp(block.timestamp + timeIncrease); vm.startPrank(address(this)); vm.expectRevert("Governance: user-proxy-not-deployed"); - governance.withdrawLQTY(1e18); + governance.withdrawLQTY(lqtyDeposit / 2); vm.stopPrank(); vm.startPrank(user); - governance.withdrawLQTY(1e18); - assertEq(UserProxy(payable(userProxy)).staked(), 1e18); - (allocatedLQTY, averageStakingTimestamp) = governance.userStates(user); - assertEq(allocatedLQTY, 0); - assertEq(averageStakingTimestamp, ((block.timestamp - timeIncrease) - timeIncrease / 2) * 1e26, "avg ts2"); + governance.withdrawLQTY(lqtyDeposit / 2); + assertEq(UserProxy(payable(userProxy)).staked(), lqtyDeposit / 2); + (unallocatedLQTY, unallocatedOffset,,) = governance.userStates(user); + assertEq(unallocatedLQTY, lqtyDeposit / 2); + // Withdrawing half of the LQTY should also halve the offset, i.e. withdraw "proportionally" from all past deposits + assertEq(unallocatedOffset, expectedOffset2 / 2, "unallocated offset2"); // withdraw remaining LQTY - governance.withdrawLQTY(1e18); + governance.withdrawLQTY(lqtyDeposit / 2); assertEq(UserProxy(payable(userProxy)).staked(), 0); - (allocatedLQTY, averageStakingTimestamp) = governance.userStates(user); - assertEq(allocatedLQTY, 0); - assertEq(averageStakingTimestamp, ((block.timestamp - timeIncrease) - timeIncrease / 2) * 1e26, "avg ts3"); + (unallocatedLQTY, unallocatedOffset,,) = governance.userStates(user); + assertEq(unallocatedLQTY, 0); + assertEq(unallocatedOffset, 0, "unallocated offset2"); vm.stopPrank(); } @@ -241,9 +249,9 @@ abstract contract GovernanceTest is Test { // deploy and deposit 1 LQTY governance.depositLQTYViaPermit(1e18, permitParams); assertEq(UserProxy(payable(userProxy)).staked(), 1e18); - (uint256 allocatedLQTY, uint256 averageStakingTimestamp) = governance.userStates(wallet.addr); - assertEq(allocatedLQTY, 0); - assertEq(averageStakingTimestamp, block.timestamp * 1e26); + (uint256 unallocatedLQTY, uint256 unallocatedOffset,,) = governance.userStates(wallet.addr); + assertEq(unallocatedLQTY, 1e18); + assertEq(unallocatedOffset, block.timestamp); } function test_claimFromStakingV1() public { @@ -322,8 +330,8 @@ abstract contract GovernanceTest is Test { } // should not revert under any input - function test_lqtyToVotes(uint256 _lqtyAmount, uint256 _currentTimestamp, uint256 _averageTimestamp) public { - governance.lqtyToVotes(_lqtyAmount, _currentTimestamp, _averageTimestamp); + function test_lqtyToVotes(uint256 _lqtyAmount, uint256 _currentTimestamp, uint256 _offset) public { + governance.lqtyToVotes(_lqtyAmount, _currentTimestamp, _offset); } function test_getLatestVotingThreshold() public { @@ -553,16 +561,16 @@ abstract contract GovernanceTest is Test { governance.allocateLQTY(initiativesToReset, initiatives, deltaLQTYVotes, deltaLQTYVetos); - (uint256 allocatedLQTY,) = governance.userStates(user); + (,,uint256 allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, 1_000e18); - (uint256 voteLQTY1,, uint256 averageStakingTimestampVoteLQTY1,,) = governance.initiativeStates(baseInitiative1); + (uint256 voteLQTY1, uint256 voteOffset1,,,) = governance.initiativeStates(baseInitiative1); (uint256 voteLQTY2,,,,) = governance.initiativeStates(baseInitiative2); // Get power at time of vote uint256 votingPower = governance.lqtyToVotes( - voteLQTY1, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY1 + voteLQTY1, block.timestamp, voteOffset1 ); assertGt(votingPower, 0, "Non zero power"); @@ -585,7 +593,7 @@ abstract contract GovernanceTest is Test { uint256 votingPowerWithProjection = governance.lqtyToVotes( voteLQTY1, uint256(governance.epochStart() + governance.EPOCH_DURATION()), - averageStakingTimestampVoteLQTY1 + voteOffset1 ); assertLt(votingPower, threshold, "Current Power is not enough - Desynch A"); assertLt(votingPowerWithProjection, threshold, "Future Power is also not enough - Desynch B"); @@ -703,7 +711,7 @@ abstract contract GovernanceTest is Test { // Get state here // Get initiative state - (uint256 b4_countedVoteLQTY, uint256 b4_countedVoteLQTYAverageTimestamp) = governance.globalState(); + (uint256 b4_countedVoteLQTY, uint256 b4_countedVoteOffset) = governance.globalState(); // I want to remove my allocation initiativesToReset = new address[](2); @@ -722,11 +730,11 @@ abstract contract GovernanceTest is Test { { // Get state here // TODO Get initiative state - (uint256 after_countedVoteLQTY, uint256 after_countedVoteLQTYAverageTimestamp) = governance.globalState(); + (uint256 after_countedVoteLQTY, uint256 after_countedVoteOffset) = governance.globalState(); assertEq(after_countedVoteLQTY, b4_countedVoteLQTY, "LQTY should not change"); assertEq( - b4_countedVoteLQTYAverageTimestamp, after_countedVoteLQTYAverageTimestamp, "Avg TS should not change" + b4_countedVoteOffset, after_countedVoteOffset, "Offset should not change" ); } } @@ -775,8 +783,8 @@ abstract contract GovernanceTest is Test { // Grab values b4 unregistering and b4 removing user allocation - (uint256 b4_countedVoteLQTY, uint256 b4_countedVoteLQTYAverageTimestamp) = governance.globalState(); - (uint256 b4_allocatedLQTY, uint256 b4_averageStakingTimestamp) = governance.userStates(user); + (uint256 b4_countedVoteLQTY, uint256 b4_countedVoteOffset) = governance.globalState(); + (,,uint256 b4_allocatedLQTY, uint256 b4_allocatedOffset) = governance.userStates(user); (uint256 b4_voteLQTY,,,,) = governance.initiativeStates(baseInitiative1); // Unregistering @@ -792,14 +800,14 @@ abstract contract GovernanceTest is Test { assertEq(after_countedVoteLQTY, b4_countedVoteLQTY - b4_voteLQTY, "Global Lqty change after unregister"); assertEq(1e18, b4_voteLQTY, "sanity check"); - (uint256 after_allocatedLQTY, uint256 after_averageStakingTimestamp) = governance.userStates(user); + (,,uint256 after_allocatedLQTY, uint256 after_unallocatedOffset) = governance.userStates(user); // We expect no changes here ( uint256 after_voteLQTY, + uint256 after_voteOffset, uint256 after_vetoLQTY, - uint256 after_averageStakingTimestampVoteLQTY, - uint256 after_averageStakingTimestampVetoLQTY, + uint256 after_vetoOffset, uint256 after_lastEpochClaim ) = governance.initiativeStates(baseInitiative1); assertEq(b4_voteLQTY, after_voteLQTY, "Initiative votes are the same"); @@ -818,16 +826,15 @@ abstract contract GovernanceTest is Test { // After user counts LQTY the { - (uint256 after_user_countedVoteLQTY, uint256 after_user_countedVoteLQTYAverageTimestamp) = + (uint256 after_user_countedVoteLQTY, uint256 after_user_countedVoteOffset) = governance.globalState(); // The LQTY was already removed assertEq(after_user_countedVoteLQTY, 0, "Removal 1"); } // User State allocated LQTY changes by entire previous allocation amount - // Timestamp should not change { - (uint256 after_user_allocatedLQTY,) = governance.userStates(user); + (,,uint256 after_user_allocatedLQTY,) = governance.userStates(user); assertEq(after_user_allocatedLQTY, 0, "Removal 2"); } @@ -863,7 +870,7 @@ abstract contract GovernanceTest is Test { int256[] memory deltaLQTYVetos = new int256[](2); governance.allocateLQTY(initiativesToReset, initiatives, deltaLQTYVotes, deltaLQTYVetos); - (uint256 allocatedB4Test,,) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); + (uint256 allocatedB4Test,,,,)= governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); console.log("allocatedB4Test", allocatedB4Test); vm.warp(block.timestamp + governance.EPOCH_DURATION()); @@ -875,11 +882,11 @@ abstract contract GovernanceTest is Test { removeInitiatives[0] = baseInitiative1; removeInitiatives[1] = baseInitiative2; - (uint256 allocatedB4Removal,,) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); + (uint256 allocatedB4Removal,,,,) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); console.log("allocatedB4Removal", allocatedB4Removal); governance.resetAllocations(removeInitiatives, true); - (uint256 allocatedAfterRemoval,,) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); + (uint256 allocatedAfterRemoval,,,,)= governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); console.log("allocatedAfterRemoval", allocatedAfterRemoval); vm.expectRevert("Governance: nothing to reset"); @@ -888,7 +895,7 @@ abstract contract GovernanceTest is Test { int256[] memory removeDeltaLQTYVetos = new int256[](2); vm.expectRevert("Governance: voting nothing"); governance.allocateLQTY(initiativesToReset, removeInitiatives, removeDeltaLQTYVotes, removeDeltaLQTYVetos); - (uint256 allocatedAfter,,) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); + (uint256 allocatedAfter,,,,) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); console.log("allocatedAfter", allocatedAfter); } @@ -907,7 +914,7 @@ abstract contract GovernanceTest is Test { lqty.approve(address(userProxy), 1e18); governance.depositLQTY(1e18); - (uint256 allocatedLQTY, uint256 averageStakingTimestampUser) = governance.userStates(user); + (,,uint256 allocatedLQTY, uint256 allocatedOffset) = governance.userStates(user); assertEq(allocatedLQTY, 0); (uint256 countedVoteLQTY,) = governance.globalState(); assertEq(countedVoteLQTY, 0); @@ -926,30 +933,26 @@ abstract contract GovernanceTest is Test { vm.warp(block.timestamp + governance.EPOCH_DURATION()); governance.allocateLQTY(initiativesToReset, initiatives, deltaLQTYVotes, deltaLQTYVetos); - (allocatedLQTY,) = governance.userStates(user); + (,,allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, 1e18); ( uint256 voteLQTY, + uint256 voteOffset, uint256 vetoLQTY, - uint256 averageStakingTimestampVoteLQTY, - uint256 averageStakingTimestampVetoLQTY, + uint256 vetoOffset, ) = governance.initiativeStates(baseInitiative1); // should update the `voteLQTY` and `vetoLQTY` variables assertEq(voteLQTY, 1e18); assertEq(vetoLQTY, 0); - // should update the average staking timestamp for the initiative based on the average staking timestamp of the user's - // voting and vetoing LQTY - assertEq(averageStakingTimestampVoteLQTY, (block.timestamp - governance.EPOCH_DURATION()) * 1e26); - assertEq(averageStakingTimestampVoteLQTY, averageStakingTimestampUser); - assertEq(averageStakingTimestampVetoLQTY, 0); + // TODO: assertions re: initiative vote & veto offsets // should remove or add the initiatives voting LQTY from the counter (countedVoteLQTY,) = governance.globalState(); assertEq(countedVoteLQTY, 1e18); uint256 atEpoch; - (voteLQTY, vetoLQTY, atEpoch) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); + (voteLQTY, vetoLQTY,,, atEpoch) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); // should update the allocation mapping from user to initiative assertEq(voteLQTY, 1e18); assertEq(vetoLQTY, 0); @@ -973,8 +976,8 @@ abstract contract GovernanceTest is Test { lqty.approve(address(user2Proxy), 1e18); governance.depositLQTY(1e18); - (, uint256 averageAge) = governance.userStates(user2); - assertEq(governance.lqtyToVotes(1e18, uint256(block.timestamp) * uint256(1e26), averageAge), 0); + (,,,uint256 allocatedOffset2) = governance.userStates(user2); + assertEq(governance.lqtyToVotes(1e18, uint256(block.timestamp), allocatedOffset2), 0); deltaLQTYVetos[0] = 1e18; @@ -986,16 +989,14 @@ abstract contract GovernanceTest is Test { governance.allocateLQTY(initiativesToReset, initiatives, deltaLQTYVotes, deltaLQTYVetos); // should update the user's allocated LQTY balance - (allocatedLQTY,) = governance.userStates(user2); + (,,allocatedLQTY,) = governance.userStates(user2); assertEq(allocatedLQTY, 1e18); - (voteLQTY, vetoLQTY, averageStakingTimestampVoteLQTY, averageStakingTimestampVetoLQTY,) = + (voteLQTY, voteOffset, vetoLQTY, vetoOffset,) = governance.initiativeStates(baseInitiative1); assertEq(voteLQTY, 2e18); assertEq(vetoLQTY, 0); - assertEq(averageStakingTimestampVoteLQTY, (block.timestamp - governance.EPOCH_DURATION()) * 1e26); - assertGt(averageStakingTimestampVoteLQTY, averageStakingTimestampUser); - assertEq(averageStakingTimestampVetoLQTY, 0); + // TODO: assertions re: initiative vote + veto offsets // should revert if the user doesn't have enough unallocated LQTY available vm.expectRevert("Governance: must-allocate-zero"); @@ -1007,18 +1008,18 @@ abstract contract GovernanceTest is Test { initiatives[0] = baseInitiative1; governance.resetAllocations(initiatives, true); - (allocatedLQTY,) = governance.userStates(user2); + (,,allocatedLQTY,) = governance.userStates(user2); assertEq(allocatedLQTY, 0); (countedVoteLQTY,) = governance.globalState(); console.log("countedVoteLQTY: ", countedVoteLQTY); assertEq(countedVoteLQTY, 1e18); - (voteLQTY, vetoLQTY, averageStakingTimestampVoteLQTY, averageStakingTimestampVetoLQTY,) = + (voteLQTY,voteOffset, vetoLQTY, vetoOffset,) = governance.initiativeStates(baseInitiative1); assertEq(voteLQTY, 1e18); assertEq(vetoLQTY, 0); - assertEq(averageStakingTimestampVoteLQTY, averageStakingTimestampUser); - assertEq(averageStakingTimestampVetoLQTY, 0); + // TODO: assertion re: vote offset + assertEq(vetoOffset, 0); vm.stopPrank(); } @@ -1031,7 +1032,7 @@ abstract contract GovernanceTest is Test { lqty.approve(address(userProxy), 1e18); governance.depositLQTY(1e18); - (uint256 allocatedLQTY, uint256 averageStakingTimestampUser) = governance.userStates(user); + (,,uint256 allocatedLQTY, uint256 allocatedOffset) = governance.userStates(user); assertEq(allocatedLQTY, 0); (uint256 countedVoteLQTY,) = governance.globalState(); assertEq(countedVoteLQTY, 0); @@ -1050,30 +1051,28 @@ abstract contract GovernanceTest is Test { vm.warp(block.timestamp + governance.EPOCH_DURATION()); governance.allocateLQTY(initiativesToReset, initiatives, deltaLQTYVotes, deltaLQTYVetos); - (allocatedLQTY,) = governance.userStates(user); + (,,allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, 1e18); ( uint256 voteLQTY, + uint256 voteOffset, uint256 vetoLQTY, - uint256 averageStakingTimestampVoteLQTY, - uint256 averageStakingTimestampVetoLQTY, + uint256 vetoOffset, ) = governance.initiativeStates(baseInitiative1); // should update the `voteLQTY` and `vetoLQTY` variables assertEq(voteLQTY, 1e18); assertEq(vetoLQTY, 0); // should update the average staking timestamp for the initiative based on the average staking timestamp of the user's // voting and vetoing LQTY - assertEq(averageStakingTimestampVoteLQTY, (block.timestamp - governance.EPOCH_DURATION()) * 1e26, "TS"); - assertEq(averageStakingTimestampVoteLQTY, averageStakingTimestampUser); - assertEq(averageStakingTimestampVetoLQTY, 0); + // TODO: assertions re: vote + veto offsets // should remove or add the initiatives voting LQTY from the counter (countedVoteLQTY,) = governance.globalState(); assertEq(countedVoteLQTY, 1e18); uint256 atEpoch; - (voteLQTY, vetoLQTY, atEpoch) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); + (voteLQTY,,vetoLQTY,,atEpoch) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); // should update the allocation mapping from user to initiative assertEq(voteLQTY, 1e18); assertEq(vetoLQTY, 0); @@ -1097,8 +1096,8 @@ abstract contract GovernanceTest is Test { lqty.approve(address(user2Proxy), 1e18); governance.depositLQTY(1e18); - (, uint256 averageAge) = governance.userStates(user2); - assertEq(governance.lqtyToVotes(1e18, uint256(block.timestamp) * uint256(1e26), averageAge), 0); + (,uint256 unallocatedOffset,,) = governance.userStates(user2); + assertEq(governance.lqtyToVotes(1e18, block.timestamp, unallocatedOffset), 0); deltaLQTYVetos[0] = 1e18; @@ -1110,16 +1109,14 @@ abstract contract GovernanceTest is Test { governance.allocateLQTY(initiativesToReset, initiatives, deltaLQTYVotes, deltaLQTYVetos); // should update the user's allocated LQTY balance - (allocatedLQTY,) = governance.userStates(user2); + (,,allocatedLQTY,) = governance.userStates(user2); assertEq(allocatedLQTY, 1e18); - (voteLQTY, vetoLQTY, averageStakingTimestampVoteLQTY, averageStakingTimestampVetoLQTY,) = + (voteLQTY, voteOffset, vetoLQTY, vetoOffset,) = governance.initiativeStates(baseInitiative1); assertEq(voteLQTY, 2e18); assertEq(vetoLQTY, 0); - assertEq(averageStakingTimestampVoteLQTY, (block.timestamp - governance.EPOCH_DURATION()) * 1e26, "TS 2"); - assertGt(averageStakingTimestampVoteLQTY, averageStakingTimestampUser); - assertEq(averageStakingTimestampVetoLQTY, 0); + // TODO: offset vote + veto assertions // should revert if the user doesn't have enough unallocated LQTY available vm.expectRevert("Governance: must-allocate-zero"); @@ -1132,7 +1129,7 @@ abstract contract GovernanceTest is Test { // should only allow for unallocating votes or allocating vetos after the epoch voting cutoff // vm.expectRevert("Governance: epoch-voting-cutoff"); governance.allocateLQTY(initiatives, initiatives, deltaLQTYVotes, deltaLQTYVetos); - (allocatedLQTY,) = governance.userStates(msg.sender); + (,,allocatedLQTY,) = governance.userStates(msg.sender); // this no longer reverts but the user allocation doesn't increase either way assertEq(allocatedLQTY, 0, "user can allocate after voting cutoff"); @@ -1149,7 +1146,7 @@ abstract contract GovernanceTest is Test { lqty.approve(address(userProxy), 2e18); governance.depositLQTY(2e18); - (uint256 allocatedLQTY,) = governance.userStates(user); + (,,uint256 allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, 0); (uint256 countedVoteLQTY,) = governance.globalState(); assertEq(countedVoteLQTY, 0); @@ -1167,22 +1164,21 @@ abstract contract GovernanceTest is Test { governance.allocateLQTY(initiativesToReset, initiatives, deltaLQTYVotes, deltaLQTYVetos); - (allocatedLQTY,) = governance.userStates(user); + (,,allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, 2e18); (countedVoteLQTY,) = governance.globalState(); assertEq(countedVoteLQTY, 2e18); ( uint256 voteLQTY, + uint256 voteOffset, uint256 vetoLQTY, - uint256 averageStakingTimestampVoteLQTY, - uint256 averageStakingTimestampVetoLQTY, + uint256 vetoOffset, ) = governance.initiativeStates(baseInitiative1); assertEq(voteLQTY, 1e18); assertEq(vetoLQTY, 0); - (voteLQTY, vetoLQTY, averageStakingTimestampVoteLQTY, averageStakingTimestampVetoLQTY,) = - governance.initiativeStates(baseInitiative2); + (voteLQTY, voteOffset, vetoLQTY,vetoOffset,) = governance.initiativeStates(baseInitiative2); assertEq(voteLQTY, 1e18); assertEq(vetoLQTY, 0); } @@ -1263,9 +1259,9 @@ abstract contract GovernanceTest is Test { int256[] memory deltaVoteLQTY = new int256[](2); deltaVoteLQTY[0] = 500e18; deltaVoteLQTY[1] = 500e18; - int88[] memory deltaVetoLQTY = new int88[](2); + int88[] memory deltaVetoLQTY = new int256[](2); governance.allocateLQTY(initiativesToReset, initiatives, deltaVoteLQTY, deltaVetoLQTY); - (uint256 allocatedLQTY,) = governance.userStates(user); + (,,uint256 allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, 1000e18); vm.warp(block.timestamp + governance.EPOCH_DURATION() + 1); @@ -1355,7 +1351,7 @@ abstract contract GovernanceTest is Test { deltaVoteLQTY[1] = 500e18; int256[] memory deltaVetoLQTY = new int256[](2); governance.allocateLQTY(initiativesToReset, initiatives, deltaVoteLQTY, deltaVetoLQTY); - (uint256 allocatedLQTY,) = governance.userStates(user); + (,,uint256 allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, 1000e18); vm.warp(block.timestamp + governance.EPOCH_DURATION() + 1); @@ -1565,14 +1561,11 @@ abstract contract GovernanceTest is Test { uint256 lqtyAmount = 1e18; _stakeLQTY(user, lqtyAmount); - (uint256 allocatedLQTY0, uint256 averageStakingTimestamp0) = governance.userStates(user); - uint256 currentUserPower0 = - governance.lqtyToVotes(allocatedLQTY0, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp0); + (,,uint256 allocatedLQTY0, uint256 allocatedOffset0) = governance.userStates(user); + uint256 currentUserPower0 =governance.lqtyToVotes(allocatedLQTY0, block.timestamp, allocatedOffset0); - (uint256 voteLQTY0,, uint256 averageStakingTimestampVoteLQTY0,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower0 = governance.lqtyToVotes( - voteLQTY0, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY0 - ); + (uint256 voteLQTY0, uint256 voteOffset0,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower0 = governance.lqtyToVotes(voteLQTY0, block.timestamp, voteOffset0); // (uint256 votes, uint256 forEpoch,,) = governance.votesForInitiativeSnapshot(baseInitiative1); // console2.log("votes0: ", votes); @@ -1584,16 +1577,16 @@ abstract contract GovernanceTest is Test { _allocateLQTY(user, lqtyAmount); // check user voting power for the current epoch - (uint256 allocatedLQTY1, uint256 averageStakingTimestamp1) = governance.userStates(user); + (,,uint256 allocatedLQTY1, uint256 allocatedOffset1) = governance.userStates(user); uint256 currentUserPower1 = - governance.lqtyToVotes(allocatedLQTY1, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp1); - // user's allocated lqty should immediately increase their voting power + governance.lqtyToVotes(allocatedLQTY1, block.timestamp, allocatedOffset1); + // user's allocated lqty should have non-zero voting power assertGt(currentUserPower1, 0, "current user voting power is 0"); // check initiative voting power for the current epoch - (uint256 voteLQTY1,, uint256 averageStakingTimestampVoteLQTY1,,) = governance.initiativeStates(baseInitiative1); + (uint256 voteLQTY1, uint256 votOffset1,,,) = governance.initiativeStates(baseInitiative1); uint256 currentInitiativePower1 = governance.lqtyToVotes( - voteLQTY1, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY1 + voteLQTY1, block.timestamp, votOffset1 ); assertGt(currentInitiativePower1, 0, "current initiative voting power is 0"); assertEq(currentUserPower1, currentInitiativePower1, "initiative and user voting power should be equal"); @@ -1607,15 +1600,15 @@ abstract contract GovernanceTest is Test { governance.snapshotVotesForInitiative(baseInitiative1); // user voting power should increase over a given chunk of time - (uint256 allocatedLQTY2, uint256 averageStakingTimestamp2) = governance.userStates(user); + (,,uint256 allocatedLQTY2, uint256 allocatedOffset2) = governance.userStates(user); uint256 currentUserPower2 = - governance.lqtyToVotes(allocatedLQTY2, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp2); + governance.lqtyToVotes(allocatedLQTY2, block.timestamp, allocatedOffset2); assertGt(currentUserPower2, currentUserPower1); // initiative voting power should increase over a given chunk of time - (uint256 voteLQTY2,, uint256 averageStakingTimestampVoteLQTY2,,) = governance.initiativeStates(baseInitiative1); + (uint256 voteLQTY2, uint256 voteOffset2,,,) = governance.initiativeStates(baseInitiative1); uint256 currentInitiativePower2 = governance.lqtyToVotes( - voteLQTY2, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY2 + voteLQTY2, block.timestamp, voteOffset2 ); assertEq( currentUserPower2, currentInitiativePower2, "user power and initiative power should increase by same amount" @@ -1631,14 +1624,14 @@ abstract contract GovernanceTest is Test { governance.snapshotVotesForInitiative(baseInitiative1); // user voting power should increase - (uint256 allocatedLQTY3, uint256 averageStakingTimestamp3) = governance.userStates(user); + (,,uint256 allocatedLQTY3, uint256 allocatedOffset) = governance.userStates(user); uint256 currentUserPower3 = - governance.lqtyToVotes(allocatedLQTY3, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp3); + governance.lqtyToVotes(allocatedLQTY3, block.timestamp, allocatedOffset); // votes should match the voting power for the initiative and subsequently the user since they're the only one allocated - (uint256 voteLQTY3,, uint256 averageStakingTimestampVoteLQTY3,,) = governance.initiativeStates(baseInitiative1); + (uint256 voteLQTY3, uint256 voteOffset3,,,) = governance.initiativeStates(baseInitiative1); uint256 currentInitiativePower3 = governance.lqtyToVotes( - voteLQTY3, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY3 + voteLQTY3, block.timestamp, voteOffset3 ); // votes should be counted in this epoch @@ -1650,14 +1643,12 @@ abstract contract GovernanceTest is Test { vm.warp(block.timestamp + EPOCH_DURATION - 1); governance.snapshotVotesForInitiative(baseInitiative1); - (uint256 allocatedLQTY4, uint256 averageStakingTimestamp4) = governance.userStates(user); + (,,uint256 allocatedLQTY4, uint256 allocatedOffset4) = governance.userStates(user); uint256 currentUserPower4 = - governance.lqtyToVotes(allocatedLQTY4, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp4); + governance.lqtyToVotes(allocatedLQTY4, block.timestamp, allocatedOffset4); - (uint256 voteLQTY4,, uint256 averageStakingTimestampVoteLQTY4,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower4 = governance.lqtyToVotes( - voteLQTY4, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY4 - ); + (uint256 voteLQTY4, uint256 voteOffset4,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower4 = governance.lqtyToVotes(voteLQTY4, block.timestamp, voteOffset4); // checking if snapshotting at the end of an epoch increases the voting power (uint256 votes2,,,) = governance.votesForInitiativeSnapshot(baseInitiative1); @@ -1700,16 +1691,14 @@ abstract contract GovernanceTest is Test { assertEq(2, governance.epoch(), "not in epoch 2"); // check user voting power before allocation at epoch start - (uint256 allocatedLQTY0, uint256 averageStakingTimestamp0) = governance.userStates(user); + (uint256 allocatedLQTY0, uint256 allocatedOffset0,,) = governance.userStates(user); uint256 currentUserPower0 = - governance.lqtyToVotes(allocatedLQTY0, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp0); + governance.lqtyToVotes(allocatedLQTY0, block.timestamp, allocatedOffset0); assertEq(currentUserPower0, 0, "user has voting power > 0"); // check initiative voting power before allocation at epoch start - (uint256 voteLQTY0,, uint256 averageStakingTimestampVoteLQTY0,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower0 = governance.lqtyToVotes( - voteLQTY0, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY0 - ); + (uint256 voteLQTY0, uint256 voteOffset0,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower0 = governance.lqtyToVotes(voteLQTY0, block.timestamp, voteOffset0); assertEq(currentInitiativePower0, 0, "current initiative voting power is > 0"); _allocateLQTY(user, lqtyAmount); @@ -1718,16 +1707,13 @@ abstract contract GovernanceTest is Test { assertEq(2, governance.epoch(), "not in epoch 2"); // check user voting power after allocation at epoch end - (uint256 allocatedLQTY1, uint256 averageStakingTimestamp1) = governance.userStates(user); - uint256 currentUserPower1 = - governance.lqtyToVotes(allocatedLQTY1, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp1); + (uint256 allocatedLQTY1, uint256 allocatedOffset1,,) = governance.userStates(user); + uint256 currentUserPower1 = governance.lqtyToVotes(allocatedLQTY1, block.timestamp, allocatedOffset1); assertGt(currentUserPower1, 0, "user has no voting power after allocation"); // check initiative voting power after allocation at epoch end - (uint256 voteLQTY1,, uint256 averageStakingTimestampVoteLQTY1,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower1 = governance.lqtyToVotes( - voteLQTY1, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY1 - ); + (uint256 voteLQTY1, uint256 voteOffset1,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower1 = governance.lqtyToVotes(voteLQTY1, block.timestamp, voteOffset1); assertGt(currentInitiativePower1, 0, "initiative has no voting power after allocation"); // check that user and initiative voting power is equivalent at epoch end @@ -1737,16 +1723,13 @@ abstract contract GovernanceTest is Test { assertEq(42, governance.epoch(), "not in epoch 42"); // get user voting power after multiple epochs - (uint256 allocatedLQTY2, uint256 averageStakingTimestamp2) = governance.userStates(user); - uint256 currentUserPower2 = - governance.lqtyToVotes(allocatedLQTY2, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp2); + (uint256 allocatedLQTY2, uint256 allocatedOffset2,,) = governance.userStates(user); + uint256 currentUserPower2 = governance.lqtyToVotes(allocatedLQTY2, block.timestamp, allocatedOffset2); assertGt(currentUserPower2, currentUserPower1, "user voting power doesn't increase"); // get initiative voting power after multiple epochs - (uint256 voteLQTY2,, uint256 averageStakingTimestampVoteLQTY2,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower2 = governance.lqtyToVotes( - voteLQTY2, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY2 - ); + (uint256 voteLQTY2, uint256 voteOffset2,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower2 = governance.lqtyToVotes(voteLQTY2, block.timestamp, voteOffset2); assertGt(currentInitiativePower2, currentInitiativePower1, "initiative voting power doesn't increase"); // check that initiative and user voting always track each other @@ -1788,10 +1771,8 @@ abstract contract GovernanceTest is Test { vm.warp(block.timestamp + EPOCH_DURATION); // warp to second epoch // get initiative voting power at start of epoch - (uint256 voteLQTY0,, uint256 averageStakingTimestampVoteLQTY0,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower0 = governance.lqtyToVotes( - voteLQTY0, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY0 - ); + (uint256 voteLQTY0, uint256 voteOffset0,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower0 = governance.lqtyToVotes(voteLQTY0, block.timestamp, voteOffset0); assertEq(currentInitiativePower0, 0, "initiative voting power is > 0"); _allocateLQTY(user, lqtyAmount); @@ -1802,10 +1783,8 @@ abstract contract GovernanceTest is Test { governance.snapshotVotesForInitiative(baseInitiative1); // get initiative voting power at time of snapshot - (uint256 voteLQTY1,, uint256 averageStakingTimestampVoteLQTY1,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower1 = governance.lqtyToVotes( - voteLQTY1, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY1 - ); + (uint256 voteLQTY1, uint256 voteOffset1,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower1 = governance.lqtyToVotes(voteLQTY1, block.timestamp, voteOffset1); assertGt(currentInitiativePower1, 0, "initiative voting power is 0"); uint256 deltaInitiativeVotingPower = currentInitiativePower1 - currentInitiativePower0; @@ -1850,12 +1829,12 @@ abstract contract GovernanceTest is Test { _allocateLQTY(user, lqtyAmount); // get user voting power at start of epoch from lqtyAllocatedByUserToInitiative - (uint256 voteLQTY0,,) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); - (uint256 allocatedLQTY, uint256 averageStakingTimestamp) = governance.userStates(user); + (uint256 voteLQTY, uint256 voteOffset,,,) = governance.lqtyAllocatedByUserToInitiative(user, baseInitiative1); + (,,uint256 allocatedLQTY, uint256 allocatedOffset) = governance.userStates(user); uint256 currentInitiativePowerFrom1 = - governance.lqtyToVotes(voteLQTY0, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp); + governance.lqtyToVotes(voteLQTY, block.timestamp, voteOffset); uint256 currentInitiativePowerFrom2 = - governance.lqtyToVotes(allocatedLQTY, uint256(block.timestamp) * uint256(1e26), averageStakingTimestamp); + governance.lqtyToVotes(allocatedLQTY, block.timestamp, allocatedOffset); assertEq( currentInitiativePowerFrom1, @@ -1864,8 +1843,8 @@ abstract contract GovernanceTest is Test { ); } - // checking if allocating to a different initiative in a different epoch modifies the avgStakingTimestamp - function test_average_timestamp() public { + // checking if allocating to a different initiative in a different epoch modifies the allocated offset + function test_allocated_offset() public { // =========== epoch 1 ================== governance = new GovernanceTester( address(lqty), @@ -1899,8 +1878,8 @@ abstract contract GovernanceTest is Test { // user allocates to baseInitiative1 _allocateLQTY(user, 1e18); - // get user voting power at start of epoch 2 from lqtyAllocatedByUserToInitiative - (, uint256 averageStakingTimestamp1) = governance.userStates(user); + // get user voting power at start of epoch 2 + (,,,uint256 allocatedOffset1) = governance.userStates(user); // =========== epoch 3 (start) ================== // 3. user allocates to baseInitiative2 in epoch 3 @@ -1908,16 +1887,17 @@ abstract contract GovernanceTest is Test { address[] memory initiativesToReset = new address[](1); initiativesToReset[0] = address(baseInitiative1); + // this should reset all alloc to initiative1, and divert it to initative 2 _allocateLQTYToInitiative(user, baseInitiative2, 1e18, initiativesToReset); - // get user voting power at start of epoch 3 from lqtyAllocatedByUserToInitiative - (, uint256 averageStakingTimestamp2) = governance.userStates(user); - assertEq(averageStakingTimestamp1, averageStakingTimestamp2); + // check offsets are equal + (,,,uint256 allocatedOffset2) = governance.userStates(user); + assertEq(allocatedOffset1, allocatedOffset2); } // checking if allocating to same initiative modifies the average timestamp // forge test --match-test test_average_timestamp_same_initiative -vv - function test_average_timestamp_same_initiative() public { + function test_offset_same_initiative() public { // =========== epoch 1 ================== governance = new GovernanceTester( address(lqty), @@ -1951,9 +1931,9 @@ abstract contract GovernanceTest is Test { // user allocates to baseInitiative1 _allocateLQTY(user, 1e18); - // get user voting power at start of epoch 2 from lqtyAllocatedByUserToInitiative - (, uint256 averageStakingTimestamp1) = governance.userStates(user); - console2.log("averageStakingTimestamp1: ", averageStakingTimestamp1); + // get user voting power at start of epoch 2 + (,,,uint256 allocatedOffset1) = governance.userStates(user); + console2.log("allocatedOffset1: ", allocatedOffset1); // =========== epoch 3 (start) ================== // 3. user allocates to baseInitiative1 in epoch 3 @@ -1961,13 +1941,13 @@ abstract contract GovernanceTest is Test { _allocateLQTY(user, 1e18); - // get user voting power at start of epoch 3 from lqtyAllocatedByUserToInitiative - (, uint256 averageStakingTimestamp2) = governance.userStates(user); - assertEq(averageStakingTimestamp1, averageStakingTimestamp2, "average timestamps differ"); + // get user voting power at start of epoch 3 + (,,,uint256 allocatedOffset2) = governance.userStates(user); + assertEq(allocatedOffset2, allocatedOffset1, "offsets differ"); } // checking if allocating to same initiative modifies the average timestamp - function test_average_timestamp_allocate_same_initiative_fuzz(uint256 allocateAmount) public { + function test_offset_allocate_same_initiative_fuzz(uint256 allocateAmount) public { // =========== epoch 1 ================== governance = new GovernanceTester( address(lqty), @@ -2003,8 +1983,8 @@ abstract contract GovernanceTest is Test { uint256 lqtyAmount2 = uint256(bound(allocateAmount, 1, lqtyAmount)); _allocateLQTY(user, lqtyAmount2); - // get user voting power at start of epoch 2 from lqtyAllocatedByUserToInitiative - (, uint256 averageStakingTimestamp1) = governance.userStates(user); + // get user voting power at start of epoch 2 + (,,,uint256 allocatedOffset1) = governance.userStates(user); // =========== epoch 3 (start) ================== // 3. user allocates to baseInitiative1 in epoch 3 @@ -2017,9 +1997,9 @@ abstract contract GovernanceTest is Test { _allocateLQTY(user, lqtyAmount3); // get user voting power at start of epoch 3 from lqtyAllocatedByUserToInitiative - (, uint256 averageStakingTimestamp2) = governance.userStates(user); + (,,,uint256 allocatedOffset2) = governance.userStates(user); assertEq( - averageStakingTimestamp1, averageStakingTimestamp2, "averageStakingTimestamp1 != averageStakingTimestamp2" + allocatedOffset2, allocatedOffset1, "allocatedOffset1 != allocatedOffset2" ); } @@ -2055,10 +2035,8 @@ abstract contract GovernanceTest is Test { vm.warp(block.timestamp + EPOCH_DURATION); // warp to second epoch // get initiative voting power at start of epoch - (uint256 voteLQTY0,, uint256 averageStakingTimestampVoteLQTY0,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower0 = governance.lqtyToVotes( - voteLQTY0, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY0 - ); + (uint256 voteLQTY0, uint256 voteOffset0,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower0 = governance.lqtyToVotes(voteLQTY0, block.timestamp, voteOffset0); assertEq(currentInitiativePower0, 0, "initiative voting power is > 0"); _allocateLQTY(user, lqtyAmount); @@ -2072,10 +2050,8 @@ abstract contract GovernanceTest is Test { governance.snapshotVotesForInitiative(baseInitiative1); // get initiative voting power at start of epoch - (uint256 voteLQTY1,, uint256 averageStakingTimestampVoteLQTY1,,) = governance.initiativeStates(baseInitiative1); - uint256 currentInitiativePower1 = governance.lqtyToVotes( - voteLQTY1, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY1 - ); + (uint256 voteLQTY1, uint256 voteOffset1,,,) = governance.initiativeStates(baseInitiative1); + uint256 currentInitiativePower1 = governance.lqtyToVotes(voteLQTY1, block.timestamp, voteOffset1); // 4a. votes from snapshotting at begging of epoch (uint256 votes,,,) = governance.votesForInitiativeSnapshot(baseInitiative1); @@ -2231,8 +2207,8 @@ abstract contract GovernanceTest is Test { assertEq(votes3, 0, "voting power should be decreased in this epoch"); } - // checking if deallocating changes the averageStakingTimestamp - function test_deallocating_decreases_avg_timestamp() public { + + function test_deallocating_decreases_offset() public { // =========== epoch 1 ================== governance = new GovernanceTester( address(lqty), @@ -2270,12 +2246,13 @@ abstract contract GovernanceTest is Test { vm.warp(block.timestamp + EPOCH_DURATION); governance.snapshotVotesForInitiative(baseInitiative1); - (, uint256 averageStakingTimestampBefore) = governance.userStates(user); + (,,,uint256 allocatedOffset) = governance.userStates(user); + assertGt(allocatedOffset, 0); _deAllocateLQTY(user, 0); - (, uint256 averageStakingTimestampAfter) = governance.userStates(user); - assertEq(averageStakingTimestampBefore, averageStakingTimestampAfter); + (,,,allocatedOffset) = governance.userStates(user); + assertEq(allocatedOffset, 0); } // vetoing shouldn't affect voting power of the initiative @@ -2325,9 +2302,9 @@ abstract contract GovernanceTest is Test { governance.snapshotVotesForInitiative(baseInitiative1); // voting power for initiative should be the same as votes from snapshot - (uint256 voteLQTY,, uint256 averageStakingTimestampVoteLQTY,,) = governance.initiativeStates(baseInitiative1); + (uint256 voteLQTY, uint256 voteOffset,,,) = governance.initiativeStates(baseInitiative1); uint256 currentInitiativePower = - governance.lqtyToVotes(voteLQTY, uint256(block.timestamp) * uint256(1e26), averageStakingTimestampVoteLQTY); + governance.lqtyToVotes(voteLQTY, block.timestamp, voteOffset); // 4. votes should not affect accounting for votes (uint256 votes,,,) = governance.votesForInitiativeSnapshot(baseInitiative1); diff --git a/test/VotingPower.t.sol b/test/VotingPower.t.sol index 79c7241..4d83474 100644 --- a/test/VotingPower.t.sol +++ b/test/VotingPower.t.sol @@ -96,41 +96,6 @@ abstract contract VotingPowerTest is Test { assertEq(powerInTheFuture, powerFromMoreDeposits, "Same result"); } - function _averageAge(uint32 _currentTimestamp, uint32 _averageTimestamp) internal pure returns (uint32) { - if (_averageTimestamp == 0 || _currentTimestamp < _averageTimestamp) return 0; - return _currentTimestamp - _averageTimestamp; - } - - function _calculateAverageTimestamp( - uint32 _prevOuterAverageTimestamp, - uint32 _newInnerAverageTimestamp, - uint256 _prevLQTYBalance, - uint256 _newLQTYBalance - ) internal view returns (uint32) { - if (_newLQTYBalance == 0) return 0; - - uint32 prevOuterAverageAge = _averageAge(uint32(block.timestamp), _prevOuterAverageTimestamp); - uint32 newInnerAverageAge = _averageAge(uint32(block.timestamp), _newInnerAverageTimestamp); - - uint256 newOuterAverageAge; - if (_prevLQTYBalance <= _newLQTYBalance) { - uint256 deltaLQTY = _newLQTYBalance - _prevLQTYBalance; - uint256 prevVotes = uint256(_prevLQTYBalance) * uint256(prevOuterAverageAge); - uint256 newVotes = uint256(deltaLQTY) * uint256(newInnerAverageAge); - uint256 votes = prevVotes + newVotes; - newOuterAverageAge = uint32(votes / uint256(_newLQTYBalance)); - } else { - uint256 deltaLQTY = _prevLQTYBalance - _newLQTYBalance; - uint256 prevVotes = uint256(_prevLQTYBalance) * uint256(prevOuterAverageAge); - uint256 newVotes = uint256(deltaLQTY) * uint256(newInnerAverageAge); - uint256 votes = (prevVotes >= newVotes) ? prevVotes - newVotes : 0; - newOuterAverageAge = uint32(votes / uint256(_newLQTYBalance)); - } - - if (newOuterAverageAge > block.timestamp) return 0; - return uint32(block.timestamp - newOuterAverageAge); - } - // This test prepares for comparing votes and vetos for state // forge test --match-test test_we_can_compare_votes_and_vetos -vv // function test_we_can_compare_votes_and_vetos() public { @@ -219,123 +184,124 @@ abstract contract VotingPowerTest is Test { // } // forge test --match-test test_crit_user_can_dilute_total_votes -vv - function test_crit_user_can_dilute_total_votes() public { - // User A deposits normaly - vm.startPrank(user); + // TODO: convert to an offset-based test + // function test_crit_user_can_dilute_total_votes() public { + // // User A deposits normaly + // vm.startPrank(user); - _stakeLQTY(user, 124); + // _stakeLQTY(user, 124); - vm.warp(block.timestamp + 124 - 15); + // vm.warp(block.timestamp + 124 - 15); - vm.startPrank(user2); - _stakeLQTY(user2, 15); + // vm.startPrank(user2); + // _stakeLQTY(user2, 15); - vm.warp(block.timestamp + 15); + // vm.warp(block.timestamp + 15); - vm.startPrank(user); - _allocate(address(baseInitiative1), 124, 0); - uint256 user1_avg = _getAverageTS(baseInitiative1); + // vm.startPrank(user); + // _allocate(address(baseInitiative1), 124, 0); + // uint256 user1_avg = _getAverageTS(baseInitiative1); - vm.startPrank(user2); - _allocate(address(baseInitiative1), 15, 0); - _reset(address(baseInitiative1)); + // vm.startPrank(user2); + // _allocate(address(baseInitiative1), 15, 0); + // _reset(address(baseInitiative1)); - uint256 griefed_avg = _getAverageTS(baseInitiative1); + // uint256 griefed_avg = _getAverageTS(baseInitiative1); - uint256 vote_power_1 = governance.lqtyToVotes(124, uint32(block.timestamp), uint32(user1_avg)); - uint256 vote_power_2 = governance.lqtyToVotes(124, uint32(block.timestamp), uint32(griefed_avg)); + // uint256 vote_power_1 = governance.lqtyToVotes(124, uint32(block.timestamp), uint32(user1_avg)); + // uint256 vote_power_2 = governance.lqtyToVotes(124, uint32(block.timestamp), uint32(griefed_avg)); - console.log("vote_power_1", vote_power_1); - console.log("vote_power_2", vote_power_2); + // console.log("vote_power_1", vote_power_1); + // console.log("vote_power_2", vote_power_2); - // assertEq(user1_avg, griefed_avg, "same avg"); // BREAKS, OFF BY ONE + // // assertEq(user1_avg, griefed_avg, "same avg"); // BREAKS, OFF BY ONE - // Causes a loss of power of 1 second per time this is done + // // Causes a loss of power of 1 second per time this is done - vm.startPrank(user); - _reset(address(baseInitiative1)); + // vm.startPrank(user); + // _reset(address(baseInitiative1)); - uint256 final_avg = _getAverageTS(baseInitiative1); - console.log("final_avg", final_avg); + // uint256 final_avg = _getAverageTS(baseInitiative1); + // console.log("final_avg", final_avg); - // This is not an issue, except for bribes, bribes can get the last claimer DOSS - } + // // This is not an issue, except for bribes, bribes can get the last claimer DOSS + // } // forge test --match-test test_can_we_spam_to_revert -vv - function test_can_we_spam_to_revert() public { - // User A deposits normaly - vm.startPrank(user); + // function test_can_we_spam_to_revert() public { + // // User A deposits normaly + // vm.startPrank(user); - _stakeLQTY(user, 124); + // _stakeLQTY(user, 124); - vm.warp(block.timestamp + 124); + // vm.warp(block.timestamp + 124); - vm.startPrank(user2); - _stakeLQTY(user2, 15); + // vm.startPrank(user2); + // _stakeLQTY(user2, 15); - vm.startPrank(user); - _allocate(address(baseInitiative1), 124, 0); + // vm.startPrank(user); + // _allocate(address(baseInitiative1), 124, 0); - vm.startPrank(user2); - _allocate(address(baseInitiative1), 15, 0); - _reset(address(baseInitiative1)); + // vm.startPrank(user2); + // _allocate(address(baseInitiative1), 15, 0); + // _reset(address(baseInitiative1)); - uint256 griefed_avg = _getAverageTS(baseInitiative1); - console.log("griefed_avg", griefed_avg); - console.log("block.timestamp", block.timestamp); + // uint256 griefed_avg = _getAverageTS(baseInitiative1); + // console.log("griefed_avg", griefed_avg); + // console.log("block.timestamp", block.timestamp); - console.log("0?"); + // console.log("0?"); - uint256 currentMagnifiedTs = uint256(block.timestamp) * uint256(1e26); + // uint256 currentMagnifiedTs = uint256(block.timestamp) * uint256(1e26); - vm.startPrank(user2); - _allocate(address(baseInitiative1), 15, 0); - _reset(address(baseInitiative1)); + // vm.startPrank(user2); + // _allocate(address(baseInitiative1), 15, 0); + // _reset(address(baseInitiative1)); - uint256 ts = _getAverageTS(baseInitiative1); - uint256 delta = currentMagnifiedTs - ts; - console.log("griefed_avg", ts); - console.log("delta", delta); - console.log("currentMagnifiedTs", currentMagnifiedTs); + // uint256 ts = _getAverageTS(baseInitiative1); + // uint256 delta = currentMagnifiedTs - ts; + // console.log("griefed_avg", ts); + // console.log("delta", delta); + // console.log("currentMagnifiedTs", currentMagnifiedTs); - console.log("0?"); - uint256 i; - while (i++ < 122) { - console.log("i", i); - _allocate(address(baseInitiative1), 15, 0); - _reset(address(baseInitiative1)); - } + // console.log("0?"); + // uint256 i; + // while (i++ < 122) { + // console.log("i", i); + // _allocate(address(baseInitiative1), 15, 0); + // _reset(address(baseInitiative1)); + // } - console.log("1?"); + // console.log("1?"); - ts = _getAverageTS(baseInitiative1); - delta = currentMagnifiedTs - ts; - console.log("griefed_avg", ts); - console.log("delta", delta); - console.log("currentMagnifiedTs", currentMagnifiedTs); + // ts = _getAverageTS(baseInitiative1); + // delta = currentMagnifiedTs - ts; + // console.log("griefed_avg", ts); + // console.log("delta", delta); + // console.log("currentMagnifiedTs", currentMagnifiedTs); - // One more time - _allocate(address(baseInitiative1), 15, 0); - _reset(address(baseInitiative1)); - _allocate(address(baseInitiative1), 15, 0); - _reset(address(baseInitiative1)); - _allocate(address(baseInitiative1), 15, 0); - _reset(address(baseInitiative1)); - _allocate(address(baseInitiative1), 15, 0); + // // One more time + // _allocate(address(baseInitiative1), 15, 0); + // _reset(address(baseInitiative1)); + // _allocate(address(baseInitiative1), 15, 0); + // _reset(address(baseInitiative1)); + // _allocate(address(baseInitiative1), 15, 0); + // _reset(address(baseInitiative1)); + // _allocate(address(baseInitiative1), 15, 0); - /// NOTE: Keep 1 wei to keep rounding error - _allocate(address(baseInitiative1), 1, 0); + // /// NOTE: Keep 1 wei to keep rounding error + // _allocate(address(baseInitiative1), 1, 0); - ts = _getAverageTS(baseInitiative1); - console.log("griefed_avg", ts); + // ts = _getAverageTS(baseInitiative1); + // console.log("griefed_avg", ts); - vm.startPrank(user); - _reset(address(baseInitiative1)); - _allocate(address(baseInitiative1), 124, 0); + // vm.startPrank(user); + // _reset(address(baseInitiative1)); + // _allocate(address(baseInitiative1), 124, 0); - ts = _getAverageTS(baseInitiative1); - console.log("end_ts", ts); - } + // ts = _getAverageTS(baseInitiative1); + // console.log("end_ts", ts); + // } // forge test --match-test test_basic_reset_flow -vv function test_basic_reset_flow() public { @@ -347,7 +313,7 @@ abstract contract VotingPowerTest is Test { // user allocates to baseInitiative1 _allocate(address(baseInitiative1), lqtyAmount / 2, 0); // 50% to it - (uint256 allocatedLQTY,) = governance.userStates(user); + (,,uint256 allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, uint256(lqtyAmount / 2), "half"); _allocate(address(baseInitiative1), lqtyAmount / 2, 0); // 50% to it @@ -364,7 +330,7 @@ abstract contract VotingPowerTest is Test { // user allocates to baseInitiative1 _allocate(address(baseInitiative1), lqtyAmount / 2, 0); // 50% to it - (uint256 allocatedLQTY,) = governance.userStates(user); + (,,uint256 allocatedLQTY,) = governance.userStates(user); assertEq(allocatedLQTY, uint256(lqtyAmount / 2), "Half"); // Go to Cutoff @@ -400,10 +366,10 @@ abstract contract VotingPowerTest is Test { // Removing just updates that + the weights // The weights are the avg time * the number - function _getAverageTS(address initiative) internal view returns (uint256) { - (,, uint256 averageStakingTimestampVoteLQTY,,) = governance.initiativeStates(initiative); + function _getInitiativeOffset(address initiative) internal view returns (uint256) { + (,uint256 voteOffset,,,) = governance.initiativeStates(initiative); - return averageStakingTimestampVoteLQTY; + return voteOffset; } function _stakeLQTY(address _user, uint256 amount) internal { diff --git a/test/recon/properties/GovernanceProperties.sol b/test/recon/properties/GovernanceProperties.sol index 2dc38c6..0b379c3 100644 --- a/test/recon/properties/GovernanceProperties.sol +++ b/test/recon/properties/GovernanceProperties.sol @@ -115,7 +115,6 @@ abstract contract GovernanceProperties is BeforeAfter { function _getGlobalLQTYAndUserSum() internal returns (uint256, uint256) { ( uint256 totalCountedLQTY, - // uint256 after_user_countedVoteLQTYAverageTimestamp // TODO: How do we do this? ) = governance.globalState(); uint256 totalUserCountedLQTY; @@ -247,7 +246,7 @@ abstract contract GovernanceProperties is BeforeAfter { (uint256 initiativeVoteLQTY, uint256 initiativeVoteOffset,,,) = governance.initiativeStates(deployedInitiatives[i]); uint256 initiativeWeight = governance.lqtyToVotes( - initiativeVoteLQTY, uint256(block.timestamp) , initiativeVoteOffset + initiativeVoteLQTY, block.timestamp, initiativeVoteOffset ); acc[i].userSum = userWeightAccumulatorForInitiative; @@ -297,7 +296,7 @@ abstract contract GovernanceProperties is BeforeAfter { } function _getInitiativeStateAndGlobalState() internal returns (uint256, uint256, uint256, uint256) { - (uint256 totalCountedLQTY, uint256 global_countedVoteLQTYAverageTimestamp) = governance.globalState(); + (uint256 totalCountedLQTY, uint256 global_countedVoteOffset) = governance.globalState(); // Can sum via projection I guess @@ -308,9 +307,9 @@ abstract contract GovernanceProperties is BeforeAfter { for (uint256 i; i < deployedInitiatives.length; i++) { ( uint256 voteLQTY, + uint256 voteOffset, uint256 vetoLQTY, - uint256 averageStakingTimestampVoteLQTY, - uint256 averageStakingTimestampVetoLQTY, + uint256 vetoOffset, ) = governance.initiativeStates(deployedInitiatives[i]); // Conditional, only if not DISABLED @@ -319,18 +318,14 @@ abstract contract GovernanceProperties is BeforeAfter { if (status != IGovernance.InitiativeStatus.DISABLED) { allocatedLQTYSum += voteLQTY; // Sum via projection - votedPowerSum += governance.lqtyToVotes( - voteLQTY, - uint256(block.timestamp) * uint256(governance.TIMESTAMP_PRECISION()), - averageStakingTimestampVoteLQTY - ); + votedPowerSum += governance.lqtyToVotes(voteLQTY, block.timestamp, voteOffset); } } uint256 govPower = governance.lqtyToVotes( totalCountedLQTY, - uint256(block.timestamp) * uint256(governance.TIMESTAMP_PRECISION()), - global_countedVoteLQTYAverageTimestamp + block.timestamp, + global_countedVoteOffset ); return (allocatedLQTYSum, totalCountedLQTY, votedPowerSum, govPower); diff --git a/test/recon/properties/RevertProperties.sol b/test/recon/properties/RevertProperties.sol index ce9b579..6ad5811 100644 --- a/test/recon/properties/RevertProperties.sol +++ b/test/recon/properties/RevertProperties.sol @@ -9,12 +9,12 @@ import {IBribeInitiative} from "src/interfaces/IBribeInitiative.sol"; // The are view functions that should never revert abstract contract RevertProperties is BeforeAfter { function property_computingGlobalPowerNeverReverts() public { - (uint256 totalCountedLQTY, uint256 global_countedVoteLQTYAverageTimestamp) = governance.globalState(); + (uint256 totalCountedLQTY, uint256 global_countedVoteOffset) = governance.globalState(); try governance.lqtyToVotes( totalCountedLQTY, - uint256(block.timestamp) * uint256(governance.TIMESTAMP_PRECISION()), - global_countedVoteLQTYAverageTimestamp + block.timestamp, + global_countedVoteOffset ) {} catch { t(false, "Should never revert"); } @@ -25,9 +25,9 @@ abstract contract RevertProperties is BeforeAfter { for (uint256 i; i < deployedInitiatives.length; i++) { ( uint256 voteLQTY, + uint256 voteOffset, uint256 vetoLQTY, - uint256 averageStakingTimestampVoteLQTY, - uint256 averageStakingTimestampVetoLQTY, + uint256 vetoOffset, ) = governance.initiativeStates(deployedInitiatives[i]); // Sum via projection @@ -35,8 +35,8 @@ abstract contract RevertProperties is BeforeAfter { unchecked { try governance.lqtyToVotes( voteLQTY, - uint256(block.timestamp) * uint256(governance.TIMESTAMP_PRECISION()), - averageStakingTimestampVoteLQTY + block.timestamp, + voteOffset ) returns (uint256 res) { votedPowerSum += res; } catch {