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