Skip to content

Commit ab22c08

Browse files
NoahZinsmeistermoodysalem
authored andcommitted
initial commit
0 parents  commit ab22c08

26 files changed

+10108
-0
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.sol linguist-language=Solidity

.github/workflows/tests.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
9+
jobs:
10+
unit-tests:
11+
name: Unit Tests
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v2
16+
- uses: actions/setup-node@v1
17+
with:
18+
node-version: 12.x
19+
- run: yarn
20+
- run: yarn lint
21+
- run: yarn test

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
build/

.mocharc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extension": ["ts"],
3+
"spec": "./test/**/*.spec.ts",
4+
"require": "ts-node/register",
5+
"timeout": 12000
6+
}

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": false,
3+
"singleQuote": true,
4+
"printWidth": 120
5+
}

.yarnrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ignore-scripts true

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Uniswap Governance
2+
3+
Forked from
4+
[https://github.com/compound-finance/compound-protocol/tree/v2.8.1](https://github.com/compound-finance/compound-protocol/tree/v2.8.1)

contracts/FeeTo.sol

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
pragma solidity ^0.5.16;
2+
3+
// this contract gives owner the ability to allow tokens. for pairs in which both tokens are allowed, fees may be
4+
// collected on that pair and send to feeRecipient, though only after burning all fees up to that point
5+
contract FeeTo {
6+
address public owner;
7+
address public feeRecipient;
8+
9+
struct TokenAllowState {
10+
bool allowed;
11+
uint128 disallowCount;
12+
}
13+
mapping(address => TokenAllowState) public tokenAllowStates;
14+
15+
struct PairAllowState {
16+
uint128 token0DisallowCount;
17+
uint128 token1DisallowCount;
18+
}
19+
mapping(address => PairAllowState) public pairAllowStates;
20+
21+
constructor(address owner_) public {
22+
owner = owner_;
23+
}
24+
25+
function setOwner(address owner_) public {
26+
require(msg.sender == owner, 'FeeTo::setOwner: not allowed');
27+
owner = owner_;
28+
}
29+
30+
function setFeeRecipient(address feeRecipient_) public {
31+
require(msg.sender == owner, 'FeeTo::setFeeRecipient: not allowed');
32+
feeRecipient = feeRecipient_;
33+
}
34+
35+
function updateTokenAllowState(address token, bool allowed) public {
36+
require(msg.sender == owner, 'FeeTo::updateTokenAllowState: not allowed');
37+
TokenAllowState storage tokenAllowState = tokenAllowStates[token];
38+
// if allowed is not changing, the function is a no-op
39+
if (allowed != tokenAllowState.allowed) {
40+
tokenAllowState.allowed = allowed;
41+
// this condition will only be true on the first call to this function (regardless of the value of allowed)
42+
// by effectively initializing disallowCount to 1,
43+
// we force renounce to be called for all pairs including newly allowed token
44+
if (tokenAllowState.disallowCount == 0) {
45+
tokenAllowState.disallowCount = 1;
46+
} else if (allowed == false) {
47+
tokenAllowState.disallowCount += 1;
48+
}
49+
}
50+
}
51+
52+
function updateTokenAllowStates(address[] memory tokens, bool allowed) public {
53+
for (uint i; i < tokens.length; i++) {
54+
updateTokenAllowState(tokens[i], allowed);
55+
}
56+
}
57+
58+
function renounce(address pair) public returns (uint value) {
59+
PairAllowState storage pairAllowState = pairAllowStates[pair];
60+
TokenAllowState storage token0AllowState = tokenAllowStates[IUniswapV2Pair(pair).token0()];
61+
TokenAllowState storage token1AllowState = tokenAllowStates[IUniswapV2Pair(pair).token1()];
62+
63+
// we must renounce if any of the following four conditions are true:
64+
// 1) token0 is currently disallowed
65+
// 2) token1 is currently disallowed
66+
// 3) token0 was disallowed at least once since the last time renounce was called
67+
// 4) token1 was disallowed at least once since the last time renounce was called
68+
if (
69+
token0AllowState.allowed == false ||
70+
token1AllowState.allowed == false ||
71+
token0AllowState.disallowCount > pairAllowState.token0DisallowCount ||
72+
token1AllowState.disallowCount > pairAllowState.token1DisallowCount
73+
) {
74+
value = IUniswapV2Pair(pair).balanceOf(address(this));
75+
if (value > 0) {
76+
// burn balance into the pair, effectively redistributing underlying tokens pro-rata back to LPs
77+
// (assert because transfer cannot fail with value as balanceOf)
78+
assert(IUniswapV2Pair(pair).transfer(pair, value));
79+
IUniswapV2Pair(pair).burn(pair);
80+
}
81+
82+
// if token0 is allowed, we can now update the pair's disallow count to match the token's
83+
if (token0AllowState.allowed) {
84+
pairAllowState.token0DisallowCount = token0AllowState.disallowCount;
85+
}
86+
// if token1 is allowed, we can now update the pair's disallow count to match the token's
87+
if (token1AllowState.allowed) {
88+
pairAllowState.token1DisallowCount = token1AllowState.disallowCount;
89+
}
90+
}
91+
}
92+
93+
function claim(address pair) public returns (uint value) {
94+
PairAllowState storage pairAllowState = pairAllowStates[pair];
95+
TokenAllowState storage token0AllowState = tokenAllowStates[IUniswapV2Pair(pair).token0()];
96+
TokenAllowState storage token1AllowState = tokenAllowStates[IUniswapV2Pair(pair).token1()];
97+
98+
// we may claim only if each of the following five conditions are true:
99+
// 1) token0 is currently allowed
100+
// 2) token1 is currently allowed
101+
// 3) renounce was not called since the last time token0 was disallowed
102+
// 4) renounce was not called since the last time token1 was disallowed
103+
// 5) feeHandler is not the 0 address
104+
if (
105+
token0AllowState.allowed &&
106+
token1AllowState.allowed &&
107+
token0AllowState.disallowCount == pairAllowState.token0DisallowCount &&
108+
token1AllowState.disallowCount == pairAllowState.token1DisallowCount &&
109+
feeRecipient != address(0)
110+
) {
111+
value = IUniswapV2Pair(pair).balanceOf(address(this));
112+
if (value > 0) {
113+
// transfer tokens to the handler (assert because transfer cannot fail with value as balanceOf)
114+
assert(IUniswapV2Pair(pair).transfer(feeRecipient, value));
115+
}
116+
}
117+
}
118+
}
119+
120+
interface IUniswapV2Pair {
121+
function token0() external view returns (address);
122+
function token1() external view returns (address);
123+
function balanceOf(address owner) external view returns (uint);
124+
function transfer(address to, uint value) external returns (bool);
125+
function burn(address to) external returns (uint amount0, uint amount1);
126+
}

contracts/FeeToSetter.sol

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
pragma solidity ^0.5.16;
2+
3+
// this contract serves as feeToSetter, allowing owner to manage fees in the context of a specific feeTo implementation
4+
contract FeeToSetter {
5+
// immutables
6+
address public factory;
7+
uint public vestingEnd;
8+
address public feeTo;
9+
10+
address public owner;
11+
12+
constructor(address factory_, uint vestingEnd_, address owner_, address feeTo_) public {
13+
require(vestingEnd_ > block.timestamp, 'FeeToSetter::constructor: vesting must end after deployment');
14+
factory = factory_;
15+
vestingEnd = vestingEnd_;
16+
owner = owner_;
17+
feeTo = feeTo_;
18+
}
19+
20+
// allows owner to change itself at any time
21+
function setOwner(address owner_) public {
22+
require(msg.sender == owner, 'FeeToSetter::setOwner: not allowed');
23+
owner = owner_;
24+
}
25+
26+
// allows owner to change feeToSetter after vesting
27+
function setFeeToSetter(address feeToSetter_) public {
28+
require(block.timestamp >= vestingEnd, 'FeeToSetter::setFeeToSetter: not time yet');
29+
require(msg.sender == owner, 'FeeToSetter::setFeeToSetter: not allowed');
30+
IUniswapV2Factory(factory).setFeeToSetter(feeToSetter_);
31+
}
32+
33+
// allows owner to turn fees on/off after vesting
34+
function toggleFees(bool on) public {
35+
require(block.timestamp >= vestingEnd, 'FeeToSetter::toggleFees: not time yet');
36+
require(msg.sender == owner, 'FeeToSetter::toggleFees: not allowed');
37+
IUniswapV2Factory(factory).setFeeTo(on ? feeTo : address(0));
38+
}
39+
}
40+
41+
interface IUniswapV2Factory {
42+
function setFeeToSetter(address) external;
43+
function setFeeTo(address) external;
44+
}

0 commit comments

Comments
 (0)