Submitted by merlinboii, also found by 0xhacksmithh, dhank, Utsav, Stormreckson, rouhsamad, sandy, AvantGard, araj, SovaSlava, Holipot, DPS, Audinarey, Haipls, Varun_05, shaflow2, and Limbooo
https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L311-L398 https://github.com/code-423n4/2024-05-munchables/blob/main/src/managers/LockManager.sol#L401-L427
The remaining locked tokens continue to accumulate after the player unlocks their tokens, whether partially or fully.
As a result, the player benefits by retrieving more NFTs when they lock their tokens again during the lock drop period or in the next lock drop period.
Location: LockManager::_lock()L344 - L380
Location: LockManager::unlock()L401 - L427
During the lock drop period or in the subsequent lock drop period, the player retrieves more NFTs in proportion to the quantity locked. The remaining tokens from previous locks enhance the new locking quantity when relocked until the end of the lock period.
Moreover, it probably assumes that the lockDuration can either be equal or greater than the lock drop duration (lockDuration >= lockdrop.start - lockdrop.end), allowing players to reenter by each lock drop, or significantly less than the lock drop duration (lockDuration < lockdrop.start - lockdrop.end), enabling players to reenter during the same lockdrop.
Therefore, the likelihood of the issue is based on how offset players can reenter and take advantage of the remainders.
The following core functions take an effect of this issue:
as uint256 quantity = _quantity + lockedToken.remainder :: L344
The state updates as follows:
Updates to lockedToken struct and LockManager contract balance:
This shows that locking of 10e18 tokens can retrieve 3 NFTs.
After the lockDuration has passed, the player can unlock the locked tokens.
as uint256 quantity = _quantity + lockedToken.remainder (prev remainder#1);
Updates to lockedToken struct and LockManager contract balance:
In total, the player retrieves 5 NFTs (3 from the first locking + 2 from the second locking) by locking 10e18 tokens, benefiting from the remainder of the previous locking.
as uint256 quantity = _quantity + lockedToken.remainder :: L344
The state updates as follows:
Updates to lockedToken struct and LockManager contract balance:
This shows that locking of 13e18 tokens can retrieve 4 NFTs.
After the lockDuration has passed, the player can unlock the locked tokens.
as uint256 quantity = _quantity + lockedToken.remainder (prev remainder#1);
Updates to lockedToken and LockManager contract balance:
In total, the player retrieves 4 new NFTs from locking 11e18 tokens. Compared to the first locking of 13e18 tokens, the second lock yields similar power to gain 4 NFTs, benefiting from the remainder of the previous locking round.
Manage the remaining tokens when the player unlocks locked tokens. Alternatively, use other methods to handling the total amount of NFTs and locked quantity during the lock drop.
Math
0xinsanity (Munchables) confirmed and commented:
0xsomeone (judge) commented:
Note: For full discussion, see here.
