RebateFi Hook

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

Pool lock by too large fee

The owner can manually set the fee > 100%, which will result in the pool lock.

Description

  • The owner can manually set the fee > 100%, and because of this, any swap will cause LPFeeTooLarge revert, which will result in the pool being lock.

// Root cause in the codebase with @> marks to highlight the relevant section
function ChangeFee(bool _isBuyFee, uint24 _buyFee, bool _isSellFee, uint24 _sellFee) external onlyOwner {
@> if (_isBuyFee) buyFee = _buyFee;
@> if (_isSellFee) sellFee = _sellFee;
}

Risk

Likelihood:

  • This will occur when the owner accidentally or intentionally set the fee > 100%.

Impact:

  • the pool is completely locked.

Proof of Concept

1) Set extreme sell fee > 100%;
2) Make swap. Now any swap must break the pool with a LPFeeTooLarge revert;
3) If the pool was locked, you will see a message "As expected, the pool is locked due to too large fee > 100%".

function test_PoC_PoolBlockedByTooLargeFee() public {
// set extreme sell fee > 100%
uint24 extremeSellFee = 1_000_001; // >100%
rebateHook.ChangeFee(false, 0, true, extremeSellFee);
// Check it was updated
(, uint24 currentSellFee) = rebateHook.getFeeConfig();
assertEq(currentSellFee, extremeSellFee, "Sell fee should be updated to extreme value");
uint256 reFiAmount = 0.001 ether;
vm.startPrank(user1);
reFiToken.approve(address(swapRouter), type(uint256).max);
SwapParams memory params = SwapParams({
zeroForOne: true, amountSpecified: -int256(reFiAmount), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
});
PoolSwapTest.TestSettings memory testSettings =
PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false});
// Now any swap must break the pool with a LPFeeTooLarge revert.
vm.expectRevert(abi.encodeWithSelector(LPFeeLibrary.LPFeeTooLarge.selector, extremeSellFee));
swapRouter.swap(key, params, testSettings, ZERO_BYTES); // It should throw a LPFeeTooLarge revert
vm.stopPrank();
console.log("As expected, the pool is locked due to too large fee > 100%");
}

Recommended Mitigation

Limit the hook to the safest possible fee range, for example ≤ 50% depending on the tokenomics, and check the limit inside ReFiSwapRebateHook::ChangeFee().

+uint24 constant MAX_FEE = 500_000; // 50%
function ChangeFee(bool _isBuyFee, uint24 _buyFee, bool _isSellFee, uint24 _sellFee) external onlyOwner {
- if (_isBuyFee) buyFee = _buyFee;
+ if (_isBuyFee) {
+ require(_buyFee <= MAX_FEE, "BuyFeeTooHigh");
+ buyFee = _buyFee;
+ }
- if (_isSellFee) sellFee = _sellFee;
+ if (_isSellFee) {
+ require(_sellFee <= MAX_FEE, "SellFeeTooHigh");
+ sellFee = _sellFee;
+ }
}
Updates

Lead Judging Commences

chaossr Lead Judge 12 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!