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

Dev governance refactor #16

Merged
merged 104 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
958749f
test for vote amount
ColinPlatt Oct 4, 2024
f66f972
Restrict deltas to int96 and assert <= 2**88
jltqy Oct 4, 2024
f1f741e
fix: compilation - safeTransfer
GalloDaSballo Oct 10, 2024
d56007f
feat: introduce `lastEpochClaim`
GalloDaSballo Oct 10, 2024
66f081c
feat: invariant - can only claim or unregister per epoch
GalloDaSballo Oct 10, 2024
bd34a83
fix: test that is broken
GalloDaSballo Oct 10, 2024
f63a541
feat: `getInitiativeState`
GalloDaSballo Oct 10, 2024
a06b94b
feat: initial fixes around maintaining storage for disabled proposals
GalloDaSballo Oct 10, 2024
1be0fb6
Merge branch 'main' into fix/voteCheck
GalloDaSballo Oct 10, 2024
84fc1e8
feat: `test_crit_accounting_mismatch`and `test_canAlwaysRemoveAllocat…
GalloDaSballo Oct 11, 2024
dcc3dd0
feat: use constant for disabled initiatives
GalloDaSballo Oct 11, 2024
efab2c6
chore: cleanup + isolate overflow checks
GalloDaSballo Oct 11, 2024
e9d117d
Restrict deltas to int88
jltqy Oct 11, 2024
a816b67
feat: FSM is enforced
GalloDaSballo Oct 11, 2024
2c52bc3
chore: remove logs
GalloDaSballo Oct 11, 2024
1fee011
feat: return 0 on duplicate claim
GalloDaSballo Oct 11, 2024
838b48c
chore: re-order functions
GalloDaSballo Oct 11, 2024
7f66112
feat: enum initiative status and improvement to `getInitiativeState`
GalloDaSballo Oct 11, 2024
9e8fd9e
feat: snapshot asserts
GalloDaSballo Oct 11, 2024
e4ccbc0
chore: future notes
GalloDaSballo Oct 11, 2024
480c077
feat: counted is gone
GalloDaSballo Oct 11, 2024
ac4d1eb
feat: counted is gone
GalloDaSballo Oct 11, 2024
9c9f77c
feat: introduce vetos as part of snapshot storage
GalloDaSballo Oct 12, 2024
10e3659
feat: use vetos from snapshot to determine FSM
GalloDaSballo Oct 12, 2024
bbc9dc8
chore: remove unused variable
GalloDaSballo Oct 12, 2024
27b33db
feat: use `getInitiativeState` for `unregisterInitiative`
GalloDaSballo Oct 12, 2024
0584ac0
chore: comments
GalloDaSballo Oct 12, 2024
c1945bf
fix: vetos, `lastCountedEpoch` and votes FSM follow the spec
GalloDaSballo Oct 12, 2024
eb2e1ec
Merge branch 'dev-governance-refactor' into fix/voteCheck
GalloDaSballo Oct 13, 2024
3adf6dc
fix: compilation
GalloDaSballo Oct 13, 2024
77fe12b
feat: broken | math tests
GalloDaSballo Oct 13, 2024
a4af757
feat: ref vs lib tests
GalloDaSballo Oct 14, 2024
78a4e55
Merge branch 'dev' into dev-governance-refactor
GalloDaSballo Oct 14, 2024
03c5315
fix: compilation
GalloDaSballo Oct 14, 2024
a932aa0
feat: fuzz tests for encoding + decoding allocation
nican0r Oct 14, 2024
65f466b
test: unit test for reproducing issue with encoding
nican0r Oct 14, 2024
9e10b09
fix: encoded value passed in for decoding
nican0r Oct 14, 2024
d5b9a21
Merge branch 'dev-governance-refactor' into allocation-encoding-tests
GalloDaSballo Oct 14, 2024
f5d30e8
Merge branch 'dev' into allocation-encoding-tests
GalloDaSballo Oct 14, 2024
629799f
fix: random use of solmate
GalloDaSballo Oct 14, 2024
e662cb7
test: rational flow for allocating/claiming bribe
nican0r Oct 14, 2024
f4ca88f
feat: encode / deocde
GalloDaSballo Oct 14, 2024
4bb1a58
feat: testFail
GalloDaSballo Oct 14, 2024
e84c00d
Merge pull request #24 from liquity/allocation-encoding-tests
GalloDaSballo Oct 14, 2024
3a6c038
fix: bribe CEI
GalloDaSballo Oct 14, 2024
af5132f
fix: cannot claim in the future
GalloDaSballo Oct 14, 2024
f118d1f
feat: rational flow
GalloDaSballo Oct 14, 2024
12f94ed
fix: use lib for encoding / decoding
GalloDaSballo Oct 14, 2024
79ab942
chore: adding governance scaffolding
nican0r Oct 14, 2024
d94b2cd
feat: property_GV01
nican0r Oct 14, 2024
2e38b40
feat: adding properties and scaffolding from bribe-initiative-invar-t…
nican0r Oct 15, 2024
eae43cd
chore: uncommenting properties
nican0r Oct 15, 2024
0d2e524
chore: updating properties table
nican0r Oct 15, 2024
0b1ab3a
Merge branch 'dev' into dev-governance-refactor
GalloDaSballo Oct 15, 2024
ceb0f7e
fix: add sanity checks to thresholds
GalloDaSballo Oct 15, 2024
8b3670e
feat: move logic to allow removing `lastCountedEpoch`
GalloDaSballo Oct 15, 2024
9109d98
feat: remove usage of `lastCountedEpoch`
GalloDaSballo Oct 15, 2024
089b968
feat: for packing
GalloDaSballo Oct 15, 2024
b692f5b
feat: view variants of snapshot functions
GalloDaSballo Oct 15, 2024
8e60ecb
chore: cleanup
GalloDaSballo Oct 15, 2024
3a7fd77
chore: more cleanuop
GalloDaSballo Oct 15, 2024
ab20d48
feat: stateless checks use memory variables
GalloDaSballo Oct 15, 2024
d77fa12
fix: 5.3
GalloDaSballo Oct 15, 2024
e39a3fd
fix: remove unnecessary threshold
GalloDaSballo Oct 15, 2024
f964608
Merge pull request #34 from liquity/feat-dev-stateless-checks
GalloDaSballo Oct 15, 2024
d4a20e1
Merge pull request #25 from liquity/bribe-claiming-unit-test
GalloDaSballo Oct 15, 2024
d4c2058
Merge remote-tracking branch 'origin/gov-invar-tester-after-refactor'…
GalloDaSballo Oct 15, 2024
0b72301
fix: deps
GalloDaSballo Oct 15, 2024
d60e671
fix: medusa and signature
GalloDaSballo Oct 15, 2024
6c8de58
fix: cryticToFoundry and overflow
GalloDaSballo Oct 15, 2024
cd7fba3
fix: property
GalloDaSballo Oct 15, 2024
dfefc27
fix: introduce more states to track initiative and fix invariant test
GalloDaSballo Oct 15, 2024
c4da233
chore: gas + docs
GalloDaSballo Oct 15, 2024
3d192fa
feat: view vs non view properties
GalloDaSballo Oct 15, 2024
aed7a96
feat: mostly using FSM, can simplify a bit
GalloDaSballo Oct 15, 2024
c312fcb
feat: commiting to using FSM
GalloDaSballo Oct 15, 2024
02bf481
feat: unregisterInitiative FSM equivalence
GalloDaSballo Oct 15, 2024
121bede
chore: comment
GalloDaSballo Oct 15, 2024
5d34676
chore: comment
GalloDaSballo Oct 15, 2024
e80a9ce
feat: simplify claim logic
GalloDaSballo Oct 15, 2024
5f863c3
feat: pretty close to done
GalloDaSballo Oct 16, 2024
cf0f98c
feat: cache data for `unregisterInitiative`
GalloDaSballo Oct 16, 2024
3660391
gas: `claimForInitiative`
GalloDaSballo Oct 16, 2024
16bf1d3
fix: remove extra SSTORE
GalloDaSballo Oct 16, 2024
6309c90
feat: FSM + Crit notice
GalloDaSballo Oct 16, 2024
ad3d9f8
Merge branch 'exp-external-functions-fsm' into dev-governance-refactor
GalloDaSballo Oct 16, 2024
83657b5
gas: remove extra 0 check
GalloDaSballo Oct 16, 2024
6b15ed6
chore: cleanup
GalloDaSballo Oct 16, 2024
b45e6a6
fix: Critical accounting bug
GalloDaSballo Oct 16, 2024
0ae541f
chore: future
GalloDaSballo Oct 16, 2024
3927a88
feat: broken key test
GalloDaSballo Oct 16, 2024
56b162d
chore: cleanup
GalloDaSballo Oct 16, 2024
7d375b1
chore: fixes to properties
GalloDaSballo Oct 16, 2024
568f189
chore: sample broken pro
GalloDaSballo Oct 16, 2024
506bf03
feat: view acounting properties
GalloDaSballo Oct 16, 2024
60f12ae
fix: dorky bug
GalloDaSballo Oct 16, 2024
c81f05d
feat: broken property
GalloDaSballo Oct 16, 2024
1541192
feat: trophies
GalloDaSballo Oct 16, 2024
85eecdd
chore: cleanuop
GalloDaSballo Oct 16, 2024
4581c3e
feat: e2e test + make invariant tests more reliable
GalloDaSballo Oct 16, 2024
b6fc28a
chore: further simplify trophy
GalloDaSballo Oct 16, 2024
e4ef357
feat: document the vote vs veto bug
GalloDaSballo Oct 16, 2024
62ed071
feat: e2e test for unregistering
GalloDaSballo Oct 16, 2024
e26ab76
chore: improved README
GalloDaSballo Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ docs/

# Dotenv file
.env

# Fuzzing
crytic-export/
echidna/
medusa/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/v4-core"]
path = lib/v4-core
url = https://github.com/Uniswap/v4-core
[submodule "lib/chimera"]
path = lib/chimera
url = https://github.com/Recon-Fuzz/chimera
12 changes: 12 additions & 0 deletions INTEGRATION.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Risks to integrators

Somebody could claim on your behalf

Votes not meeting the threshold may result in 0 rewards

Claiming more than once will return 0

## INVARIANT: You can only claim for previous epoch

assert(votesSnapshot_.forEpoch == epoch() - 1); /// @audit INVARIANT: You can only claim for previous epoch
/// All unclaimed rewards are always recycled
35 changes: 24 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,35 +51,33 @@ Claims for Initiatives which have met the minimum qualifying threshold, can be c
in which they are awarded. Failure to do so will result in the unclaimed portion being reused in the following epoch.

As Initiatives are assigned to arbitrary addresses, they can be used for any purpose, including EOAs, Multisigs, or smart contracts designed
for targetted purposes. Smart contracts should be designed in a way that they can support BOLD and include any additional logic about
how BOLD is to be used.
for targetted purposes. Smart contracts should be designed in a way that they can support BOLD and include any additional logic about how BOLD is to be used.

### Malicious Initiatives

It's important to note that initiatives could be malicious, and the system does it's best effort to prevent any DOS to happen, however, a malicious initiative could drain all rewards if voted on.

## Voting

Users with LQTY staked in Governance.sol, can allocate LQTY in the same epoch in which they were deposited. But the
effective voting power at that point would be insignificant.
Users with LQTY staked in Governance.sol, can allocate LQTY in the same epoch in which they were deposited. But the effective voting power at that point would be insignificant.

Votes can take two forms, a vote for an Initiative or a veto vote. Initiatives which have received vetoes which are both:
three times greater than the minimum qualifying threshold, and greater than the number of votes for will not be eligible for claims by being excluded from the vote count and maybe deregistered as an Initiative.

Users may split their votes for and veto votes across any number of initiatives. But cannot vote for and veto vote the same Initiative.

Each epoch is split into two parts, a six day period where both votes for and veto votes take place, and a final 24 hour period where votes
can only be made as veto votes. This is designed to give a period where any detrimental distributions can be mitigated should there be
sufficient will to do so by voters, but is not envisaged to be a regular occurance.
Each epoch is split into two parts, a six day period where both votes for and veto votes take place, and a final 24 hour period where votes can only be made as veto votes. This is designed to give a period where any detrimental distributions can be mitigated should there be sufficient will to do so by voters, but is not envisaged to be a regular occurance.

## Snapshots

Snapshots of results from the voting activity of an epoch takes place on an initiative by initiative basis in a permissionless manner.
User interactions or direct calls following the closure of an epoch trigger the snapshot logic which makes a Claim available to a
qualifying Initiative.
User interactions or direct calls following the closure of an epoch trigger the snapshot logic which makes a Claim available to a qualifying Initiative.

## Bribing

LQTY depositors can also receive bribes in the form of ERC20s in exchange for voting for a specified initiative.
This is done externally to the Governance.sol logic and should be implemented at the initiative level.
BaseInitiative.sol is a reference implementation which allows for bribes to be set and paid in BOLD + another token,
all claims for bribes are made by directly interacting with the implemented BaseInitiative contract.
BaseInitiative.sol is a reference implementation which allows for bribes to be set and paid in BOLD + another token, all claims for bribes are made by directly interacting with the implemented BaseInitiative contract.

## Example Initiatives

Expand All @@ -95,3 +93,18 @@ Claiming and depositing to gauges must be done manually after each epoch in whic
### Uniswap v4

Simple hook for Uniswap v4 which implements a donate to a preconfigured pool. Allowing for adjustments to liquidity positions to make Claims which are smoothed over a vesting epoch.

## Known Issues

### Vetoed Initiatives and Initiatives that receive votes that are below the treshold cause a loss of emissions to the voted initiatives

Because the system counts: valid_votes / total_votes
By definition, initiatives that increase the total_votes without receiving any rewards are stealing the rewards from other initiatives

The rewards will be re-queued in the next epoch

see: `test_voteVsVeto` as well as the miro and comments

### User Votes, Initiative Votes and Global State Votes can desynchronize

See `test_property_sum_of_lqty_global_user_matches_0`
10 changes: 10 additions & 0 deletions echidna.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
testMode: "assertion"
prefix: "crytic_"
coverage: true
corpusDir: "echidna"
balanceAddr: 0x1043561a8829300000
balanceContract: 0x1043561a8829300000
filterFunctions: []
cryticArgs: ["--foundry-compile-all"]
testMode: "exploration"
testLimit: 500000000
1 change: 1 addition & 0 deletions lib/chimera
Submodule chimera added at d5cf52
88 changes: 88 additions & 0 deletions medusa.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"fuzzing": {
"workers": 10,
"workerResetLimit": 50,
"timeout": 0,
"testLimit": 0,
"callSequenceLength": 100,
"corpusDirectory": "medusa",
"coverageEnabled": true,
"deploymentOrder": [
"CryticTester"
],
"targetContracts": [
"CryticTester"
],
"targetContractsBalances": [
"0x27b46536c66c8e3000000"
],
"constructorArgs": {},
"deployerAddress": "0x30000",
"senderAddresses": [
"0x10000",
"0x20000",
"0x30000"
],
"blockNumberDelayMax": 60480,
"blockTimestampDelayMax": 604800,
"blockGasLimit": 125000000,
"transactionGasLimit": 12500000,
"testing": {
"stopOnFailedTest": false,
"stopOnFailedContractMatching": false,
"stopOnNoTests": true,
"testAllContracts": false,
"traceAll": false,
"assertionTesting": {
"enabled": true,
"testViewMethods": true,
"panicCodeConfig": {
"failOnCompilerInsertedPanic": false,
"failOnAssertion": true,
"failOnArithmeticUnderflow": false,
"failOnDivideByZero": false,
"failOnEnumTypeConversionOutOfBounds": false,
"failOnIncorrectStorageAccess": false,
"failOnPopEmptyArray": false,
"failOnOutOfBoundsArrayAccess": false,
"failOnAllocateTooMuchMemory": false,
"failOnCallUninitializedVariable": false
}
},
"propertyTesting": {
"enabled": true,
"testPrefixes": [
"crytic_"
]
},
"optimizationTesting": {
"enabled": false,
"testPrefixes": [
"optimize_"
]
}
},
"chainConfig": {
"codeSizeCheckDisabled": true,
"cheatCodes": {
"cheatCodesEnabled": true,
"enableFFI": false
}
}
},
"compilation": {
"platform": "crytic-compile",
"platformConfig": {
"target": ".",
"solcVersion": "",
"exportDirectory": "",
"args": [
"--foundry-compile-all"
]
}
},
"logging": {
"level": "info",
"logDirectory": ""
}
}
2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
v4-core/=lib/v4-core/
forge-std/=lib/forge-std/src/
@chimera/=lib/chimera/src/
20 changes: 13 additions & 7 deletions src/BribeInitiative.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {console} from "forge-std/console.sol";

import {IERC20} from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

Expand All @@ -12,6 +10,10 @@ import {IBribeInitiative} from "./interfaces/IBribeInitiative.sol";

import {DoubleLinkedList} from "./utils/DoubleLinkedList.sol";


import {EncodingDecodingLib} from "src/utils/EncodingDecodingLib.sol";


contract BribeInitiative is IInitiative, IBribeInitiative {
using SafeERC20 for IERC20;
using DoubleLinkedList for DoubleLinkedList.List;
Expand Down Expand Up @@ -56,18 +58,19 @@ contract BribeInitiative is IInitiative, IBribeInitiative {

/// @inheritdoc IBribeInitiative
function depositBribe(uint128 _boldAmount, uint128 _bribeTokenAmount, uint16 _epoch) external {
bold.safeTransferFrom(msg.sender, address(this), _boldAmount);
bribeToken.safeTransferFrom(msg.sender, address(this), _bribeTokenAmount);

uint16 epoch = governance.epoch();
require(_epoch > epoch, "BribeInitiative: only-future-epochs");
require(_epoch >= epoch, "BribeInitiative: only-future-epochs");

Bribe memory bribe = bribeByEpoch[_epoch];
bribe.boldAmount += _boldAmount;
bribe.bribeTokenAmount += _bribeTokenAmount;
bribeByEpoch[_epoch] = bribe;

emit DepositBribe(msg.sender, _boldAmount, _bribeTokenAmount, _epoch);

bold.safeTransferFrom(msg.sender, address(this), _boldAmount);
bribeToken.safeTransferFrom(msg.sender, address(this), _bribeTokenAmount);
}

function _claimBribe(
Expand All @@ -76,7 +79,7 @@ contract BribeInitiative is IInitiative, IBribeInitiative {
uint16 _prevLQTYAllocationEpoch,
uint16 _prevTotalLQTYAllocationEpoch
) internal returns (uint256 boldAmount, uint256 bribeTokenAmount) {
require(_epoch != governance.epoch(), "BribeInitiative: cannot-claim-for-current-epoch");
require(_epoch < governance.epoch(), "BribeInitiative: cannot-claim-for-current-epoch");
require(!claimedBribeAtEpoch[_user][_epoch], "BribeInitiative: already-claimed");

Bribe memory bribe = bribeByEpoch[_epoch];
Expand Down Expand Up @@ -164,8 +167,11 @@ contract BribeInitiative is IInitiative, IBribeInitiative {
emit ModifyLQTYAllocation(_user, _epoch, _lqty, _averageTimestamp);
}

function _encodeLQTYAllocation(uint88 _lqty, uint32 _averageTimestamp) private pure returns (uint224) {
return EncodingDecodingLib.encodeLQTYAllocation(_lqty, _averageTimestamp);
}
function _decodeLQTYAllocation(uint224 _value) private pure returns (uint88, uint32) {
return (uint88(_value >> 32), uint32(_value));
return EncodingDecodingLib.decodeLQTYAllocation(_value);
}

function _loadTotalLQTYAllocation(uint16 _epoch) private view returns (uint88, uint32) {
Expand Down
4 changes: 2 additions & 2 deletions src/ForwardBribe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract ForwardBribe is BribeInitiative {
uint boldAmount = bold.balanceOf(address(this));
uint bribeTokenAmount = bribeToken.balanceOf(address(this));

if (boldAmount != 0) bold.safeTransfer(receiver, boldAmount);
if (bribeTokenAmount != 0) bribeToken.safeTransfer(receiver, bribeTokenAmount);
if (boldAmount != 0) bold.transfer(receiver, boldAmount);
if (bribeTokenAmount != 0) bribeToken.transfer(receiver, bribeTokenAmount);
}
}
Loading
Loading