Skip to content

Commit 1c29fd1

Browse files
ducquangkstnilanDoron
authored andcommitted
Add EmergencyFeeHandler that can be used to collect fees in the event that kyberFeeHandler has a bug or issue.
* a simple version of kyberFeeHandler * will enable collecting fee if any bug found in KyberFeeHandler
1 parent cb48a5d commit 1c29fd1

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)