RebateFi Hook

First Flight #53
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: low
Valid

Sell-fee event is computed 10× too large (uses 1e5 divisor instead of Uniswap v4 pips 1e6)

In RebateFiHook::_beforeSwap we compute the feeAmount with 100000 (1e5) while Uniswap v4 LP fees are expressed in pips where 1000000 (1e6) pip =100%, causing the event to overstates the fee by 10x (e.g., 0.30% is logged as 3.0%)



Description

  • Normal behavior:

    When a user sells ReFi (i.e., ends the swap holding fewer ReFi), the hook emits ReFiSold(seller, amount, fee), where fee should represent the applied LP fee proportionally to the trade amount using pips (1e6 = 100%).


  • Issue:

    The event fee is computed with / 100000, causing a 10× overstatement (0.30% → 3.0%). This does not change pool math (the pool fee override is set correctly via fee | OVERRIDE_FEE_FLAG), but it corrupts on-chain telemetry


// Root cause in code:
function _beforeSwap(
address sender,
PoolKey calldata key,
SwapParams calldata params,
bytes calldata
) internal override returns (bytes4, BeforeSwapDelta, uint24) {
...
if (isReFiBuy) {
...
} else {
fee = sellFee;
//@> uint256 feeAmount = (swapAmount * sellFee) / 100000; // ← uses 1e5; should be 1e6 (pips)
emit ReFiSold(sender, swapAmount, feeAmount);
}
...
}

Risk

Likelihood:

  • Every sell path emits this event, so it is hit under normal trading.


Impact:

  • Off-chain rebate/fee sharing logic based on this event will overcharge or mis-credit by 10×.


Proof of Concept

Add this test to RebateFiHookTest.t.sol. It proves the hook emits the wrong (10×) fee amount in the ReFiSold event.

function test_SellReFi_EmitsFee_TenX_TooHigh() public {
// Arrange
uint24 sellFeePips = 3000; // 0.30%
(, uint24 currentSellFee) = rebateHook.getFeeConfig();
assertEq(currentSellFee, sellFeePips, "precondition");
uint256 reFiAmount = 1 ether;
vm.startPrank(user1);
reFiToken.approve(address(swapRouter), type(uint256).max);
// Prepare swap: ReFi -> ETH (sell ReFi). We set exact-output style amount
// so that hook's `swapAmount` == abs(amountSpecified) == `reFiAmount`.
SwapParams memory params = SwapParams({
zeroForOne: false, // currency1 -> currency0 (ReFi is currency1 in the default setup)
amountSpecified: -int256(reFiAmount), // negative => exact-output mode per v4 convention
sqrtPriceLimitX96: TickMath.MAX_SQRT_PRICE - 1
});
PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({
takeClaims: false,
settleUsingBurn: false
});
// Wrong (as currently emitted): divide by 100_000 → 10× too large
uint256 wrongFee = (reFiAmount * sellFeePips) / 100_000; // 3.0%
// Correct (expected in pips): divide by 1_000_000 → 0.30%
uint256 correctFee = (reFiAmount * sellFeePips) / 1_000_000;
// Show the numbers differ by 10×
assertEq(wrongFee, correctFee * 10, "sanity: wrongFee should be 10x larger than correctFee");
// Expect the event with the WRONG (current) value to demonstrate the bug.
vm.expectEmit(true, false, false, true, address(rebateHook));
emit ReFiSwapRebateHook.ReFiSold(user1, reFiAmount, wrongFee);
// Act
swapRouter.swap(key, params, testSettings, ZERO_BYTES);
vm.stopPrank();
}

Recommended Mitigation

Use pips correctly (divide by 1_000_000) and optionally clarify the comment:

- uint256 feeAmount = (swapAmount * sellFee) / 100000;
+ // LP fee is specified in pips (1_000_000 pips = 100%)
+ uint256 feeAmount = (swapAmount * sellFee) / 1_000_000;
Updates

Lead Judging Commences

chaossr Lead Judge 11 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Incorrect denominator (100000 instead of likely 1000000 or 10000) in fee calculation for ReFiSold event.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!