RebateFi Hook

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

[H-2] Buy/sell direction logic is inverted in `_isReFiBuy()`, causing users to pay wrong fees

Root + Impact

Description

The _isReFiBuy() function determines whether a swap is buying or selling ReFi tokens based on the swap direction (zeroForOne) and which currency is ReFi. When zeroForOne is true, the user is swapping from currency0 to currency1. If ReFi is currency0, this means the user is selling ReFi, not buying it.

The function incorrectly returns true (indicating a buy) when ReFi is currency0 and zeroForOne is true, which is actually a sell operation. This inverts the entire fee structure.

function _isReFiBuy(PoolKey calldata key, bool zeroForOne) internal view returns (bool) {
bool IsReFiCurrency0 = Currency.unwrap(key.currency0) == ReFi;
if (IsReFiCurrency0) {
@> return zeroForOne; // @audit WRONG: zeroForOne means selling ReFi (token0 -> token1)
} else {
return !zeroForOne;
}
}

Risk

Likelihood:

  • Every swap through a pool where ReFi is currency0 will have inverted fee logic

  • Every swap through a pool where ReFi is currency1 will have inverted fee logic

  • This affects 100% of all swaps in all pool configurations

Impact:

  • Users pay 0% fees when selling ReFi (intended: 0.3% to discourage dumping)

  • Users pay 0.3% fees when buying ReFi (intended: 0% to encourage accumulation)

  • The entire economic incentive model is backwards

  • Protocol fails to discourage sell pressure on ReFi tokens

  • Protocol fails to encourage ReFi token accumulation

  • Revenue model is broken as sells generate no fees

Proof of Concept

a simple solidity test to show how this can be implemented in the real world

function test_InvertedBuySellFees() public {
// Setup: ReFi is currency0, USDC is currency1
// User wants to SELL ReFi for USDC (swap token0 -> token1)
SwapParams memory params = SwapParams({
zeroForOne: true, // Swapping token0 (ReFi) -> token1 (USDC) = SELLING ReFi
amountSpecified: -1e18,
sqrtPriceLimitX96: MIN_PRICE_LIMIT
});
// Call _isReFiBuy - it will incorrectly return TRUE
// This causes buyFee (0%) to be applied instead of sellFee (0.3%)
vm.prank(swapper);
swap(key, params, ZERO_BYTES);
// Check that buyFee was applied (0%) when sellFee should have been applied
(uint24 appliedBuyFee, uint24 appliedSellFee) = hook.getFeeConfig();
// User sold ReFi but paid 0% fee (should have paid 0.3%)
// This proves the buy/sell logic is inverted
}

Recommended Mitigation

A quick and simple mitigation to stop this and use the correct buying/selling logic

function _isReFiBuy(PoolKey calldata key, bool zeroForOne) internal view returns (bool) {
bool IsReFiCurrency0 = Currency.unwrap(key.currency0) == ReFi;
if (IsReFiCurrency0) {
- return zeroForOne;
+ return !zeroForOne; // Buying ReFi = swapping from currency1 to currency0
} else {
- return !zeroForOne;
+ return zeroForOne; // Buying ReFi = swapping from currency0 to currency1
}
}
Updates

Lead Judging Commences

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

Inverted buy/sell logic when ReFi is currency0, leading to incorrect fee application.

Support

FAQs

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

Give us feedback!