RebateFi Hook

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

Critical Logic Bug: _beforeInitialize checks currency1 twice - pools with ReFi as currency0 cannot be initialized

The hook intends to allow any Uniswap V4 pool that contains the ReFi token (either as currency0 or currency1). In _beforeInitialize, this check is implemented incorrectly so that only pools with ReFi as currency1 can ever be initialized; pools where ReFi is configured as currency0 always revert.

Problematic code (RebateFiHook.sol):
https://github.com/CodeHawks-Contests/2025-11-rebatefi-hook/blob/main/src/RebateFiHook.sol#L119-L128

function _beforeInitialize(address, PoolKey calldata key, uint160)
internal
view
override
returns (bytes4)
{
if (Currency.unwrap(key.currency1) != ReFi &&
Currency.unwrap(key.currency1) != ReFi)
{
revert ReFiNotInPool();
}
return BaseHook.beforeInitialize.selector;
}

The condition is effectively:

if (currency1 != ReFi) revert ReFiNotInPool();

currency0 is never checked. This contradicts the comment "Validates that ReFi token is in the pool" and the general description of the hook. The practical consequences:

  • Any pool with currency0 == ReFi and currency1 != ReFi cannot be initialized; _beforeInitialize always reverts with ReFiNotInPool().

  • Only pools with currency1 == ReFi are allowed.

  • Integrators who reasonably expect “ReFi must be in the pair” (but not necessarily on a specific side) will hit a hard, non-obvious configuration DoS.

Because every pool that wants to use this hook must pass through _beforeInitialize, and half of the natural configurations are permanently blocked, this is a High-impact, High-likelihood logic bug.

Minimal PoC (conceptual):

  1. Deploy ReFiSwapRebateHook with ReFi = 0xReFiToken.

  2. Create a PoolKey where:

    • currency0 = Currency.wrap(0xReFiToken)

    • currency1 = Currency.wrap(0xOtherToken)

  3. Call poolManager.initialize(key, sqrtPriceX96) for that key.
    _beforeInitialize reverts with ReFiNotInPool() even though the pool contains ReFi.

  4. Swap the order (put ReFi into currency1) and call initialize again.
    Initialization now succeeds.

Suggested fix:

Update _beforeInitialize to actually require ReFi to be present in either side of the pair:

function _beforeInitialize(address, PoolKey calldata key, uint160)
internal
view
override
returns (bytes4)
{
address c0 = Currency.unwrap(key.currency0);
address c1 = Currency.unwrap(key.currency1);
if (c0 != ReFi && c1 != ReFi) {
revert ReFiNotInPool();
}
return BaseHook.beforeInitialize.selector;
}
Updates

Lead Judging Commences

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

Faulty pool check; only checks currency1 twice, omitting currency0.

Support

FAQs

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

Give us feedback!