The UpliftOnlyExample contract is designed to manage liquidity positions in a pool. When users deposit assets into the pool, they receive LP tokens represented by NFTs (LPNFT). Upon withdrawal, users are charged a fee based on the uplift, which is the profit they've made since their initial deposit. The fee mechanism is intended to ensure that users pay fees proportional to their gains, benefiting the protocol and discouraging fee avoidance.
When an NFT representing a liquidity position is transferred from one user (_from) to another (_to), certain updates occur in the afterUpdate function.
The afterUpdate function is supposed to handle any necessary state changes when an NFT ownership changes.
The contract updates feeDataArray[tokenIdIndex].lpTokenDepositValue to lpTokenDepositValueNow, which is the current value of the LP token.
This action effectively resets the recorded deposit value to the current value at the time of transfer.
The uplift fee is calculated based on the difference between the current LP token value at withdrawal and the lpTokenDepositValue.
By resetting this value during transfer, the contract allows the new owner to avoid paying fees on any uplift that occurred before the transfer.
Example:
User A deposits assets into the pool.
Let's assume the LP token's value at deposit (lpTokenDepositValue) is $100.
Over time, the pool performs well, and the LP token's value increases to $150.
This represents a 50% uplift from the initial deposit.
User A wants to withdraw their liquidity.
Normally, they would be charged a fee based on the 50% uplift, according to the contract's fee structure.
To avoid paying the uplift fee, User A transfers the NFT representing their liquidity position to User B (who could be another address controlled by User A).
During the transfer, the afterUpdate function resets lpTokenDepositValue to the current LP token value of $150.
For User B, the recorded lpTokenDepositValue is now $150.
The prior uplift is effectively erased in terms of fee calculation.
User B withdraws the liquidity.
The uplift is calculated as (Current Value - lpTokenDepositValue) / lpTokenDepositValue.
Substituting the values: (150 - 150) / 150 = 0% uplift.
As a result, the uplift fee is zero or significantly reduced.
User A, by transferring the NFT to User B before withdrawal, successfully avoids paying the intended uplift fees.
This undermines the fee mechanism, allowing users to exploit the contract and depriving the protocol of expected fee revenue. Users might repeatedly transfer NFTs between addresses they control to continually reset lpTokenDepositValue.
Manual Review
Prevent the contract from updating lpTokenDepositValue during NFT transfers. Retain the original deposit data in the FeeData structure.
Likelihood: High, any transfer will trigger the bug. Impact: High, will update lpTokenDepositValue to the new current value without taking fees on profit.
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.