The UpliftOnlyExample
contract's afterUpdate
function, which is triggered after an LPNFT transfer, assumes that a corresponding FeeData
entry will always exist for the transferred NFT's tokenID
within the recipient's feeDataArray
for the relevant pool. However, this assumption can be violated if there's an error in the NFT transfer logic or if the system can reach an unexpected state where an NFT exists without associated fee data. If the FeeData
entry is not found, the function does not revert, leading to an inconsistent state where a valid NFT holder might not have the necessary data for proper fee calculation upon withdrawal. This can result in incorrect fee calculations, potential denial of service, or the possibility of exploitation in conjunction with other vulnerabilities. The root cause is likely an error in the NFT transfer or FeeData
management logic.
The vulnerability arises in the afterUpdate
function of the UpliftOnlyExample
contract which is called after an LPNFT transfer is completed via the overridden _update
function in the LPNFT
contract LPNFT.sol#L49-L56
. The core issue is an assumption that a corresponding FeeData
entry will always exist for a given NFT's tokenID
within the recipient's feeDataArray
after a transfer.
The afterUpdate
function iterates through the sender's feeDataArray
(poolsFeeData[poolAddress][msg.sender]
) to locate the FeeData
struct associated with the transferred NFT's tokenID
(UpliftOnlyExample.sol#L597-L603
):
It expects to find a match, updates the entry's fields, and then copies this entry to the recipient's feeDataArray
(poolsFeeData[poolAddress][_to]
). Finally, it removes the entry from the sender's array and reorders the array to maintain FILO order (UpliftOnlyExample.sol#L605-L627
):
However, if the tokenID
is not found in the sender's feeDataArray
(i.e., tokenIdIndexFound
remains false
after the loop), the function does not revert. This implies that the system can enter a state where a user holds a valid NFT (transferred to them) but does not have the corresponding FeeData
entry in their poolsFeeData
mapping for the relevant pool.
This situation can occur if there is a bug in the NFT transfer logic that fails to correctly manage the FeeData
entries, or if there is another way to manipulate the system state such that an NFT can exist without its associated FeeData
being properly created or transferred.
The absence of a FeeData
entry for a valid NFT holder can lead to incorrect fee calculations during withdrawals, potentially causing a denial of service if the withdrawal process relies on this data or a miscalculation of fees owed. The code in LPNFT._update
relies on UpliftOnlyExample.afterUpdate
for updating the fee data. Thus, any issues here directly affect the LPNFT
contract's operation.
The absence of a corresponding FeeData
entry for a valid NFT holder leads to the following impacts:
Data Inconsistency: The primary impact is an inconsistent state within the UpliftOnlyExample
contract's accounting. The poolsFeeData
mapping is intended to accurately reflect the deposit history and fee-related information for each user and pool. When an NFT is transferred, the associated FeeData
should also be transferred. If this fails, the mapping no longer accurately represents the on-chain reality.
Incorrect Fee Calculation: The FeeData
entry contains critical information for calculating withdrawal fees, including the initial deposit value, deposit timestamp, and applicable uplift fee basis points. If this data is missing, the onAfterRemoveLiquidity
function in which relies on it for fee calculations, will not be able to calculate the correct fee.
Potential for Exploitation: While a direct exploit might not be immediately obvious, an inconsistent state can create opportunities for malicious actors to gain an unfair advantage. For example, if an attacker can manipulate the system to transfer an NFT without transferring the FeeData
, they might be able to avoid paying fees on withdrawal or manipulate the fee calculation in their favor. The exact exploit would depend on the specific way the missing FeeData
is handled (or not handled) by the withdrawal logic.
Reputational Damage: Incorrect fee calculations and failed withdrawals can damage the protocol's reputation and erode user trust.
Given these potential consequences, the severity of this vulnerability is best classified as Medium. It does not lead to an immediate and direct loss of funds in all cases, but it creates a serious data inconsistency and opens the door for potential issues and exploits.
Manual Review
The provided code revision, which adds a require
statement to revert if tokenIdIndexFound
is false
in UpliftOnlyExample.afterUpdate
, effectively addresses the immediate vulnerability. By ensuring that a FeeData
entry must exist for the transferred NFT, the function prevents the system from entering an inconsistent state:
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.