Platform: Code4rena
Start Date: 22/05/2024
Pot Size: $20,000 USDC
Total HM: 6
Participants: 126
Period: 5 days
Judge: 0xsomeone
Total Solo HM: 1
Id: 379
League: ETH
Rank: 88/126
Findings: 1
Award: $0.01
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: SpicyMeatball
Also found by: 0rpse, 0xMosh, 0xblack_bird, 0xdice91, 0xhacksmithh, 0xleadwizard, 0xmystery, Audinarey, AvantGard, Bigsam, Dots, EPSec, Eeyore, Janio, Limbooo, LinKenji, Mahmud, MrPotatoMagic, Myd, Oxsadeeq, Sabit, SovaSlava, Stefanov, Tychai0s, Utsav, Varun_05, Walter, adam-idarrha, ahmedaghadi, araj, aslanbek, ayden, bigtone, c0pp3rscr3w3r, carrotsmuggler, crypticdefense, dhank, fyamf, gajiknownnothing, gavfu, itsabinashb, jasonxiale, joaovwfreire, ke1caM, leegh, merlinboii, mitko1111, n4nika, pfapostol, prapandey031, rouhsamad, sandy, snakeeaterr, stakog, steadyman, swizz, tedox, th3l1ghtd3m0n, trachev, turvy_fuzz, xyz, yashgoel72, zhaojohnson
0.0105 USDC - $0.01
If a user calls setLockDuration
multiple times without unlocking their tokens in between, the unlock time will be calculated incorrectly. This means that users may be able to unlock their tokens earlier or later than intended, violating the intended locking rules.
Potential Loss of Funds If the unlock time is calculated to be earlier than intended, users may be able to unlock and transfer their tokens before the intended lock period has expired. This could lead to potential loss of funds or assets that were meant to be locked for a specific duration.
The code responsible for updating the unlock time: LockManager.sol#L254-268
if (lockedTokens[msg.sender][tokenContract].quantity > 0) { // check they are not setting lock time before current unlocktime if ( uint32(block.timestamp) + uint32(_duration) < lockedTokens[msg.sender][tokenContract].unlockTime ) { revert LockDurationReducedError(); } uint32 lastLockTime = lockedTokens[msg.sender][tokenContract].lastLockTime; lockedTokens[msg.sender][tokenContract].unlockTime = lastLockTime + uint32(_duration); }
The problem is that the new unlock time is calculated based on the lastLockTime
instead of the current block.timestamp
. This means that if the user calls setLockDuration
multiple times without unlocking their tokens in between, the unlock time will be calculated incorrectly.
For example, let's say the initial lock duration is set to 1 week, and the user calls setLockDuration
again after 3 days to extend the lock duration to 2 weeks. The new unlock time should be block.timestamp + 2 weeks
, but instead, it will be calculated as lastLockTime + 2 weeks
, which is incorrect.
Alice calls setLockDuration(604800)
(1 week) at block timestamp 1000
. Her unlock time is set to 1000 + 604800 = 605800.
Three days later, at block timestamp 1259200
, Alice calls setLockDuration(1209600)
(2 weeks) to extend her lock duration.
According to the current implementation, the new unlock time is calculated as lastLockTime + 1209600 = 605800 + 1209600 = 1815400.
However, the correct unlock time should be block.timestamp + 1209600 = 1259200 + 1209600 = 2468800
.
As you can see, the calculated unlock time (1815400
) is earlier than the intended unlock time (2468800
), allowing Alice to unlock her tokens before the intended lock period has expired.
Manual Review
The code should use the current block.timestamp instead of lastLockTime
when calculating the new unlock time.
if (lockedTokens[msg.sender][tokenContract].quantity > 0) { // check they are not setting lock time before current unlocktime if ( uint32(block.timestamp) + uint32(_duration) < lockedTokens[msg.sender][tokenContract].unlockTime ) { revert LockDurationReducedError(); } lockedTokens[msg.sender][tokenContract].unlockTime = uint32(block.timestamp) + uint32(_duration); }
Error
#0 - c4-judge
2024-06-04T12:40:57Z
alex-ppg marked the issue as duplicate of #89
#1 - c4-judge
2024-06-05T12:53:38Z
alex-ppg marked the issue as partial-75
#2 - c4-judge
2024-06-05T12:54:34Z
alex-ppg changed the severity to 3 (High Risk)