Skip to content
This repository has been archived by the owner on Mar 3, 2024. It is now read-only.

lemonmon - Users can potentially get an unlimited amount of reward tokens from LMPVault and from DestinationVaults #362

Closed
sherlock-admin opened this issue Aug 29, 2023 · 0 comments
Labels
Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label High A valid High severity issue Reward A payout will be made for this issue

Comments

@sherlock-admin
Copy link
Contributor

sherlock-admin commented Aug 29, 2023

lemonmon

high

Users can potentially get an unlimited amount of reward tokens from LMPVault and from DestinationVaults

Summary

Whenever a user receives vault tokens of LMPVault or of a DestinationVault (i.e. via minting from depositting assets to the vault or via transferring the vault tokens to another user), they will instantly receive reward tokens which should not happen.

Vulnerability Detail

When LMPVault._afterTokenTransfer() or DestinationVault._afterTokenTransfer() gets called, MainRewarder.stake() is being called (line 351 DestinationVault.sol, line 863 LMPVault.sol), which subsequently calls AbstractRewarder._updateReward() (line 87 MainRewarder.sol) and then AbstractRewarder.earned() (line 134 AbstractRewarder.sol)) to determine the earned rewards for the user and assign the value to rewards[account] (line 135 AbstractRewarder.sol).

Inside the AbstractRewarder.earned() method the new rewards are determined based on the balance of the user (balanceOf(account)). The AbstractRewarder.balanceOf() method returns the balance of the user in the vault (stakeTracker.balanceOf(account), line 156 AbstractRewarder.sol). At this moment the balance of the user in the vault is already the updated balance which includes the new tokens that were minted for the user or transferred to the user, since the call stack starts from LMPVault._afterTokenTransfer() or DestinationVault._afterTokenTransfer(). This means that the user will instantly receive reward tokens from the protocol directly after vault tokens from LMPVault or from a DestinationVault were minted or transferred to them, which should not happen.

AbstractRewarder.earned() returns the currently earned reward as described above and this value gets assigned to rewards[account] (line 135 AbstractRewarder.sol). If AbstractRewarder.earned() would then be called again for example to withdraw the rewards, the return value includes the accounted value from rewards[account] plus the new rewards (balance * (rewardPerToken() - userRewardPerTokenPaid[account])) that were realized meanwhile:

// AbstractRewarder.earned()
205        return (balanceOf(account) * (rewardPerToken() - userRewardPerTokenPaid[account]) / 1e18) + rewards[account];

Example:

  • Alice never received any reward tokens from LMPVault or from a DestinationVault.
  • Alice deposits into LMPVault the first time, immediately receiving reward tokens. Because AbstractRewarder.earned() gets called subsequently from LMPVault._afterTokenTransfer(), and there userRewardPerTokenPaid[account] will be equal to 0, since it is the first time that Alice deposits into LMPVault. This means that Alice instantly receives the full amount of reward tokens calculated from AbstractRewarder.earned() based on her balance of vault tokens AFTER she depositted.
  • Alice transfers her LMPVault tokens to Bob, which triggers LMPVault._afterTokenTransfer() for Bob so that Bob will instantly receive reward tokens based on their new balance AFTER Alice sent them their LMPVault tokens. Bob also never received reward tokens before so that they now also receive a lot of reward tokens from their LMPVault tokens that were transferred to them.
  • This can be repeated by transferring the vault tokens to other wallets so that reward tokens are being generated again and again to create a potentially unlimited amount of reward tokens, by abusing this issue.

In the same way as described above, a potentially unlimited amount of reward tokens from the DesinationVaults can be gotten, since the destination vaults suffer from the same issue.

Impact

By abusing this issue a user can potentially receive an unlimited amount of reward tokens from the LMPVault and from the DestinationVaults.

Code Snippet

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/LMPVault.sol#L854-L865

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/vault/DestinationVault.sol#L345-L353

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/rewarders/MainRewarder.sol#L86-L93

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/rewarders/AbstractRewarder.sol#L128-L140

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/rewarders/AbstractRewarder.sol#L204-L206

https://github.com/sherlock-audit/2023-06-tokemak/blob/main/v2-core-audit-2023-07-14/src/rewarders/AbstractRewarder.sol#L155-L157

Tool used

Manual Review

Recommendation

Consider calling MainRewarder.getReward(to, true) inside LMPVault._beforeTokenTransfer() and DestinationVault._beforeTokenTransfer() in order to make sure that userRewardPerTokenPaid[account] (line 136 AbstractRewarder.sol) gets updated so that the user will not receive rewards for the new vault tokens that are being minted or transferred to them.

Duplicate of #603

@github-actions github-actions bot added High A valid High severity issue Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label labels Sep 11, 2023
@sherlock-admin2 sherlock-admin2 changed the title Perfect Dijon Leopard - Users can potentially get an unlimited amount of reward tokens from LMPVault and from DestinationVaults lemonmon - Users can potentially get an unlimited amount of reward tokens from LMPVault and from DestinationVaults Oct 3, 2023
@sherlock-admin2 sherlock-admin2 added the Reward A payout will be made for this issue label Oct 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate A valid issue that is a duplicate of an issue with `Has Duplicates` label High A valid High severity issue Reward A payout will be made for this issue
Projects
None yet
Development

No branches or pull requests

2 participants