unstake() function: The unstake function permits the unstaking of multiple position NFTs from the same liquidity pool (LP) by the same lender. This opens the possibility for a lender to claim more Ajna token rewards than they are entitled to by staking and unstaking multiple NFTs associated with the same LP. #454
The absence of a mechanism to prevent a lender from unstaking multiple NFTs for the same liquidity pool (LP) could potentially lead to the exploitation of the Ajna token reward system. A lender can mint, stake, and unstake multiple NFTs for the same LP from the same pool, thereby claiming Ajna token rewards multiple times. This not only undermines the fairness of the reward distribution but can also lead to a significant imbalance in the token economy, undermining the overall stability and integrity of the system.
Proof of Concept
In this scenario, an attacker can exploit the staking and unstaking mechanism to claim more rewards than they deserve. The attacker starts by minting multiple position NFTs for the same LP from the same pool. Afterward, they stake these NFTs and subsequently unstake them, seemingly claiming the same Ajna token rewards multiple times.
Here's a high-level overview of how this attack could be executed:
Minting Multiple NFTs: The attacker calls the mint function multiple times to mint multiple position NFTs for the same pool and LP.
mint({pool: samePool, poolSubsetHash: sameHash, recipient: attackerAddress});
mint({pool: samePool, poolSubsetHash: sameHash, recipient: attackerAddress});
// ... repeat as many times as desired
Staking the NFTs: The attacker then stakes these NFTs one by one using the stake function.
stake(tokenId1);
stake(tokenId2);
// ... repeat for each minted NFT
Unstaking and Claiming Rewards: Finally, the attacker unstakes these NFTs one by one, each time claiming rewards using the unstake function.
unstake(tokenId1); // claims rewards
unstake(tokenId2); // claims rewards again
// ... repeat for each staked NFT
By repeating this process, the attacker can seemingly claim the same Ajna token rewards multiple times, as many times as the number of NFTs they have staked. This exploits the Ajna token reward system, possibly allowing the attacker to claim more rewards than they earned.
This process illustrates that the current implementation does not prevent a user from staking and unstaking multiple NFTs from the same LP, which can lead to an imbalance in the token rewards distribution. Therefore, there is a need for a mechanism to prevent a lender from unstaking multiple NFTs associated with the same LP from the same pool.
The main functions involved in this scenario are as follows:
Introduce a data structure, such as a mapping, to keep track of the staked positions for each lender per pool. This will prevent a lender from unstaking multiple NFTs associated with the same LP from the same pool.
Enhance the mint function to restrict minting more than one NFT for the same lender + pool + LP combination. This will nip the issue in the bud.
Ensure only lenders can mint position NFTs and that they can only be minted if the recipient has existing LP in the Ajna pool. Upon unstaking, remove the flag from the stakedPools mapping.
Review the _claimRewards function for similar vulnerabilities. This function should prevent claiming rewards for multiple staked NFTs of the same LP from the same pool by a single lender.
Lines of code
https://github.com/code-423n4/2023-05-ajna/blob/d80daab705a066828ef1c5d9ba85f315c7c932db/ajna-core/src/RewardsManager.sol#L270-L303 https://github.com/code-423n4/2023-05-ajna/blob/d80daab705a066828ef1c5d9ba85f315c7c932db/ajna-core/src/RewardsManager.sol#L207-L260 https://github.com/code-423n4/2023-05-ajna/blob/d80daab705a066828ef1c5d9ba85f315c7c932db/ajna-core/src/PositionManager.sol#L227-L241
Vulnerability details
Impact
The absence of a mechanism to prevent a lender from unstaking multiple NFTs for the same liquidity pool (LP) could potentially lead to the exploitation of the Ajna token reward system. A lender can mint, stake, and unstake multiple NFTs for the same LP from the same pool, thereby claiming Ajna token rewards multiple times. This not only undermines the fairness of the reward distribution but can also lead to a significant imbalance in the token economy, undermining the overall stability and integrity of the system.
Proof of Concept
In this scenario, an attacker can exploit the staking and unstaking mechanism to claim more rewards than they deserve. The attacker starts by minting multiple position NFTs for the same LP from the same pool. Afterward, they stake these NFTs and subsequently unstake them, seemingly claiming the same Ajna token rewards multiple times.
Here's a high-level overview of how this attack could be executed:
mint({pool: samePool, poolSubsetHash: sameHash, recipient: attackerAddress}); mint({pool: samePool, poolSubsetHash: sameHash, recipient: attackerAddress}); // ... repeat as many times as desired
stake(tokenId1); stake(tokenId2); // ... repeat for each minted NFT
unstake(tokenId1); // claims rewards unstake(tokenId2); // claims rewards again // ... repeat for each staked NFT
By repeating this process, the attacker can seemingly claim the same Ajna token rewards multiple times, as many times as the number of NFTs they have staked. This exploits the Ajna token reward system, possibly allowing the attacker to claim more rewards than they earned.
This process illustrates that the current implementation does not prevent a user from staking and unstaking multiple NFTs from the same LP, which can lead to an imbalance in the token rewards distribution. Therefore, there is a need for a mechanism to prevent a lender from unstaking multiple NFTs associated with the same LP from the same pool.
The main functions involved in this scenario are as follows:
mint(): https://github.com/code-423n4/2023-05-ajna/blob/d80daab705a066828ef1c5d9ba85f315c7c932db/ajna-core/src/PositionManager.sol#L227-L241
stake(): https://github.com/code-423n4/2023-05-ajna/blob/d80daab705a066828ef1c5d9ba85f315c7c932db/ajna-core/src/RewardsManager.sol#L207-L260
unstake(): https://github.com/code-423n4/2023-05-ajna/blob/d80daab705a066828ef1c5d9ba85f315c7c932db/ajna-core/src/RewardsManager.sol#L270-L303
_transferAjnaRewards(): https://github.com/code-423n4/2023-05-ajna/blob/d80daab705a066828ef1c5d9ba85f315c7c932db/ajna-core/src/RewardsManager.sol#L811-L821
Tools Used
VSC.
Recommended Mitigation Steps
Introduce a data structure, such as a mapping, to keep track of the staked positions for each lender per pool. This will prevent a lender from unstaking multiple NFTs associated with the same LP from the same pool.
Enhance the mint function to restrict minting more than one NFT for the same lender + pool + LP combination. This will nip the issue in the bud.
Ensure only lenders can mint position NFTs and that they can only be minted if the recipient has existing LP in the Ajna pool. Upon unstaking, remove the flag from the stakedPools mapping.
Review the _claimRewards function for similar vulnerabilities. This function should prevent claiming rewards for multiple staked NFTs of the same LP from the same pool by a single lender.
Assessed type
Other