Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UNSAFE | TO REDO packing and tests #31

Closed
wants to merge 11 commits into from
4 changes: 2 additions & 2 deletions src/BribeInitiative.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ contract BribeInitiative is IInitiative, IBribeInitiative {
);

(uint88 totalLQTY, uint32 totalAverageTimestamp) = _decodeLQTYAllocation(totalLQTYAllocation.value);
uint240 totalVotes = governance.lqtyToVotes(totalLQTY, block.timestamp, totalAverageTimestamp);
uint240 totalVotes = governance.lqtyToVotes(totalLQTY, uint32(block.timestamp), totalAverageTimestamp);
if (totalVotes != 0) {
(uint88 lqty, uint32 averageTimestamp) = _decodeLQTYAllocation(lqtyAllocation.value);
uint240 votes = governance.lqtyToVotes(lqty, block.timestamp, averageTimestamp);
uint240 votes = governance.lqtyToVotes(lqty, uint32(block.timestamp), averageTimestamp);
boldAmount = uint256(bribe.boldAmount) * uint256(votes) / uint256(totalVotes);
bribeTokenAmount = uint256(bribe.bribeTokenAmount) * uint256(votes) / uint256(totalVotes);
}
Expand Down
28 changes: 9 additions & 19 deletions src/Governance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, IGovernance
bold = IERC20(_bold);
require(_config.minClaim <= _config.minAccrual, "Gov: min-claim-gt-min-accrual");
REGISTRATION_FEE = _config.registrationFee;

// Registration threshold must be below 100% of votes
require(_config.registrationThresholdFactor < WAD, "Gov: registration-config");
REGISTRATION_THRESHOLD_FACTOR = _config.registrationThresholdFactor;
Expand Down Expand Up @@ -239,12 +239,12 @@ contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, IGovernance
}

/// @inheritdoc IGovernance
function lqtyToVotes(uint88 _lqtyAmount, uint256 _currentTimestamp, uint32 _averageTimestamp)
function lqtyToVotes(uint88 _lqtyAmount, uint32 _currentTimestamp, uint32 _averageTimestamp)
public
pure
returns (uint240)
returns (uint120)
{
return uint240(_lqtyAmount) * _averageAge(uint32(_currentTimestamp), _averageTimestamp);
return uint120(_lqtyAmount) * uint120(_averageAge(_currentTimestamp, _averageTimestamp));
}

/// @inheritdoc IGovernance
Expand Down Expand Up @@ -287,29 +287,19 @@ contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, IGovernance
if (initiativeSnapshot.forEpoch < currentEpoch - 1) {
uint256 votingThreshold = calculateVotingThreshold();
uint32 start = epochStart();
uint240 votes =
uint120 votes =
lqtyToVotes(initiativeState.voteLQTY, start, initiativeState.averageStakingTimestampVoteLQTY);
uint240 vetos =
uint120 vetos =
lqtyToVotes(initiativeState.vetoLQTY, start, initiativeState.averageStakingTimestampVetoLQTY);
// if the votes didn't meet the voting threshold then no votes qualify
/// @audit TODO TEST THIS
/// The change means that all logic for votes and rewards must be done in `getInitiativeState`
initiativeSnapshot.votes = uint224(votes); /// @audit TODO: We should change this to check the treshold, we should instead use the snapshot to just report all the valid data
initiativeSnapshot.votes = uint120(votes); /// @audit TODO: We should change this to check the treshold, we should instead use the snapshot to just report all the valid data

initiativeSnapshot.vetos = uint224(vetos); /// @audit TODO: Overflow + order of operations
initiativeSnapshot.vetos = uint120(vetos); /// @audit TODO: Overflow + order of operations

initiativeSnapshot.forEpoch = currentEpoch - 1;

/// @audit Conditional
/// If we meet the threshold then we increase this
/// TODO: Either simplify, or use this for the state machine as well
if(
initiativeSnapshot.votes > initiativeSnapshot.vetos &&
initiativeSnapshot.votes >= votingThreshold
) {
// initiativeSnapshot.lastCountedEpoch = currentEpoch - 1; /// @audit This updating makes it so that we lose track | TODO: Find a better way
}

votesForInitiativeSnapshot[_initiative] = initiativeSnapshot;
emit SnapshotVotesForInitiative(_initiative, initiativeSnapshot.votes, initiativeSnapshot.forEpoch);
}
Expand Down Expand Up @@ -409,7 +399,7 @@ contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, IGovernance
// an initiative can be registered if the registrant has more voting power (LQTY * age)
// than the registration threshold derived from the previous epoch's total global votes
require(
lqtyToVotes(uint88(stakingV1.stakes(userProxyAddress)), block.timestamp, userState.averageStakingTimestamp)
lqtyToVotes(uint88(stakingV1.stakes(userProxyAddress)), uint32(block.timestamp), userState.averageStakingTimestamp)
>= snapshot.votes * REGISTRATION_THRESHOLD_FACTOR / WAD,
"Governance: insufficient-lqty"
);
Expand Down
162 changes: 120 additions & 42 deletions src/interfaces/IGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,39 @@ import {PermitParams} from "../utils/Types.sol";

interface IGovernance {
event DepositLQTY(address user, uint256 depositedLQTY);
event WithdrawLQTY(address user, uint256 withdrawnLQTY, uint256 accruedLUSD, uint256 accruedETH);
event WithdrawLQTY(
address user,
uint256 withdrawnLQTY,
uint256 accruedLUSD,
uint256 accruedETH
);

event SnapshotVotes(uint240 votes, uint16 forEpoch);
event SnapshotVotesForInitiative(address initiative, uint240 votes, uint16 forEpoch);
event SnapshotVotesForInitiative(
address initiative,
uint240 votes,
uint16 forEpoch
);

event RegisterInitiative(address initiative, address registrant, uint16 atEpoch);
event RegisterInitiative(
address initiative,
address registrant,
uint16 atEpoch
);
event UnregisterInitiative(address initiative, uint16 atEpoch);

event AllocateLQTY(address user, address initiative, int256 deltaVoteLQTY, int256 deltaVetoLQTY, uint16 atEpoch);
event ClaimForInitiative(address initiative, uint256 bold, uint256 forEpoch);
event AllocateLQTY(
address user,
address initiative,
int256 deltaVoteLQTY,
int256 deltaVetoLQTY,
uint16 atEpoch
);
event ClaimForInitiative(
address initiative,
uint256 bold,
uint256 forEpoch
);

struct Configuration {
uint128 registrationFee;
Expand Down Expand Up @@ -51,7 +74,10 @@ interface IGovernance {
function EPOCH_DURATION() external view returns (uint256 epochDuration);
/// @notice Voting period of an epoch in seconds (e.g. 6 days)
/// @return epochVotingCutoff Epoch voting cutoff
function EPOCH_VOTING_CUTOFF() external view returns (uint256 epochVotingCutoff);
function EPOCH_VOTING_CUTOFF()
external
view
returns (uint256 epochVotingCutoff);
/// @notice Minimum BOLD amount that has to be claimed, if an initiative doesn't have enough votes to meet the
/// criteria then it's votes a excluded from the vote count and distribution
/// @return minClaim Minimum claim amount
Expand All @@ -65,49 +91,64 @@ interface IGovernance {
function REGISTRATION_FEE() external view returns (uint256 registrationFee);
/// @notice Share of all votes that are necessary to register a new initiative
/// @return registrationThresholdFactor Threshold factor
function REGISTRATION_THRESHOLD_FACTOR() external view returns (uint256 registrationThresholdFactor);
function REGISTRATION_THRESHOLD_FACTOR()
external
view
returns (uint256 registrationThresholdFactor);
/// @notice Multiple of the voting threshold in vetos that are necessary to unregister an initiative
/// @return unregistrationThresholdFactor Unregistration threshold factor
function UNREGISTRATION_THRESHOLD_FACTOR() external view returns (uint256 unregistrationThresholdFactor);
function UNREGISTRATION_THRESHOLD_FACTOR()
external
view
returns (uint256 unregistrationThresholdFactor);
/// @notice Number of epochs an initiative has to exist before it can be unregistered
/// @return registrationWarmUpPeriod Number of epochs
function REGISTRATION_WARM_UP_PERIOD() external view returns (uint256 registrationWarmUpPeriod);
function REGISTRATION_WARM_UP_PERIOD()
external
view
returns (uint256 registrationWarmUpPeriod);
/// @notice Number of epochs an initiative has to be inactive before it can be unregistered
/// @return unregistrationAfterEpochs Number of epochs
function UNREGISTRATION_AFTER_EPOCHS() external view returns (uint256 unregistrationAfterEpochs);
function UNREGISTRATION_AFTER_EPOCHS()
external
view
returns (uint256 unregistrationAfterEpochs);
/// @notice Share of all votes that are necessary for an initiative to be included in the vote count
/// @return votingThresholdFactor Voting threshold factor
function VOTING_THRESHOLD_FACTOR() external view returns (uint256 votingThresholdFactor);
function VOTING_THRESHOLD_FACTOR()
external
view
returns (uint256 votingThresholdFactor);

/// @notice Returns the amount of BOLD accrued since last epoch (last snapshot)
/// @return boldAccrued BOLD accrued
function boldAccrued() external view returns (uint256 boldAccrued);

struct VoteSnapshot {
uint240 votes; // Votes at epoch transition
uint120 votes; // Votes at epoch transition
uint16 forEpoch; // Epoch for which the votes are counted
}

struct InitiativeVoteSnapshot {
uint224 votes; // Votes at epoch transition
uint120 votes; // Votes at epoch transition
uint16 forEpoch; // Epoch for which the votes are counted
uint16 lastCountedEpoch; // Epoch at which which the votes where counted last in the global snapshot
uint224 vetos; // Vetos at epoch transition
uint120 vetos; // Vetos at epoch transition
}

/// @notice Returns the vote count snapshot of the previous epoch
/// @return votes Number of votes
/// @return forEpoch Epoch for which the votes are counted
function votesSnapshot() external view returns (uint240 votes, uint16 forEpoch);
function votesSnapshot()
external
view
returns (uint120 votes, uint16 forEpoch);
/// @notice Returns the vote count snapshot for an initiative of the previous epoch
/// @param _initiative Address of the initiative
/// @return votes Number of votes
/// @return forEpoch Epoch for which the votes are counted
/// @return lastCountedEpoch Epoch at which which the votes where counted last in the global snapshot
function votesForInitiativeSnapshot(address _initiative)
external
view
returns (uint224 votes, uint16 forEpoch, uint16 lastCountedEpoch, uint224 vetos);
function votesForInitiativeSnapshot(
address _initiative
) external view returns (uint120 votes, uint16 forEpoch, uint120 vetos);

struct Allocation {
uint88 voteLQTY; // LQTY allocated vouching for the initiative
Expand Down Expand Up @@ -137,15 +178,22 @@ interface IGovernance {
/// @param _user Address of the user
/// @return allocatedLQTY LQTY allocated by the user
/// @return averageStakingTimestamp Average timestamp at which LQTY was staked (deposited) by the user
function userStates(address _user) external view returns (uint88 allocatedLQTY, uint32 averageStakingTimestamp);
function userStates(
address _user
)
external
view
returns (uint88 allocatedLQTY, uint32 averageStakingTimestamp);
/// @notice Returns the initiative's state
/// @param _initiative Address of the initiative
/// @return voteLQTY LQTY allocated vouching for the initiative
/// @return vetoLQTY LQTY allocated vetoing the initiative
/// @return averageStakingTimestampVoteLQTY // Average staking timestamp of the voting LQTY for the initiative
/// @return averageStakingTimestampVetoLQTY // Average staking timestamp of the vetoing LQTY for the initiative
/// @return lastEpochClaim // Last epoch at which rewards were claimed
function initiativeStates(address _initiative)
function initiativeStates(
address _initiative
)
external
view
returns (
Expand All @@ -158,22 +206,30 @@ interface IGovernance {
/// @notice Returns the global state
/// @return countedVoteLQTY Total LQTY that is included in vote counting
/// @return countedVoteLQTYAverageTimestamp Average timestamp: derived initiativeAllocation.averageTimestamp
function globalState() external view returns (uint88 countedVoteLQTY, uint32 countedVoteLQTYAverageTimestamp);
function globalState()
external
view
returns (
uint88 countedVoteLQTY,
uint32 countedVoteLQTYAverageTimestamp
);
/// @notice Returns the amount of voting and vetoing LQTY a user allocated to an initiative
/// @param _user Address of the user
/// @param _initiative Address of the initiative
/// @return voteLQTY LQTY allocated vouching for the initiative
/// @return vetoLQTY LQTY allocated vetoing the initiative
/// @return atEpoch Epoch at which the allocation was last updated
function lqtyAllocatedByUserToInitiative(address _user, address _initiative)
external
view
returns (uint88 voteLQTY, uint88 vetoLQTY, uint16 atEpoch);
function lqtyAllocatedByUserToInitiative(
address _user,
address _initiative
) external view returns (uint88 voteLQTY, uint88 vetoLQTY, uint16 atEpoch);

/// @notice Returns when an initiative was registered
/// @param _initiative Address of the initiative
/// @return atEpoch Epoch at which the initiative was registered
function registeredInitiatives(address _initiative) external view returns (uint16 atEpoch);
function registeredInitiatives(
address _initiative
) external view returns (uint16 atEpoch);

/*//////////////////////////////////////////////////////////////
STAKING
Expand All @@ -186,15 +242,20 @@ interface IGovernance {
/// @notice Deposits LQTY via Permit
/// @param _lqtyAmount Amount of LQTY to deposit
/// @param _permitParams Permit parameters
function depositLQTYViaPermit(uint88 _lqtyAmount, PermitParams memory _permitParams) external;
function depositLQTYViaPermit(
uint88 _lqtyAmount,
PermitParams memory _permitParams
) external;
/// @notice Withdraws LQTY and claims any accrued LUSD and ETH rewards from StakingV1
/// @param _lqtyAmount Amount of LQTY to withdraw
function withdrawLQTY(uint88 _lqtyAmount) external;
/// @notice Claims staking rewards from StakingV1 without unstaking
/// @param _rewardRecipient Address that will receive the rewards
/// @return accruedLUSD Amount of LUSD accrued
/// @return accruedETH Amount of ETH accrued
function claimFromStakingV1(address _rewardRecipient) external returns (uint256 accruedLUSD, uint256 accruedETH);
function claimFromStakingV1(
address _rewardRecipient
) external returns (uint256 accruedLUSD, uint256 accruedETH);

/*//////////////////////////////////////////////////////////////
VOTING
Expand All @@ -208,30 +269,42 @@ interface IGovernance {
function epochStart() external view returns (uint32 epochStart);
/// @notice Returns the number of seconds that have gone by since the current epoch started
/// @return secondsWithinEpoch Seconds within the current epoch
function secondsWithinEpoch() external view returns (uint32 secondsWithinEpoch);
function secondsWithinEpoch()
external
view
returns (uint32 secondsWithinEpoch);
/// @notice Returns the number of votes per LQTY for a user
/// @param _lqtyAmount Amount of LQTY to convert to votes
/// @param _currentTimestamp Current timestamp
/// @param _averageTimestamp Average timestamp at which the LQTY was staked
/// @return votes Number of votes
function lqtyToVotes(uint88 _lqtyAmount, uint256 _currentTimestamp, uint32 _averageTimestamp)
external
pure
returns (uint240);
function lqtyToVotes(
uint88 _lqtyAmount,
uint32 _currentTimestamp,
uint32 _averageTimestamp
) external pure returns (uint120);

/// @notice Voting threshold is the max. of either:
/// - 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 calculateVotingThreshold()
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
/// @return voteSnapshot Vote snapshot
/// @return initiativeVoteSnapshot Vote snapshot of the initiative
function snapshotVotesForInitiative(address _initiative)
function snapshotVotesForInitiative(
address _initiative
)
external
returns (VoteSnapshot memory voteSnapshot, InitiativeVoteSnapshot memory initiativeVoteSnapshot);
returns (
VoteSnapshot memory voteSnapshot,
InitiativeVoteSnapshot memory initiativeVoteSnapshot
);

/// @notice Registers a new initiative
/// @param _initiative Address of the initiative
Expand All @@ -247,11 +320,16 @@ interface IGovernance {
/// @param _initiatives Addresses of the initiatives to allocate to
/// @param _deltaLQTYVotes Delta LQTY to allocate to the initiatives as votes
/// @param _deltaLQTYVetos Delta LQTY to allocate to the initiatives as vetos
function allocateLQTY(address[] memory _initiatives, int88[] memory _deltaLQTYVotes, int88[] memory _deltaLQTYVetos)
external;
function allocateLQTY(
address[] memory _initiatives,
int88[] memory _deltaLQTYVotes,
int88[] memory _deltaLQTYVetos
) external;

/// @notice Splits accrued funds according to votes received between all initiatives
/// @param _initiative Addresse of the initiative
/// @return claimed Amount of BOLD claimed
function claimForInitiative(address _initiative) external returns (uint256 claimed);
function claimForInitiative(
address _initiative
) external returns (uint256 claimed);
}
Loading