RebateFi Hook

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

Events lack pool/pair context

Description

  • Normally, a trading hook that can be attached to multiple pools should emit events that uniquely identify which pool / pair a trade belongs to (for example via PoolId or token addresses). This enables accurate per-pool analytics, rewards, and monitoring.

  • In this implementation, the events only include the buyer/seller, amount, and fee, with no pool or token context. When the same hook is attached to multiple ReFi pools, event consumers cannot distinguish which specific pool generated a trade.

/* CUSTOM EVENTS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
@> event ReFiBought(address indexed buyer, uint256 amount);
@> event ReFiSold(address indexed seller, uint256 amount, uint256 fee);

Risk

Likelihood:

  • When the same hook instance is attached to several pools involving ReFi (e.g., ReFi/WETH, ReFi/USDC, ReFi/USDT), each trade emits identical-looking events from the pool’s perspective.

  • When off-chain systems (indexers, dashboards, reward engines) subscribe only to these hook events without correlating with PoolManager logs, they always see a pool-agnostic stream of ReFi trades.

Impact:

  • Analytics and dashboards cannot correctly attribute ReFi volume, liquidity, or fees per specific pool, which can mislead protocol decisions and external users.

  • Any future per-pool logic (e.g., higher rebates on ReFi/WETH, different incentives by pair) becomes brittle or impossible using these events alone, potentially forcing a protocol migration to a new event format.

Proof of Concept

// Assume the same hook is attached to two pools:
// 1) ReFi / WETH
// 2) ReFi / USDC
// Off-chain consumer that listens only to hook events:
function onReFiTradeEvent(address trader, uint256 amount, uint256 fee) external {
// The system wants per-pair stats, but from the event:
// - No poolId
// - No token0 / token1 addresses
//
// There is no way to know if this trade came from:
// - ReFi/WETH, or
// - ReFi/USDC, or some other ReFi pair.
//
// Result: all trades must be lumped together as generic "ReFi volume".
}

In practice, any consumer relying purely on ReFiBought / ReFiSold cannot know which pool produced the event.

Recommended Mitigation

Add pool/pair context to the events and emit using key.currency0 / key.currency1 (or PoolId).

- event ReFiBought(address indexed buyer, uint256 amount);
- event ReFiSold(address indexed seller, uint256 amount, uint256 fee);
+ event ReFiBought(
+ address indexed buyer,
+ address indexed token0,
+ address indexed token1,
+ uint256 amount,
+ uint256 feeBips
+ );
+
+ event ReFiSold(
+ address indexed seller,
+ address indexed token0,
+ address indexed token1,
+ uint256 amount,
+ uint256 feeAmount
+ );
function _beforeSwap(
address sender,
PoolKey calldata key,
SwapParams calldata params,
bytes calldata /* data */
)
internal
override
returns (bytes4, BeforeSwapDelta, uint24)
{
...
- if (isReFiBuy) {
- fee = buyFee;
- emit ReFiBought(sender, swapAmount);
- } else {
- fee = sellFee;
- uint256 feeAmount = (swapAmount * sellFee) / 100000;
- emit ReFiSold(sender, swapAmount, feeAmount);
- }
+ address token0 = Currency.unwrap(key.currency0);
+ address token1 = Currency.unwrap(key.currency1);
+
+ if (isReFiBuy) {
+ fee = buyFee;
+ emit ReFiBought(sender, token0, token1, swapAmount, buyFee);
+ } else {
+ fee = sellFee;
+ uint256 feeAmount = (swapAmount * sellFee) / 100000;
+ emit ReFiSold(sender, token0, token1, swapAmount, feeAmount);
+ }
...
}
Updates

Lead Judging Commences

chaossr Lead Judge 11 days ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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

Give us feedback!