Guardian's review of Tokenized Aerodrome Position for Impermax, published August 2025. The report records 11 findings, including 1 high and 2 medium.
- Published
- Review window
- July 30 to August 4, 2025
- Language
- Solidity
- Chains
- Arbitrum, Base, Unichain, Hyperliquid
- Sector
- Lending
- 0 Critical
- 1 High
- 2 Medium
- 6 Low
- 2 Informational
Scope
1 file in scope · 187 nSLOC
| File | nSLOC | Lines |
|---|---|---|
contracts/extensions/TokenizedAeroCLPosition.sol | 187 | 257 |
Findings 11
-
H-01 High ETH Refund Can Revert Critical Functions DoS Resolved
Description
The
increaseLiquidityfunction in theTokenizedAeroCLPositioncontract allows users to add liquidity to an existing position. It does so by withdrawing the user’s position from the gauge, callingincreaseLiquidityon theNonfungiblePositionManagerwith the original and additional amounts, and then redepositing the position.However, the
NonfungiblePositionManager’sincreaseLiquidityfunction ends by callingrefundETH, which sends any residual ETH in the contract back tomsg.sender. In this context,msg.senderis theTokenizedAeroCLPositioncontract itself. Since the contract lacks afallbackfunction, the refund fails and causes the entire transaction to revert.A malicious actor could exploit this by sending a small amount of ETH to the
NonfungiblePositionManagercontract before aincreaseLiquiditycall, ensuring that therefundETHcall fails and reverts the transaction.The same issue exists in the
splitfunction, which also callsminton theNonfungiblePositionManager, triggering a similar refund. This is more critical sincesplitis used in liquidation flows—meaning an attacker could block or delay liquidations by intentionally triggering a refund failure.Recommendation
Consider adding a fallback function to ensure the contract can safely receive ETH refunds and avoid unexpected reverts.
-
M-01 Medium Full Split Causes Revert Logical Error Resolved
Description
The
splitfunction allows users to split their position by a specified percentage, with 100% being the maximum. However, if a user attempts a full (100%) split, thedecreaseAndMintfunction in theNfpmAeroInteractionslibrary burns the originaltokenId, since no liquidity remains in the original position.The issue arises when the
splitfunction subsequently attempts to redeposit the now-burnedtokenIdinto the gauge. This causes a revert, making a full split impossible. Additionally, because the original token is burned without claiming any pending fees from the gauge, those rewards are lost for the user.A similar issue occurs when a user passes in 0% to
split— the function attempts to mint a new position with no liquidity, which results in the new NFT never being minted.Recommendation
Prevent 0% and 100% splits by modifying the logic to require
percentage > 0 && percentage < 1e18, or alternatively, add checks to ensure fees are claimed and that the original token is not redeposited after being burned. -
M-02 Medium getPositionData Will Revert For Certain Prices DoS Acknowledged
Description
The getPositionData function is intended to return price and liquidity information for a given position. It computes values such as
currentPrice,lowestPrice, andhighestPriceusing the current price and a user-defined safety margin. These values are constrained using thesafe160helper to ensure they fit within auint160.However, in certain token pairs where the price can near the maximum limit representable in Uniswap V3, the computed
highestPricecan overflow theuint160range. When this occurs, thesafe160check will revert the transaction, even though the position itself remains valid within Uniswap.This is particularly problematic because
getPositionDatais used in several critical functions, including liquidation checks. A revert in this context could block or delay important protocol operations.Recommendation
Ensure newly added token pairs do not result in price ranges exceeding the uint160 max, as this can disrupt core functions.
-
L-01 Low Unclaimed Fees Are Lost After
mintLogical Error AcknowledgedDescription
When
mintis called, it deposits into the gauge, which triggers a collection of any accrued LP fees. These fees are transferred toTokenizedAeroCLPosition. However, unless the caller explicitly invokesskim, these tokens remain unclaimed and can be taken by anyone. As a result, the user who calledmintmay unintentionally forfeit their accrued LP fees if they do not immediately follow up with askim.Recommendation
Consider calling
skim(msg.sender)at the end of themintfunction. -
L-02 Low Lack Of Slippage Protection MEV Acknowledged
Description
The
increaseLiquidityfunction enables users to add liquidity to their existing position. However, bothamount0Minandamount1Minare hardcoded to zero, meaning no slippage protection is applied during the minting process. This exposes users to unfavorable price movements or MEV attacks.The same issue exists in the
splitfunction, which reduces a position by a specified percentage and mints a new position with the withdrawn liquidity. Here too,amount0Minandamount1Minare set to zero, exposing users to similar risks.Recommendation
Consider adding slippage protection by allowing user-defined amount0Min and amount1Min values or by setting reasonable minimum thresholds to reduce the risk of poor execution or MEV exploitation.
-
L-03 Low
ecrecoverAllows Signature Malleability Signatures AcknowledgedDescription
ImpermaxERC721.soluses vanillaecrecoverfor signer recovery, which is susceptible to malleability due to signature variations.This does not cause any immediate damage since the signed permit itself does not change. However, this should still be considered for improvement.
Recommendation
Consider using OpenZeppelin's ECDSA library for signer recovery to mitigate malleability risks.
-
L-04 Low Unclaimed Dust After
splitWarning AcknowledgedDescription
splitremoves some liquidity from the current LP position and mints a new one. Due to Aero rounding during minting, small residual (dust) token amounts can remain in theTokenizedAeroCLPositioncontract. These residual tokens left behind can be skimmed by anyone, If not reclaimed, the owner of the original position loses these tokens.Recommendation
Considering calling
skimto the position owner aftersplit. -
L-05 Low Rewards Cannot Be Claimed By EOA Unexpected Behavior Acknowledged
Description
In the
claimfunction,_checkAuthorizedCollateralobtains owner of thetokenIdfrom the Collateral contract:address collateral = _requireOwned(tokenId); address owner = IERC721(collateral).ownerOf(tokenId);This assumes that every user holding the wrapper NFT will deposit into the Collateral contract. If the wrapper NFT is still in a EOA wallet or separate contract, then the
ownerOfcall will revert. This blocks anyone from callingclaimwhen the wrapper NFT is not being used as collateral.Recommendation
Consider if this is expected behavior and document this risk for users.
-
L-06 Low Unused Tokens Not Returned Logical Error Acknowledged
Description
The
increaseLiquidityfunction in theTokenizedAeroCLPositioncontract allows users to add liquidity to an existing position. The user must first transfertoken0andtoken1to the contract, then callincreaseLiquidity. The function withdraws the user’s position from the gauge, attempts to increase its liquidity using the transferred amounts, and then redeposits the position.However, if the provided token amounts are unbalanced, the actual liquidity added may use only part of the transferred tokens. Any remaining tokens stay in the contract without being returned to the user. These leftover tokens can later be claimed by anyone through the
skimfunction, resulting in a potential loss of funds for the user.Recommendation
Consider modifying increaseLiquidity to automatically return unused tokens to the user. Alternatively, clearly document this behaviour and ensure users are advised to call skim immediately after increaseLiquidity to recover any remaining tokens.
-
I-01 Informational Naming Convention For
_addGaugeInformational ResolvedDescription
The function
_addGaugeis declaredexternalbut is named with a leading underscore, a convention usually reserved forprivateorinternalfunctions.Recommendation
Rename _
addGaugetoaddGaugeto align with standard Solidity conventions:. -
I-02 Informational Gas Optimization For
nonReentrantModifier Gas Optimization AcknowledgedDescription
The
nonReentrantmodifier inTokenizedAeroCLPosition.solcurrently relies on aboolflag (_notEntered) to prevent reentrancy. However, this approach incurs high gas costs because each call involves a storage write from zero to nonzero (and vice versa).Recommendation
Update the
nonReentrantmodifier to use a uint256 two-state pattern (as recommended by OpenZeppelin), which avoids zero-value writes and reduces gas usage by approximately 10,000–15,000 per call.// storage uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; // in your constructor or initialize: _status = _NOT_ENTERED; // modifier modifier nonReentrant() { require(_status != _ENTERED, "Impermax: REENTERED"); _status = _ENTERED; _; _status = _NOT_ENTERED; }
No findings match.
More from Impermax
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
