RebateFi Hook

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

_beforeInitialize only allows ReFi as currency1, preventing valid pools where ReFi is currency0

_beforeInitialize incorrectly validates only currency1 twice instead of checking both currency0 and currency1, preventing legitimate pool creation when ReFi is currency0

Description

  • The _beforeInitialize hook validates that the ReFi token is present in the pool by checking that at least one of the two currencies (currency0 or currency1) matches the ReFi token address. This ensures the hook only operates on pools containing the ReFi token.

  • The current implementation contains a copy-paste error where currency1 is checked twice instead of checking both currency0 and currency1. This causes the validation to reject any pool where ReFi is currency0, even though such pools are valid. Since Uniswap v4 orders currencies by address (lower address becomes currency0), users cannot create ReFi pools if the ReFi token has a lower address than the paired token.

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;
}

Risk

Likelihood:

  • Users attempt to create a ReFi pool with any token that has a higher address than ReFi, causing ReFi to become currency0 due to Uniswap v4's address-based ordering. Pool initialization reverts with ReFiNotInPool() despite ReFi being present in the pool, blocking legitimate pool creation.

  • The issue affects approximately 50% of potential token pairs (those where the paired token has a higher address than ReFi).

Impact:

  • Legitimate ReFi pools cannot be initialized when ReFi has a lower address than the paired token, severely limiting protocol functionality. Users are forced to only pair ReFi with tokens that have lower addresses, drastically reducing the number of viable trading pairs.

  • Protocol deployment becomes unusable if major tokens like USDC or WETH have higher addresses than ReFi, requiring contract redeployment

Proof of Concept

This test creates a pool where ReFi has a lower address than the paired token (using type(uint160).max), forcing ReFi into the currency0 position after Uniswap's address-based sorting. The hook's beforeInitialize validation incorrectly checks currency1 twice instead of checking both currencies, causing it to revert with ReFiNotInPool() despite ReFi being present as currency0.

function test_BeforeInitialize_RevertsWhenReFiIsCurrency0() public {
Currency highCurrency = Currency.wrap(address(type(uint160).max));
PoolKey memory testKey = PoolKey({
currency0: reFiCurrency,
currency1: highCurrency,
fee: LPFeeLibrary.DYNAMIC_FEE_FLAG,
tickSpacing: 60,
hooks: rebateHook
});
// Prank as PoolManager to bypass the poolManagerOnly modifier
vm.prank(address(manager));
vm.expectRevert(ReFiSwapRebateHook.ReFiNotInPool.selector);
rebateHook.beforeInitialize(address(this), testKey, SQRT_PRICE_1_1);
}

Recommended Mitigation

Fix the copy-paste error in the validation logic to check both currency0 and currency1 instead of checking currency1 twice. The condition should verify that ReFi is present in at least one of the two currency positions. By correcting currency1 to currency0 in the second check, the validation properly ensures the ReFi token exists in the pool regardless of its position after Uniswap's address-based sorting.

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