RebateFi Hook

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

Pool initialization fails when ReFi token is currency0 due to incomplete validation

Root + Impact

The _beforeInitialize function only validates that ReFi token is present in currency1 position, completely ignoring currency0, which causes all pool initialization attempts where ReFi is sorted as currency0 to incorrectly revert with ReFiNotInPool(), preventing legitimate pool creation and breaking protocol functionality for specific token pairs.

Description

  • The hook is designed to validate during pool initialization that the ReFi token is one of the two currencies in the pool (either currency0 or currency1), and should allow pool creation as long as ReFi is present in either position.

  • The validation logic contains an error where currency1 is checked twice instead of checking both currency0 and currency1, creating a condition that only passes when ReFi is specifically in the currency1 position, causing valid pools where ReFi is sorted as currency0 to incorrectly revert.

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

This simplifies to if (currency1 != ReFi) revert; which means currency0 is never checked.

Risk

Likelihood:

  • In Uniswap V4, currencies are automatically sorted by address (currency0 < currency1), so the position of ReFi token depends entirely on the paired token's address, which is beyond protocol control.

  • Approximately 50% of potential token pairings will result in ReFi being sorted as currency0 rather than currency1, making this a frequent occurrence.

  • Users attempting to create pools with tokens that have addresses greater than ReFi's address will always encounter this failure.

Impact:

  • Protocol cannot create pools with approximately half of all potential token pairs, severely limiting market making opportunities and liquidity provision.

  • Protocol deployment addresses are deterministic but unpredictable, meaning this could completely block pairing with major liquidity tokens on certain chains.

  • Users experience confusing failures with misleading error messages claiming "ReFi not in pool" when ReFi is actually present.

Proof of Concept

The test demonstrates that when ReFi token ends up in the currency0 position (which happens when the paired token has a higher address), the pool initialization incorrectly reverts even though ReFi is present in the pool. We use a high address value to guarantee that ReFi will be sorted as currency0 by Uniswap V4's automatic sorting mechanism.

function test_VULN_PoolCreationFailsWithReFiInCurrency0() public {
address highAddress = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);
require(highAddress > address(reFiToken), "High address required");
console.log("Demonstrating vulnerability:");
console.log("- ReFi address:", address(reFiToken));
console.log("- Paired address:", highAddress);
console.log("- ReFi will be currency0 (valid configuration)");
// Catch the incorrect revert
vm.expectRevert();
initPool(
reFiCurrency,
Currency.wrap(highAddress),
rebateHook,
LPFeeLibrary.DYNAMIC_FEE_FLAG,
SQRT_PRICE_1_1_s
);
console.log("VULNERABILITY CONFIRMED: Valid pool was rejected");
}

The test passes, confirming the vulnerability: the pool creation reverts with ReFiNotInPool() error even though ReFi is present in the pool as currency0. This proves that the validation logic is fundamentally broken and only checks one of the two currency positions.

Recommended Mitigation

The fix is straightforward: change the first condition to check currency0 instead of checking currency1 twice. This ensures both currency positions are validated.

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

After this fix, the validation will correctly check both positions. This allows pools to be created regardless of whether ReFi ends up as currency0 or currency1 after Uniswap's automatic sorting.

Updates

Lead Judging Commences

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