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