RebateFi Hook

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

`RefiSold` event has 2 problems

Root + Impact

Description

  • The ReFiSold event should correctly log the amount of ReFi tokens being sold and the actual fee charged by the pool

  • 2 problems. First SwapEvent represent the wrong token and secondly the fee calculation is incorrect

function _beforeSwap(
address sender,
PoolKey calldata key,
SwapParams calldata params,
bytes calldata
) internal override returns (bytes4, BeforeSwapDelta, uint24) {
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;
@> uint256 feeAmount = (swapAmount * sellFee) / 100000; // Wrong divisor
emit ReFiSold(sender, swapAmount, feeAmount);
}
return (
BaseHook.beforeSwap.selector,
BeforeSwapDeltaLibrary.ZERO_DELTA, re
fee | LPFeeLibrary.OVERRIDE_FEE_FLAG
);
}

Risk

Likelihood:

  • Every sell swap event reports fees 10x higher than actually charged

  • Offchain systems receive incorrect data

Impact:

  • Trading bots and aggregators misinterpret data

  • Wrong trading data shown

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {Test} from "forge-std/Test.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {Currency} from "v4-core/types/Currency.sol";
import {SwapParams} from "v4-core/types/PoolOperation.sol";
import {ReFiSwapRebateHook} from "../src/RebateFiHook.sol";
contract EventMisreportingTest is Test {
ReFiSwapRebateHook hook;
PoolKey poolKey;
address refiToken = address(0x123);
address usdcToken = address(0x456);
event ReFiSold(address indexed seller, uint256 amount, uint256 fee);
function setUp() public {
IPoolManager poolManager = IPoolManager(address(0x789));
hook = new ReFiSwapRebateHook(poolManager, refiToken);
poolKey = PoolKey({
currency0: Currency.wrap(usdcToken),
currency1: Currency.wrap(refiToken),
fee: 0x800000,
tickSpacing: 60,
hooks: hook
});
}
function testExactOutputSwapLogsWrongToken() public {
SwapParams memory params = SwapParams({
zeroForOne: false,
amountSpecified: 1000e6, // Positive = exact output
sqrtPriceLimitX96: 0
});
vm.expectEmit(true, false, false, false);
emit ReFiSold(address(this), 1000e6, 0); // Wrong token amount logged
hook.beforeSwap(address(this), poolKey, params, "");
}
function testFeeCalculation10xOverreported() public {
// User sells exactly 10000 ReFi
SwapParams memory params = SwapParams({
zeroForOne: false,
amountSpecified: -10_000e18, // Exact input
sqrtPriceLimitX96: 0
});
(, , uint24 actualFee) = hook.beforeSwap(address(this), poolKey, params, "");
uint256 actualFeeAmount = (10_000e18 * 3000) / 1_000_000;
assertEq(actualFeeAmount, 30e18);
uint256 reportedFeeAmount = (10_000e18 * 3000) / 100_000;
assertEq(reportedFeeAmount, 300e18);
assertEq(reportedFeeAmount, actualFeeAmount * 10);
}
}

Recommended Mitigation

function _beforeSwap(
address sender,
PoolKey calldata key,
SwapParams calldata params,
bytes calldata
) internal override returns (bytes4, BeforeSwapDelta, uint24) {
bool isReFiBuy = _isReFiBuy(key, params.zeroForOne);
- uint256 swapAmount = params.amountSpecified < 0
- ? uint256(-params.amountSpecified)
- : uint256(params.amountSpecified);
uint24 fee;
if (isReFiBuy) {
fee = buyFee;
+ // For events, use absolute value - actual amount determined by pool
+ uint256 amount = params.amountSpecified < 0
+ ? uint256(-params.amountSpecified)
+ : uint256(params.amountSpecified);
+ emit ReFiBought(sender, amount);
- emit ReFiBought(sender, swapAmount);
} else {
fee = sellFee;
+ // Calculate fee using Uniswap's divisor (1,000,000)
+ uint256 amount = params.amountSpecified < 0
+ ? uint256(-params.amountSpecified)
+ : uint256(params.amountSpecified);
+ uint256 feeAmount = (amount * sellFee) / 1_000_000;
- uint256 feeAmount = (swapAmount * sellFee) / 100000;
+ emit ReFiSold(sender, amount, feeAmount);
- emit ReFiSold(sender, swapAmount, feeAmount);
}
}
Updates

Lead Judging Commences

chaossr Lead Judge 12 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!