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: 27/126
Findings: 3
Award: $0.03
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Circolors
Also found by: 0rpse, 0x175, 0xAadi, 0xHash, 0xMax1mus, 0xMosh, 0xblack_bird, 0xdice91, 0xfox, 0xhacksmithh, 0xloscar01, 0xrex, 4rdiii, Audinarey, AvantGard, Bigsam, DPS, Dots, Drynooo, Dudex_2004, Evo, Kaysoft, King_, Limbooo, MrPotatoMagic, PENGUN, Sabit, SovaSlava, SpicyMeatball, TheFabled, Utsav, Varun_05, Walter, adam-idarrha, araj, aslanbek, ayden, bctester, biakia, bigtone, brgltd, carrotsmuggler, cats, crypticdefense, dd0x7e8, dhank, fandonov, fyamf, grearlake, iamandreiski, ilchovski, jasonxiale, joaovwfreire, lanrebayode77, m4ttm, merlinboii, niser93, nnez, octeezy, oxchsyston, pamprikrumplikas, rouhsamad, tedox, trachev, turvy_fuzz, twcctop, yotov721, zhaojohnson
0.0056 USDC - $0.01
Users can set their own lockDuration
to any value by calling the setLockDuration
function. The only requirement is that they must not have any locks which are about to expire outside the new window in the future.
for (uint256 i; i < configuredTokensLength; i++) { //... if (lockedTokens[msg.sender][tokenContract].quantity > 0) { //... if (uint32(block.timestamp) + uint32(_duration) < lockedTokens[msg.sender][tokenContract].unlockTime) { revert LockDurationReducedError(); //@audit rejects setting if it effectively lowers any lock duration } } }
So if a lock is about to expire in 10 days, a user cannot set the new _duration
to less than 10 days. This is done to make sure that the locks are not shortened in any way.
The issue is that other users can open locks on behalf of this user via the lockOnBehalf
function. So they can open new locks, which will prevent the victim from shortening their duration.
Since any user can stop other users from reducing their lock duration, this can be considered a high severity issue.
Say Alice is a player, and has her current lock duration set to 10 days. She wants to reduce it to 5 days.
Alice has no locks active. So she should be able to call the setLockDuration
function with a new _duration
of 5 days.
However, Bob calls the lockOnBehalf
function with Alice's address and locks some tokens for 10 days. Now, when Alice tries to call the setLockDuration
function with a new _duration
of 5 days, the transaction will revert. This is because the lockedTokens[msg.sender][tokenContract].unlockTime
is set to be 10 days away, but block.timestamp + _duration
is only 5 days away.
So Alice is unable to reduce her lock duration.
Manual Review
Consider disabling the lockOnBehalf
function. Or, add a check so that only a set of addresses approved by the user can use lockOnBehalf
for the target user.
Access Control
#0 - c4-judge
2024-06-05T12:59:27Z
alex-ppg marked the issue as partial-75
🌟 Selected for report: Circolors
Also found by: 0rpse, 0x175, 0xAadi, 0xHash, 0xMax1mus, 0xMosh, 0xblack_bird, 0xdice91, 0xfox, 0xhacksmithh, 0xloscar01, 0xrex, 4rdiii, Audinarey, AvantGard, Bigsam, DPS, Dots, Drynooo, Dudex_2004, Evo, Kaysoft, King_, Limbooo, MrPotatoMagic, PENGUN, Sabit, SovaSlava, SpicyMeatball, TheFabled, Utsav, Varun_05, Walter, adam-idarrha, araj, aslanbek, ayden, bctester, biakia, bigtone, brgltd, carrotsmuggler, cats, crypticdefense, dd0x7e8, dhank, fandonov, fyamf, grearlake, iamandreiski, ilchovski, jasonxiale, joaovwfreire, lanrebayode77, m4ttm, merlinboii, niser93, nnez, octeezy, oxchsyston, pamprikrumplikas, rouhsamad, tedox, trachev, turvy_fuzz, twcctop, yotov721, zhaojohnson
0.0056 USDC - $0.01
Users can unlock after their lock period ends. The unlock time is denoted by the unlockTime
field of the struct. This check is done in the unlock
function.
if (lockedToken.unlockTime > uint32(block.timestamp)) { revert TokenStillLockedError(); }
However, when a user creates a lock over an existing lock, the time gets reset. The previous unlock time is overwritten with the new unlock time. This is done in the lock
function.
lockedToken.lastLockTime = uint32(block.timestamp); lockedToken.unlockTime = uint32(block.timestamp) + uint32(_lockDuration);
So if a fresh lock is opened, the unlockTime
is refreshed and pushed back by the duration amount. The issue is that any user can trigger this via the lockOnBehalf
function. This function allows users to lock tokens on behalf of other users. This will also reset the lock time.
So if a user waits untl the unlock time to unlock their stake, another user can come in and reset the lock time by depositing a single wei through the lockOnBehalf
function. The original user will not be able to unlock their stake until the new lock time is reached.
Since this prevents unlocks, this can be considered a high severity issue.
Say Alice has a lock expiring today. Her lockDuration
is set to 10 days.
Bob calls the lockOnBehalf
function with Alice's address and locks some tokens. Alice's unlockTime
is now pushed back by 10 days.
Alice is unable to unlock her tokens. This can be done indefinitely to prevent Alice from unlocking her tokens.
Manual Review
Consider removing the lockOnBehalf
function. Or, add a check so that only a set of addresses approved by the user can use lockOnBehalf
for the target user.
Access Control
#0 - c4-judge
2024-06-05T12:59:26Z
alex-ppg marked the issue as satisfactory
🌟 Selected for report: Circolors
Also found by: 0rpse, 0x175, 0xAadi, 0xHash, 0xMax1mus, 0xMosh, 0xblack_bird, 0xdice91, 0xfox, 0xhacksmithh, 0xloscar01, 0xrex, 4rdiii, Audinarey, AvantGard, Bigsam, DPS, Dots, Drynooo, Dudex_2004, Evo, Kaysoft, King_, Limbooo, MrPotatoMagic, PENGUN, Sabit, SovaSlava, SpicyMeatball, TheFabled, Utsav, Varun_05, Walter, adam-idarrha, araj, aslanbek, ayden, bctester, biakia, bigtone, brgltd, carrotsmuggler, cats, crypticdefense, dd0x7e8, dhank, fandonov, fyamf, grearlake, iamandreiski, ilchovski, jasonxiale, joaovwfreire, lanrebayode77, m4ttm, merlinboii, niser93, nnez, octeezy, oxchsyston, pamprikrumplikas, rouhsamad, tedox, trachev, turvy_fuzz, twcctop, yotov721, zhaojohnson
0.0056 USDC - $0.01
When a user creates a lock, if their lockDuration
was set to 0, their lock duration will get set to the minimum value.
if (_lockDuration == 0) { _lockDuration = lockdrop.minLockDuration; } playerSettings[_lockRecipient].lockDuration = _lockDuration;
This is present in the _lock
function. the issue is that this can be trigerred by other users, via the lockOnBehalf
function.
If a user has their lock duration set to 0, either because its a fresh account, or if they had deliberately set it as such, their lockduration can be reset to the minimum value by any other user.
Since a different user is able to manipulate the settings of a user's account, this is a medium severity issue.
Say Alice hsa her lockDuration
set to 0. Bob can call the lockOnBehalf
function with Alice's address, which will reset her lock duration to the minimum value.
Manual code review
Consider disabling the lockOnBehalf
function. Or, add a check so that only a set of addresses approved by the user can use lockOnBehalf
for the target user.
Access Control
#0 - c4-judge
2024-06-05T12:59:25Z
alex-ppg marked the issue as satisfactory
#1 - c4-judge
2024-06-05T13:00:01Z
alex-ppg changed the severity to 3 (High Risk)
🌟 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
The function setLockDuration
can be called to change the duration of locks that the user wishes to have applied to their locks by default. This function should make sure that users cannot reduce the lock times of their locks retroactively, so puts in a safeguard for the same.
Hypothetically, if users were able to reduce their lock times, that would be problematic. The system gives out rewards/incentives based on the duration of the locks, so users can game the system by creating long locks, collecting the benefits, and then reducing the lock time. This should not be allowed by the system.
In this contract, such a safeguard is implemented, but it is not implemented correctly.
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 lock time is set to the lastLockTime + duration
This can actually reduce the lock time.
Lets say a lock exists. It was created on Jan 1, and had a duration of 10 days. Thus, the unlock time is Jan 11.
On Jan 7, Alice calls setLockDuration
with a duration of 5 days.
block.timestamp + duration
= Jan 7 + 5 days = Jan 12. This is higher than the unlockTime
, which is Jan 11. So no revert happens.
lastLockTime
is set to the previous lock time, of Jan 1.
unlockTime
is set to lastLockTime + duration
= Jan 1 + 5 days = Jan 6.
Alice's lock, which was about to expire on Jan 11, is now immediately available!
Manual analysis
The new lock time should be block.timestamp
, not the old lock time. This will basically create a fresh lock from today. In the above POC, if the lastLockTime
was set to block.timestamp
, the new unlock time will be Jan 7 + 5 days = Jan 12 (since block.timestamp is Jan 7). This will ensure that the unlock time never decreases.
Change the code to:
lockedTokens[msg.sender][tokenContract].unlockTime = block.timestamp + uint32(_duration);
Invalid Validation
#0 - c4-judge
2024-06-04T12:40:45Z
alex-ppg marked the issue as duplicate of #89
#1 - c4-judge
2024-06-05T12:54:08Z
alex-ppg marked the issue as partial-75
🌟 Selected for report: robertodf99
Also found by: 0xAadi, 0xAkira, 0xdice91, 0xhacksmithh, 0xleadwizard, AgileJune, Bauchibred, Bbash, Beosin, Bigsam, Dots, EPSec, EaglesSecurity, Eeyore, Evo, John_Femi, Mahmud, MrPotatoMagic, RotiTelur, Rushkov_Boyan, Sabit, Sentryx, Stormreckson, Topmark, Tychai0s, Utsav, Walter, ZanyBonzy, ZdravkoHr, adam-idarrha, araj, aslanbek, avoloder, bigtone, brevis, brgltd, carrotsmuggler, crypticdefense, dd0x7e8, dhank, djanerch, falconhoof, iamandreiski, joaovwfreire, leegh, merlinboii, mitko1111, pamprikrumplikas, pfapostol, prapandey031, swizz, trachev, twcctop, typicalHuman, unique, xyz
0.0148 USDC - $0.01
Oracles are able to put up a proposal for a price update, and then the other oracles can vote on it. This is done via the approveUSDPrice
and the disapproveUSDPrice
functions.
After an oracle approves a proposal, they cannot disapprove it. This is ensured by the following check:
function approveUSDPrice(uint256 _price) { usdUpdateProposal.approvals[msg.sender] = _usdProposalId; } function disapproveUSDPrice(uint256 _price) { if (usdUpdateProposal.approvals[msg.sender] == _usdProposalId) { revert ProposalAlreadyApprovedError(); } }
So if a userhas already voted for a price, they cannot vote against it.
However, the opposite check is not present. If a user votes against a proposal, their name is marked in the usdUpdateProposal.disapprovals
mapping. However, this is not checked in the approveUSDPrice
function. This means that a user can vote against a proposal, and then vote for it, and their vote will be counted both times.
Since an oracle gets two vote twice, this is considered manipulation of the voting system.
Due to the missing check, Alice, an oracle, can first vote against a proposal. then she can also vote for that proposal, since the usdUpdateProposal.disapprovals[Alice]
value is not checked.
Manual Review
Add a check in the approveUSDPrice
function to ensure that a user cannot vote for a proposal if they have already voted against it.
Invalid Validation
#0 - 0xinsanity
2024-05-30T23:17:50Z
Duplicate
#1 - c4-judge
2024-06-05T12:42:53Z
alex-ppg marked the issue as satisfactory