A potential vulnerability has been identified in the original UpliftOnlyExample contract, related to high gas consumption and the possibility of a partial DoS when multiple deposits and NFT transfers are performed. Specifically, the logic of storing deposits in an array and manually reordering it during each transfer can trigger costly iterations, increasing the likelihood of gas-related reverts (especially in congested scenarios or with high block.gaslimit).
Location and Behavior
The vulnerability primarily appears in the onAfterRemoveLiquidity and afterUpdate functions, where the contract manually reorders a user’s FeeData[] array.
onAfterRemoveLiquidity iterates backward over the array to update and remove deposits, applying fees and computing amounts. This reverse loop can trigger substantial gas usage if many deposits exist.
afterUpdate (NFT transfers) searches for the tokenID in the array, then shifts elements in storage to maintain FILO order, causing multiple writes when the array holds numerous entries.
More generally, each deposit is “pushed” to the array on deposit, while withdrawals and transfers require multiple storage touches (deletion, rewriting, copying) that become expensive as the array grows (up to 100 entries per user).
Potential Partial DoS
In a high gas cost environment or with a low block.gaslimit, these loops might exceed the available gas, causing the transaction to revert.
This situation blocks withdrawals or transfers for users holding many deposits, resulting in a partial DoS: user funds remain secure, but certain operations are effectively inaccessible during periods of elevated gas usage.
The vulnerability impacts the usability and efficiency of the contract:
Partial Denial of Service:
Users with a large number of deposits (close to 100) may experience failed transfers or withdrawals during periods of high gas prices or network congestion.
Excessive Gas Costs:
Regular operations (e.g., NFT transfers) become prohibitively expensive, discouraging participation and reducing the system's scalability.
Risk of DoS Exploitation:
A malicious actor could intentionally bloat arrays to degrade system performance, affecting other users indirectly.
Add the following test to pkg/pool-hooks/test/foundry/UpliftExample.t.sol:
Test Explanation
We run testExcessiveGasWhenTransferringAfter100Deposits() (shown below) to create 100 deposits for Bob, then transfer the NFT #100 to Alice. The test logs reveal significant gas usage for both:
Creating 100 deposits.
Transferring the NFT and triggering array reordering.
Logs
The logs show Gas used for 100 deposits: 26331593 and Gas used for NFT transfer & array reorder: 210236.
This high gas usage affirms the vulnerability, as the array reordering calls multiple storage writes that can revert transactions under congested conditions.
Foundry: Used to develop and run tests to validate the vulnerability.
Manual Code Review: Confirms the array reordering logic exists in the contract and triggers repeated storage writes.
Use a Mapping Instead of Arrays
Map deposit IDs to structs (e.g., mapping(uint256 => FeeData)) and store only references to relevant data. This design avoids shifting array elements on transfer or removal.
Lower the Maximum Deposit Count
Reduce the hard limit from 100 to a smaller number (e.g., 30) to limit the iteration and partially mitigate gas usage if array logic is retained.
Aggregate Deposits
Consolidate multiple deposits into a single record when feasible. This drastically reduces the overall deposit count per user.
Off-Chain Indexing
Outsource complex ordering or deposit history to an off-chain index (like The Graph), thereby removing heavy on-chain loops.
User Warnings
Document or notify users that high deposit counts may yield significant gas costs and potential transaction failures in congested conditions.
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.