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
+ }
0 commit comments