RebateFi Hook

First Flight #53
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Impact: low
Likelihood: medium
Invalid

Integer division in sell fee calculation causes precision loss and zero revenue on small swaps

The contract applies a dynamic sell fee using the formula (swapAmount * sellFee) / 100_000. Solidity performs integer division, which truncates downward. When the result is less than 1 full token, feeAmount becomes 0, even though the user is still charged the full percentage via the dynamic fee override. This causes systematic revenue leakage for the hook on small swaps

bool isReFiBuy = _isReFiBuy(key, params.zeroForOne);

uint256 swapAmount = params.amountSpecified < 0
? uint256(-params.amountSpecified)
: uint256(params.amountSpecified);
uint24 fee;

if (isReFiBuy) {
fee = buyFee;
emit ReFiBought(sender, swapAmount);
} else {
fee = sellFee;
// @> BUG: Integer division rounds down → dust is lost forever
uint256 feeAmount = (swapAmount * sellFee) / 100000; // @> truncation here
emit ReFiSold(sender, swapAmount, feeAmount);
}

https://github.com/CodeHawks-Contests/2025-11-rebatefi-hook/blob/add4b298d1246ad2f1df726216849c1c31f83065/src/RebateFiHook.sol#L166

Risk

Likelihood:

  • Occurs on every sell swap below the threshold of ~33.33 tokens (at default 3% fee)

  • Small swaps are common in production (especially from wallets, bots, or aggregators)

Impact:

  • Hook earns zero revenue on a significant portion of sell volume

  • ReFiSold event emits misleading/incorrect fee amounts (reports 0 when user actually paid a fee)

Proof of Concept

// Default sellFee = 3000 (3%)
// User sells 10 tokens (exact input)
uint256 swapAmount = 10e18;
uint256 feeAmount = (10e18 * 3000) / 100_000;
30_000_000_000_000_000_000 / 100_000 = 300_000_000_000_000_000 (0.3 tokens)
→ Solidity truncates → feeAmount = 0
// User pays 3% via dynamic fee, but hook collects nothing
// Event emits: ReFiSold(user, 10e18, 0) → lies about actual fee paid

Recommended Mitigation

- uint256 feeAmount = (swapAmount * sellFee) / 100000;
+ // Round up using standard OpenZeppelin/Uni-safe pattern
+ uint256 feeAmount = (swapAmount * sellFee + 99_999) / 100_000;
+ import {FullMath} from "v4-core/libraries/FullMath.sol";
+ uint256 feeAmount = FullMath.mulDiv(swapAmount, sellFee, 100_000, Rounding.Up);
Updates

Lead Judging Commences

chaossr Lead Judge
9 days ago
chaossr Lead Judge 8 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

comfortnurse021 Submitter
8 days ago
chaossr Lead Judge
8 days ago
chaossr Lead Judge 4 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!