Closed code423n4 closed 1 year ago
Indeed when logging console.log(position.lps); console.log(position.depositTime);
values aren't 0 but this is the expected behavior as values have been copied in memory before the deletion of the storage slots.
The memory variables are scoped and will be cleared anyway, and the storage variables are properly deleted here.
Picodes marked the issue as unsatisfactory: Invalid
Lines of code
https://github.com/code-423n4/2023-05-ajna/blob/fc70fb9d05b13aee2b44be2cb652478535a90edd/ajna-core/src/PositionManager.sol#L367
Vulnerability details
Impact
This bug could lead to a situation where a user can 'redeem' their positions without the associated liquidity positions (LPs) being properly reset. This could result in the user being able to artificially inflate their LP balance, which could lead to financial loss for other system users.
Proof of Concept
The 'redeemPositions' function in the 'PositionManager' contract allows users to remove their liquidity positions (LPs) from the contract and return them to the NFT owner.
Here is the relevant section of the code in question:
https://github.com/code-423n4/2023-05-ajna/blob/fc70fb9d05b13aee2b44be2cb652478535a90edd/ajna-core/src/PositionManager.sol#L352-L393
Let's have a breakdown what's happening on each line.
The function first retrieves the pool and creates a memory array
lpAmounts
to hold the liquidity token amounts of the positions that are being redeemed.Next, the function enters a for loop that iterates over the index values provided in
params_.indexes
.In each iteration, it does the following:
Position
structure related to the provided token ID and the current index. ThePosition
structure contains details about the liquidity position, including the deposit time and the number of liquidity tokens (lps
) held.positionIndex
, which is a set of position indexes associated with the provided token ID. If this removal fails, the function reverts with the message "RemovePositionFailed".lpAmounts
array.Position
structure associated with the current index and the provided token ID from thepositions
mapping.The problem here is in step 1 and 6. It retrieves the position in memory
and then attempts to delete it
If we add some logs
After adding some console.logs,
https://github.com/code-423n4/2023-05-ajna/blob/fc70fb9d05b13aee2b44be2cb652478535a90edd/ajna-core/src/PositionManager.sol#L387-L391
Running the
testNFTTransferByApprove
we see that the console log output shows 'position.lps' and 'position.depositTime' values remain unchanged even after the 'delete' operation. This means that the LP balance associated with the position is not being properly reset upon redemption.However, when the
position
variable is changed tomemory
,Conversely, when the 'position' variable is declared as a storage variable, the deletion operation correctly resets the 'position.lps' and 'position.depositTime' values.
This issue could be exploited by a malicious user by redeeming their positions. Still, since the LPs for that position are not reset, the user could potentially call
memorializePosition
again with the same tokenId and index, effectively getting free LPs.https://github.com/code-423n4/2023-05-ajna/blob/fc70fb9d05b13aee2b44be2cb652478535a90edd/ajna-core/src/PositionManager.sol#L210-L212
Side note: It could also be exploitable when moving liquidity.
Consider the following scenario:
tokenId: 5
andpositionIndex: 1
memorializePositions
, the old lps is incremented with the current lp balanceTools Used
Manual Review
https://docs.soliditylang.org/en/latest/types.html#delete
Recommended Mitigation Steps
To fix this issue, change the declaration of the 'position' variable from memory to storage. This will ensure that the deletion operation correctly resets the LP balance associated with the position:
Assessed type
Other