Skip to content

Commit fd171c8

Browse files
authored
Add emergency fee handler. If Dao setup fails or our fee handler is buggy. Can immediately deploy emergency fee handle which will collect fees till the bug is fixed.
* add EmergencyFeeHandler contract that supports IKyberFeeHandler interface.
2 parents 2e823e2 + 944f418 commit fd171c8

File tree

5 files changed

+835
-64
lines changed

5 files changed

+835
-64
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
pragma solidity 0.6.6;
2+
3+
import "../../IKyberFeeHandler.sol";
4+
import "../../utils/PermissionGroupsNoModifiers.sol";
5+
import "../../utils/zeppelin/ReentrancyGuard.sol";
6+
import "../../utils/zeppelin/SafeMath.sol";
7+
import "../../utils/Utils5.sol";
8+
9+
/**
10+
* @title kyberFeeHandler
11+
*
12+
* @dev EmergencyFeeHandler works when dao has problem
13+
* rebateBps and rewardBps is only set when initialization
14+
* user can claim platformfee, rebate and reward will be distributed by admin
15+
*/
16+
contract EmergencyKyberFeeHandler is IKyberFeeHandler, PermissionGroupsNoModifiers, ReentrancyGuard, Utils5 {
17+
using SafeMath for uint256;
18+
19+
uint16 public immutable rewardBps;
20+
uint16 public immutable rebateBps;
21+
address public kyberNetwork;
22+
23+
mapping(address => uint256) public feePerPlatformWallet;
24+
uint256 public totalPlatformFeeWei; // total balance in the contract that is for platform fee
25+
mapping(address => uint256) public rebatePerWallet;
26+
uint256 public totalRewardWei;
27+
28+
event HandleFeeFailed(address[] rebateWallets, uint256[] rebateBpsPerWallet, uint256 feeBRRWei);
29+
30+
event HandleFee(
31+
IERC20 indexed token,
32+
address indexed platformWallet,
33+
uint256 platformFeeWei,
34+
address[] rebateWallets,
35+
uint256[] rebateBpsPerWallet,
36+
uint256 feeBRRWei
37+
);
38+
39+
event FeeDistribution(
40+
IERC20 indexed token,
41+
address indexed platformWallet,
42+
uint256 platformFeeWei,
43+
uint256 rewardWei,
44+
uint256 rebateWei,
45+
address[] rebateWallets,
46+
uint256[] rebatePercentBpsPerWallet,
47+
uint256 burnAmountWei
48+
);
49+
50+
event EtherWithdraw(uint256 amount, address sendTo);
51+
52+
event KyberNetworkUpdated(address kyberNetwork);
53+
54+
constructor(
55+
address admin,
56+
address _kyberNetwork,
57+
uint256 _rewardBps,
58+
uint256 _rebateBps,
59+
uint256 _burnBps
60+
) public PermissionGroupsNoModifiers(admin) {
61+
require(_burnBps.add(_rewardBps).add(_rebateBps) == BPS, "Bad BRR values");
62+
rewardBps = uint16(_rewardBps);
63+
rebateBps = uint16(_rebateBps);
64+
kyberNetwork = _kyberNetwork;
65+
}
66+
67+
modifier onlyKyberNetwork {
68+
require(msg.sender == address(kyberNetwork), "only kyberNetwork");
69+
_;
70+
}
71+
72+
/// @dev handleFees function is called per trade on KyberNetwork. unless the trade is not involving any fees.
73+
/// @param token Token currency of fees
74+
/// @param rebateWallets a list of rebate wallets that will get rebate for this trade.
75+
/// @param rebateBpsPerWallet percentage of rebate for each wallet, out of total rebate.
76+
/// @param platformWallet Wallet address that will receive the platfrom fee.
77+
/// @param platformFee Fee amount in wei the platfrom wallet is entitled to.
78+
/// @param networkFee Fee amount (in wei) to be allocated for BRR
79+
function handleFees(
80+
IERC20 token,
81+
address[] calldata rebateWallets,
82+
uint256[] calldata rebateBpsPerWallet,
83+
address platformWallet,
84+
uint256 platformFee,
85+
uint256 networkFee
86+
) external payable override onlyKyberNetwork nonReentrant {
87+
require(token == ETH_TOKEN_ADDRESS, "token not eth");
88+
require(msg.value == platformFee.add(networkFee), "msg.value not equal to total fees");
89+
90+
// handle platform fee
91+
feePerPlatformWallet[platformWallet] = feePerPlatformWallet[platformWallet].add(
92+
platformFee
93+
);
94+
totalPlatformFeeWei = totalPlatformFeeWei.add(platformFee);
95+
emit HandleFee(ETH_TOKEN_ADDRESS, platformWallet, platformFee, rebateWallets, rebateBpsPerWallet, networkFee);
96+
97+
if (networkFee == 0) {
98+
emit FeeDistribution(
99+
ETH_TOKEN_ADDRESS,
100+
platformWallet,
101+
platformFee,
102+
0,
103+
0,
104+
rebateWallets,
105+
rebateBpsPerWallet,
106+
0
107+
);
108+
return;
109+
}
110+
111+
(bool success, ) = address(this).call(
112+
abi.encodeWithSignature(
113+
"calculateAndRecordFeeData(address,uint256,address[],uint256[],uint256)",
114+
platformWallet,
115+
platformFee,
116+
rebateWallets,
117+
rebateBpsPerWallet,
118+
networkFee
119+
)
120+
);
121+
if (!success) {
122+
emit HandleFeeFailed(rebateWallets, rebateBpsPerWallet, networkFee);
123+
}
124+
}
125+
126+
function calculateAndRecordFeeData(
127+
address platformWallet,
128+
uint256 platformFee,
129+
address[] calldata rebateWallets,
130+
uint256[] calldata rebateBpsPerWallet,
131+
uint256 feeBRRWei
132+
) external virtual {
133+
require(msg.sender == address(this), "only Feehandler contract can call this function");
134+
uint256 rebateWei = feeBRRWei.mul(rebateBps).div(BPS);
135+
uint256 rewardWei = feeBRRWei.mul(rewardBps).div(BPS);
136+
137+
rebateWei = updateRebateValues(rebateWei, rebateWallets, rebateBpsPerWallet);
138+
139+
totalRewardWei = totalRewardWei.add(rewardWei);
140+
141+
uint burnAmountWei = feeBRRWei.sub(rewardWei).sub(rebateWei);
142+
143+
emit FeeDistribution(
144+
ETH_TOKEN_ADDRESS,
145+
platformWallet,
146+
platformFee,
147+
rewardWei,
148+
rebateWei,
149+
rebateWallets,
150+
rebateBpsPerWallet,
151+
burnAmountWei
152+
);
153+
}
154+
155+
/// @dev claim accumulated fee per platform wallet. Called by any address
156+
/// @param platformWallet the wallet to claim fee for. Total accumulated fee sent to this wallet.
157+
/// @return amountWei amount of fee claimed
158+
function claimPlatformFee(address platformWallet)
159+
external
160+
override
161+
nonReentrant
162+
returns (uint256 amountWei)
163+
{
164+
require(feePerPlatformWallet[platformWallet] > 1, "no fee to claim");
165+
// Get total amount of fees accumulated
166+
amountWei = feePerPlatformWallet[platformWallet].sub(1);
167+
168+
// redundant check, but can't happen
169+
assert(totalPlatformFeeWei >= amountWei);
170+
totalPlatformFeeWei = totalPlatformFeeWei.sub(amountWei);
171+
172+
feePerPlatformWallet[platformWallet] = 1; // avoid zero to non zero storage cost
173+
174+
(bool success, ) = platformWallet.call{value: amountWei}("");
175+
require(success, "platform fee transfer failed");
176+
177+
emit PlatformFeePaid(platformWallet, ETH_TOKEN_ADDRESS, amountWei);
178+
return amountWei;
179+
}
180+
181+
function withdraw(address payable sendTo, uint256 amount) external nonReentrant {
182+
onlyAdmin();
183+
184+
uint256 balance = address(this).balance;
185+
// check if the remain balance is enough for withdraw and paying platform fee
186+
require(amount <= balance.sub(totalPlatformFeeWei), "amount > available funds");
187+
188+
(bool success, ) = sendTo.call{value: amount}("");
189+
require(success, "withdraw transfer failed");
190+
emit EtherWithdraw(amount, sendTo);
191+
}
192+
193+
/// @dev claimReserveRebate is implemented for IKyberFeeHandler
194+
function claimReserveRebate(address) external override returns (uint256) {
195+
revert("not implemented");
196+
}
197+
198+
/// @dev claimStakerReward is implemented for IKyberFeeHandler
199+
function claimStakerReward(address, uint256) external override returns (uint256) {
200+
revert("not implemented");
201+
}
202+
203+
/// @dev set new kyberNetwork address by daoOperator
204+
/// @param _kyberNetwork new kyberNetwork contract
205+
function setNetworkContract(address _kyberNetwork) external {
206+
onlyAdmin();
207+
require(_kyberNetwork != address(0), "kyberNetwork 0");
208+
if (_kyberNetwork != kyberNetwork) {
209+
kyberNetwork = _kyberNetwork;
210+
emit KyberNetworkUpdated(kyberNetwork);
211+
}
212+
}
213+
214+
function updateRebateValues(
215+
uint256 rebateWei,
216+
address[] memory rebateWallets,
217+
uint256[] memory rebateBpsPerWallet
218+
) internal returns (uint256 totalRebatePaidWei) {
219+
uint256 totalRebateBps;
220+
uint256 walletRebateWei;
221+
222+
for (uint256 i = 0; i < rebateWallets.length; i++) {
223+
require(rebateWallets[i] != address(0), "rebate wallet address 0");
224+
225+
walletRebateWei = rebateWei.mul(rebateBpsPerWallet[i]).div(BPS);
226+
rebatePerWallet[rebateWallets[i]] = rebatePerWallet[rebateWallets[i]].add(
227+
walletRebateWei
228+
);
229+
230+
// a few wei could be left out due to rounding down. so count only paid wei
231+
totalRebatePaidWei = totalRebatePaidWei.add(walletRebateWei);
232+
totalRebateBps = totalRebateBps.add(rebateBpsPerWallet[i]);
233+
}
234+
235+
require(totalRebateBps <= BPS, "rebates more then 100%");
236+
}
237+
238+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
pragma solidity 0.6.6;
2+
3+
import "../emergency/EmergencyFeeHandler.sol";
4+
5+
contract MockEmergencyFeeHandler is EmergencyKyberFeeHandler {
6+
constructor(
7+
address admin,
8+
address _kyberNetwork,
9+
uint256 _rewardBps,
10+
uint256 _rebateBps,
11+
uint256 _burnBps
12+
) public EmergencyKyberFeeHandler(admin, _kyberNetwork, _rewardBps, _rebateBps, _burnBps) {}
13+
14+
function calculateAndRecordFeeData(
15+
address,
16+
uint256,
17+
address[] calldata,
18+
uint256[] calldata,
19+
uint256
20+
) external override {
21+
revert();
22+
}
23+
}

package-lock.json

Lines changed: 17 additions & 63 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)