Skip to content

Commit d7a026d

Browse files
Fix handling of rebate leftover (for a reserve that is accounted fee but not entitled for rebate)
* For split trade 50:50 with 2 reserves fee entitled. out of them one is entitled rebate. current: 1 entitled rebate reserve will receive all rebate amount fix: entitled rebate reserve will receive rebate amount according to its split in the trade Co-authored-by: Ilan Doron <[email protected]> * Update handling of rebate leftover. use leftover for extra rewards instead of burning * if 2 reserves are fee accounted but only 1 reserve entitled for rebate, there is some leftover of rebate amount. current: leftover rebate will be burnt fix: leftover rebate will be added to reward amount Co-authored-by: Desmond <[email protected]>
1 parent ed32f24 commit d7a026d

File tree

5 files changed

+64
-52
lines changed

5 files changed

+64
-52
lines changed

contracts/sol6/Dao/KyberFeeHandler.sol

+14-6
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ contract KyberFeeHandler is IKyberFeeHandler, Utils5, DaoOperator, ReentrancyGua
6161

6262
struct BRRWei {
6363
uint256 rewardWei;
64-
uint256 rebateWei;
64+
uint256 fullRebateWei;
65+
uint256 paidRebateWei;
6566
uint256 burnWei;
6667
}
6768

@@ -203,22 +204,29 @@ contract KyberFeeHandler is IKyberFeeHandler, Utils5, DaoOperator, ReentrancyGua
203204
uint256 epoch;
204205

205206
// Decoding BRR data
206-
(brrAmounts.rewardWei, brrAmounts.rebateWei, epoch) = getRRWeiValues(networkFee);
207+
(brrAmounts.rewardWei, brrAmounts.fullRebateWei, epoch) = getRRWeiValues(networkFee);
207208

208-
brrAmounts.rebateWei = updateRebateValues(brrAmounts.rebateWei, rebateWallets, rebateBpsPerWallet);
209+
brrAmounts.paidRebateWei = updateRebateValues(
210+
brrAmounts.fullRebateWei, rebateWallets, rebateBpsPerWallet
211+
);
212+
brrAmounts.rewardWei = brrAmounts.rewardWei.add(
213+
brrAmounts.fullRebateWei.sub(brrAmounts.paidRebateWei)
214+
);
209215

210216
rewardsPerEpoch[epoch] = rewardsPerEpoch[epoch].add(brrAmounts.rewardWei);
211217

212218
// update total balance of rewards, rebates, fee
213-
totalPayoutBalance = totalPayoutBalance.add(platformFee).add(brrAmounts.rewardWei).add(brrAmounts.rebateWei);
219+
totalPayoutBalance = totalPayoutBalance.add(
220+
platformFee).add(brrAmounts.rewardWei).add(brrAmounts.paidRebateWei
221+
);
214222

215-
brrAmounts.burnWei = networkFee.sub(brrAmounts.rewardWei).sub(brrAmounts.rebateWei);
223+
brrAmounts.burnWei = networkFee.sub(brrAmounts.rewardWei).sub(brrAmounts.paidRebateWei);
216224
emit FeeDistributed(
217225
ETH_TOKEN_ADDRESS,
218226
platformWallet,
219227
platformFee,
220228
brrAmounts.rewardWei,
221-
brrAmounts.rebateWei,
229+
brrAmounts.paidRebateWei,
222230
rebateWallets,
223231
rebateBpsPerWallet,
224232
brrAmounts.burnWei

contracts/sol6/KyberNetwork.sol

+4-8
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ contract KyberNetwork is WithdrawableNoModifiers, Utils5, IKyberNetwork, Reentra
6262
/// @param networkFeeBps Network fee bps determined by kyberDao, or default value
6363
/// @param numEntitledRebateReserves No. of reserves that are eligible for rebates
6464
/// @param feeAccountedBps Proportion of this trade that fee is accounted to, in BPS. Up to 2 * BPS
65-
/// @param entitledRebateBps Proportion of reserves entitled for rebate, in BPS. Up to 2 * BPS
66-
/// @param rateWithNetworkFee src -> dest token rate, after accounting for only network fee
6765
struct TradeData {
6866
TradeInput input;
6967
ReservesData tokenToEth;
@@ -74,7 +72,6 @@ contract KyberNetwork is WithdrawableNoModifiers, Utils5, IKyberNetwork, Reentra
7472
uint256 networkFeeBps;
7573
uint256 numEntitledRebateReserves;
7674
uint256 feeAccountedBps; // what part of this trade is fee paying. for token -> token - up to 200%
77-
uint256 entitledRebateBps;
7875
}
7976

8077
struct TradeInput {
@@ -979,7 +976,7 @@ contract KyberNetwork is WithdrawableNoModifiers, Utils5, IKyberNetwork, Reentra
979976
rebatePercentBps,
980977
tradeData.tokenToEth,
981978
index,
982-
tradeData.entitledRebateBps
979+
tradeData.feeAccountedBps
983980
);
984981

985982
// eth -> token
@@ -988,7 +985,7 @@ contract KyberNetwork is WithdrawableNoModifiers, Utils5, IKyberNetwork, Reentra
988985
rebatePercentBps,
989986
tradeData.ethToToken,
990987
index,
991-
tradeData.entitledRebateBps
988+
tradeData.feeAccountedBps
992989
);
993990

994991
rebateWallets = kyberStorage.getRebateWalletsFromIds(rebateReserveIds);
@@ -999,14 +996,14 @@ contract KyberNetwork is WithdrawableNoModifiers, Utils5, IKyberNetwork, Reentra
999996
uint256[] memory rebatePercentBps,
1000997
ReservesData memory reservesData,
1001998
uint256 index,
1002-
uint256 entitledRebateBps
999+
uint256 feeAccountedBps
10031000
) internal pure returns (uint256) {
10041001
uint256 _index = index;
10051002

10061003
for (uint256 i = 0; i < reservesData.isEntitledRebateFlags.length; i++) {
10071004
if (reservesData.isEntitledRebateFlags[i]) {
10081005
rebateReserveIds[_index] = reservesData.ids[i];
1009-
rebatePercentBps[_index] = (reservesData.splitsBps[i] * BPS) / entitledRebateBps;
1006+
rebatePercentBps[_index] = (reservesData.splitsBps[i] * BPS) / feeAccountedBps;
10101007
_index++;
10111008
}
10121009
}
@@ -1130,7 +1127,6 @@ contract KyberNetwork is WithdrawableNoModifiers, Utils5, IKyberNetwork, Reentra
11301127
tradeData.feeAccountedBps += reservesData.splitsBps[i];
11311128

11321129
if (reservesData.isEntitledRebateFlags[i]) {
1133-
tradeData.entitledRebateBps += reservesData.splitsBps[i];
11341130
tradeData.numEntitledRebateReserves++;
11351131
}
11361132
}

solcOptimiserSettings.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
module.exports = {
22
enabled: true,
3-
runs: 410
3+
runs: 430
44
}

test/sol6/kyberFeeHandler.js

+16-12
Original file line numberDiff line numberDiff line change
@@ -540,11 +540,16 @@ contract('KyberFeeHandler', function(accounts) {
540540

541541
expectedTotalReward = expectedTotalReward.add(sendVal.mul(currentRewardBps).div(BPS));
542542
expectedRebate = sendVal.mul(currentRebateBps).div(BPS);
543+
let totalPaidRebate = new BN(0);
543544

544545
for(let i = 0; i < rebateBpsPerWallet.length; i++) {
545-
expectedTotalRebate.iadd((new BN(rebateBpsPerWallet[i])).mul(expectedRebate).div(BPS));
546+
let paidRebate = (new BN(rebateBpsPerWallet[i])).mul(expectedRebate).div(BPS);
547+
expectedTotalRebate.iadd(paidRebate);
548+
totalPaidRebate = totalPaidRebate.add(paidRebate);
546549
}
547550

551+
//whatever wasn't paid for rebates to wallets due to rounding errors, will be added to reward amount
552+
expectedTotalReward = expectedTotalReward.add(expectedRebate.sub(totalPaidRebate));
548553
expectedTotalPayOut = expectedTotalReward.add(expectedTotalRebate);
549554
totalPayOutBalance = await feeHandler.totalPayoutBalance();
550555
Helper.assertEqual(expectedTotalPayOut, totalPayOutBalance);
@@ -744,9 +749,7 @@ contract('KyberFeeHandler', function(accounts) {
744749
rebateWallets, rebateBpsPerWallet
745750
);
746751

747-
expectedRewardPerEpoch = expectedRewardPerEpoch.add(sendVal.mul(currentRewardBps).div(BPS));
748-
rewardPerEpoch = await feeHandler.rewardsPerEpoch(currentEpoch);
749-
Helper.assertEqual(expectedRewardPerEpoch, rewardPerEpoch);
752+
//above function also validates reward per eopch.
750753
});
751754

752755
it("test reward per eopch updated when epoch advances", async() => {
@@ -770,14 +773,9 @@ contract('KyberFeeHandler', function(accounts) {
770773
Helper.assertEqual(0, rewardPerEpoch);
771774

772775
sendVal = oneEth.div(new BN(333));
773-
expectedRebates = await callHandleFeeAndVerifyValues(
774-
sendVal, zeroAddress, 0, currentRebateBps, currentRewardBps, currentEpoch,
775-
rebateWallets, rebateBpsPerWallet
776-
);
776+
expectedRebates = await callHandleFeeAndVerifyValues(sendVal, zeroAddress, 0, currentRebateBps, currentRewardBps, currentEpoch,rebateWallets, rebateBpsPerWallet);
777777

778-
expectedRewardPerEpoch = sendVal.mul(currentRewardBps).div(BPS);
779-
rewardPerEpoch = await feeHandler.rewardsPerEpoch(currentEpoch);
780-
Helper.assertEqual(expectedRewardPerEpoch, rewardPerEpoch);
778+
//above function also validates reward per eopch.
781779
});
782780

783781
it("claim reward and see total payout balance updated.", async() => {
@@ -2027,12 +2025,18 @@ async function callHandleFeeAndVerifyValues(sendValWei, platformWallet, platFeeW
20272025

20282026
//validate values
20292027
let expectedRebates = [];
2028+
let totalRebateWei = feeAmountBRR.mul(rebateBps).div(BPS);
2029+
let paidRebatesWei = new BN(0);
20302030
for (let i = 0; i < rebateWalletArr.length; i++) {
2031-
expectedRebates[i] = currentRebatesArr[i].add(feeAmountBRR.mul(rebateBps).div(BPS).mul(rebateBpsArr[i]).div(BPS));
2031+
let walletRebateWei = totalRebateWei.mul(rebateBpsArr[i]).div(BPS)
2032+
expectedRebates[i] = currentRebatesArr[i].add(walletRebateWei);
2033+
paidRebatesWei = paidRebatesWei.add(walletRebateWei);
20322034
let actualRebate = await feeHandler.rebatePerWallet(rebateWalletArr[i]);
20332035
Helper.assertEqual(actualRebate, expectedRebates[i]);
20342036
}
20352037

2038+
const extraRewardFromUnpaidRebate = totalRebateWei.sub(paidRebatesWei);
2039+
expectedRewardForEpoch = expectedRewardForEpoch.add(extraRewardFromUnpaidRebate);
20362040
const actualFeeWallet = await feeHandler.feePerPlatformWallet(platformWallet);
20372041
Helper.assertEqual(actualFeeWallet, expectedPlatWalletFee);
20382042

test/sol6/kyberNetwork.js

+29-25
Original file line numberDiff line numberDiff line change
@@ -2399,7 +2399,7 @@ contract('KyberNetwork', function(accounts) {
23992399
});
24002400
});
24012401

2402-
describe("test fee handler integrations with 1 mock and 1 apr", async() => {
2402+
describe("test fee handler integrations with 1 mock and 1 fpr", async() => {
24032403
let platformFee = new BN(200);
24042404
let reserveIdToWallet = [];
24052405
let rebateWallets;
@@ -2457,26 +2457,24 @@ contract('KyberNetwork', function(accounts) {
24572457
reserveInstances = {};
24582458
});
24592459

2460-
async function assertFeeHandlerUpdate(tradeWei, platfromFeeBps, feeAccountedBps, rebatePerWallet){
2460+
async function assertFeeHandlerUpdate(tradeWei, platfromFeeBps, feeAccountedBps, rebateEntitledBps, rebatePerWallet){
24612461
platformFeeWei = tradeWei.mul(platfromFeeBps).div(BPS);
24622462
Helper.assertEqual(await feeHandler.feePerPlatformWallet(platformWallet), beforePlatformFee.add(platformFeeWei), "unexpected rebate value");
24632463
networkFeeWei = tradeWei.mul(networkFeeBps).div(BPS).mul(feeAccountedBps).div(BPS);
2464-
let hasRebate = false;
2464+
rebateWei = zeroBN;
24652465
for (const [rebateWallet, beforeBalance] of Object.entries(beforeRebate)) {
24662466
if (rebateWallet in rebatePerWallet) {
2467+
rebateWei = rebateWei.add(rebatePerWallet[rebateWallet]);
24672468
Helper.assertApproximate(await feeHandler.rebatePerWallet(rebateWallet), beforeBalance.add(rebatePerWallet[rebateWallet]), "unexpected rebate value");
2468-
hasRebate = true;
24692469
}else {
24702470
Helper.assertApproximate(await feeHandler.rebatePerWallet(rebateWallet), beforeBalance, "unexpected rebate value");
24712471
}
24722472
}
2473-
if (hasRebate) {
2474-
totalPayout = platformFeeWei.add(networkFeeWei.mul(rewardInBPS).div(BPS)).add(networkFeeWei.mul(rebateInBPS).div(BPS));
2475-
} else {
2476-
totalPayout = platformFeeWei.add(networkFeeWei.mul(rewardInBPS).div(BPS));
2477-
}
2473+
2474+
let rebateLeftOver = feeAccountedBps == zeroBN? zeroBN : networkFeeWei.mul(feeAccountedBps.sub(rebateEntitledBps)).div(feeAccountedBps).mul(rebateInBPS).div(BPS);
2475+
let rewardWei = (networkFeeWei.mul(rewardInBPS).div(BPS)).add(rebateLeftOver);
2476+
totalPayout = platformFeeWei.add(rewardWei.add(rebateWei));
24782477
Helper.assertApproximate(await feeHandler.totalPayoutBalance(), beforeTotalBalancePayout.add(totalPayout), "unexpected payout balance");
2479-
24802478
}
24812479

24822480
it("e2t trade. see fee updated in fee handler.", async() => {
@@ -2494,7 +2492,7 @@ contract('KyberNetwork', function(accounts) {
24942492
let expectedRebate = new BN(tradeEventArgs.ethWeiValue).mul(networkFeeBps).div(BPS).mul(rebateInBPS).div(BPS);
24952493
let rebatePerWallet = {}
24962494
rebatePerWallet[rebateWallet] = expectedRebate;
2497-
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, platformFee, BPS, rebatePerWallet);
2495+
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, platformFee, BPS, BPS, rebatePerWallet);
24982496
});
24992497

25002498
it("t2e trade. see fee in fee handler.", async() => {
@@ -2513,10 +2511,10 @@ contract('KyberNetwork', function(accounts) {
25132511
let expectedRebate = new BN(tradeEventArgs.ethWeiValue).mul(networkFeeBps).div(BPS).mul(rebateInBPS).div(BPS);
25142512
let rebatePerWallet = {}
25152513
rebatePerWallet[rebateWallet] = expectedRebate;
2516-
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, platformFee, BPS, rebatePerWallet);
2514+
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, platformFee, BPS, BPS, rebatePerWallet);
25172515
});
25182516

2519-
it("should have rebate given only to rebate entitled reserve.", async() => {
2517+
it("check that reserve rebate amount is correct", async() => {
25202518
await storage.setFeeAccountedPerReserveType(true, true, true, true, true, true, {from: admin});
25212519
// set rebate entitled true for FPR
25222520
await storage.setEntitledRebatePerReserveType(true, false, false, false, false, false, {from: admin});
@@ -2530,14 +2528,22 @@ contract('KyberNetwork', function(accounts) {
25302528
await srcToken.transfer(network.address, srcQty);
25312529
hint = await nwHelper.getHint(rateHelper, matchingEngine, reserveInstances, SPLIT_HINTTYPE, numReserves,
25322530
srcToken.address, destToken.address, srcQty);
2531+
25332532
txResult = await network.tradeWithHintAndFee(networkProxy, srcToken.address, srcQty, destToken.address, taker,
25342533
maxDestAmt, minConversionRate, platformWallet, platformFee, hint, {from: networkProxy});
2534+
25352535
// assert first rebateWallet is received enitled rebate value
25362536
let tradeEventArgs = nwHelper.getTradeEventArgs(txResult);
2537-
let rebatePerWallet = {}
2538-
let expectedRebate = new BN(tradeEventArgs.ethWeiValue).mul(networkFeeBps).div(BPS).mul(new BN(2)).mul(rebateInBPS).div(BPS);
2539-
rebatePerWallet[rebateWallets[0]] = expectedRebate
2540-
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, platformFee, BPS.mul(new BN(2)), rebatePerWallet);
2537+
let rebatePerWallet = {};
2538+
let feeAccountedBps = new BN(2).mul(BPS);
2539+
let entitledRebateBps = BPS;
2540+
let networkFeeWei = new BN(tradeEventArgs.ethWeiValue).mul(networkFeeBps).div(BPS).mul(new BN(2));
2541+
let expectedRebateWei = networkFeeWei.mul(entitledRebateBps).div(feeAccountedBps).mul(rebateInBPS).div(BPS);
2542+
2543+
rebatePerWallet[rebateWallets[0]] = expectedRebateWei;
2544+
rebatePerWallet[rebateWallets[1]] = zeroBN;
2545+
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, platformFee, feeAccountedBps,
2546+
entitledRebateBps, rebatePerWallet);
25412547
// revert changes
25422548
await storage.setEntitledRebatePerReserveType(true, true, true, true, true, true, {from: admin});
25432549
});
@@ -2551,27 +2557,25 @@ contract('KyberNetwork', function(accounts) {
25512557
let txResult = await network.tradeWithHintAndFee(networkProxy, ethAddress, srcQty, srcToken.address, taker,
25522558
maxDestAmt, minConversionRate, platformWallet, zeroBN, '0x', {from: networkProxy, value: srcQty});
25532559
let tradeEventArgs = nwHelper.getTradeEventArgs(txResult);
2554-
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, zeroBN, zeroBN, {});
2560+
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, zeroBN, zeroBN, zeroBN, {});
25552561

25562562
// revert changes
25572563
await storage.setFeeAccountedPerReserveType(true, true, true, true, true, true, { from: admin });
25582564
await storage.setEntitledRebatePerReserveType(true, true, true, true, true, true, {from: admin});
25592565
});
25602566

2561-
it("should have no rebate fee if entitled rebate data is set to false", async() => {
2567+
it("should have no rebate if entitled rebate data is set to false", async() => {
25622568
await storage.setFeeAccountedPerReserveType(true, true, true, true, true, true, {from: admin});
25632569
// set entitled rebate data to false
25642570
await storage.setEntitledRebatePerReserveType(false, false, false, false, false, false, {from: admin});
2565-
2566-
// let payoutBalance0 = await feeHandler.totalPayoutBalance();
2571+
25672572
let srcQty = oneEth;
25682573

25692574
let txResult = await network.tradeWithHintAndFee(networkProxy, ethAddress, srcQty, srcToken.address, taker,
25702575
maxDestAmt, minConversionRate, platformWallet, zeroBN, '0x', {from: networkProxy, value: srcQty});
2571-
// let payoutBalance1 = await feeHandler.totalPayoutBalance();
2572-
// Helper.assertEqual(payoutBalance0, payoutBalance1);
2576+
25732577
let tradeEventArgs = nwHelper.getTradeEventArgs(txResult);
2574-
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, zeroBN, BPS, {});
2578+
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, zeroBN, BPS, zeroBN, {});
25752579

25762580
// reset fees
25772581
await storage.setFeeAccountedPerReserveType(true, true, true, true, true, true, { from: admin });
@@ -2587,7 +2591,7 @@ contract('KyberNetwork', function(accounts) {
25872591
let txResult = await network.tradeWithHintAndFee(networkProxy, ethAddress, srcQty, srcToken.address, taker,
25882592
maxDestAmt, minConversionRate, platformWallet, platformFee, '0x', {from: networkProxy, value: srcQty});
25892593
let tradeEventArgs = nwHelper.getTradeEventArgs(txResult);
2590-
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, platformFee, zeroBN, {});
2594+
await assertFeeHandlerUpdate(tradeEventArgs.ethWeiValue, platformFee, zeroBN, zeroBN, {});
25912595

25922596
// reset fees
25932597
await storage.setFeeAccountedPerReserveType(true, true, true, true, true, true, { from: admin });

0 commit comments

Comments
 (0)