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: 102/126
Findings: 2
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.014 USDC - $0.01
https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L257 https://github.com/code-423n4/2024-05-munchables/blob/57dff486c3cd905f21b330c2157fe23da2a4807d/src/managers/LockManager.sol#L265-L267
In the current implementation of LockManager::setLockDuration
, user can manipulate the unlock time, resulting into decreasing of the unlockTime.
In the current implementation of LockManager::setLockDuration
, users have the ability to decrease the lock time of their funds. This arises from the function's conditional check utilizing block.timestamp
, which inherently increases over time. Consequently, as users attempt to reduce the lock duration, the block.timestamp
continues to increment, leading to a decrease in the unlock time.
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);
Allowing users to reduce unlock time can have multifaceted impacts on the platform. Firstly, it facilitates early access to locked resources, potentially disrupting planned release schedules and causing unforeseen consequences.
Moreover, there's a potential increase in NFT acquisition as users exploit the system to acquire more NFTs than intended, which can affect asset scarcity and value. Additionally, premature unlocking could disrupt economic models tied to resource release schedules, impacting supply-demand dynamics, pricing mechanisms, and overall platform stability.
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);
setLockDurationTests.test.ts
and run tests with yarn build && yarn test
.... import { testClient, ONE_DAY, ONE_WEEK } from "../../utils/consts"; ... ... initialLockDuration = maxLockDuration - 50n; ... it("should revert with LockDurationReducedError when reducing lock duration", async () => { const setLockDurationTxHash = await testContracts.lockManager.contract.write.setLockDuration([initialLockDuration + 3n], { account: alice, }); const txReceipt = await assertTxSuccess({ txHash: setLockDurationTxHash }); const block = await testClient.getBlock({ blockHash: txReceipt.blockHash }); const lockedTokens = await testContracts.lockManager.contract.read.getLocked([alice]); assert(lockedTokens instanceof Array); const lockedEthToken = lockedTokens.find((t) => t.tokenContract === zeroAddress); assert.ok(lockedEthToken); assert(lockedEthToken.lockedToken.lastLockTime + Number(initialLockDuration) + Number(2n) < lockedEthToken.lockedToken.unlockTime); await testClient.setNextBlockTimestamp({ timestamp: block.timestamp + ONE_DAY, }); await assert.rejects( testContracts.lockManager.contract.simulate.setLockDuration([initialLockDuration + 2n], { account: alice, }), (err: Error) => assertContractFunctionRevertedError(err, "LockDurationReducedError") ); assert(lockedEthToken.lockedToken.lastLockTime + Number(initialLockDuration) + Number(1n) < lockedEthToken.lockedToken.unlockTime); await testClient.setNextBlockTimestamp({ timestamp: block.timestamp + (ONE_DAY + ONE_WEEK), }); await assert.rejects( testContracts.lockManager.contract.simulate.setLockDuration([initialLockDuration + 1n], { account: alice, }), (err: Error) => assertContractFunctionRevertedError(err, "LockDurationReducedError") ); });
Manual Review
Consider using lockedTokens[msg.sender][tokenContract].lastLockTime
instead of uint32(block.timestamp)
in the LockManager::setLockDuration
function.
It will look like this:
function setLockDuration(uint256 _duration) external notPaused { if (_duration > configStorage.getUint(StorageKey.MaxLockDuration)) revert MaximumLockDurationError(); playerSettings[msg.sender].lockDuration = uint32(_duration); // update any existing lock uint256 configuredTokensLength = configuredTokenContracts.length; for (uint256 i; i < configuredTokensLength; i++) { address tokenContract = configuredTokenContracts[i]; if (lockedTokens[msg.sender][tokenContract].quantity > 0) { // check they are not setting lock time before current unlocktime if ( lockedTokens[msg.sender][tokenContract].lastLockTime + uint32(_duration) < lockedTokens[msg.sender][tokenContract].unlockTime ) { revert LockDurationReducedError(); } uint32 lastLockTime = lockedTokens[msg.sender][tokenContract].lastLockTime; lockedTokens[msg.sender][tokenContract].unlockTime = lastLockTime + uint32(_duration); } } emit LockDuration(msg.sender, _duration); }
Invalid Validation
#0 - c4-judge
2024-06-04T12:41:41Z
alex-ppg marked the issue as duplicate of #89
#1 - c4-judge
2024-06-05T12:52:46Z
alex-ppg marked the issue as satisfactory
🌟 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.0037 USDC - $0.00
Judge has assessed an item in Issue #548 as 2 risk. The relevant finding follows:
There are two functions responsible for approving and disapproving proposals. Users are expected to either approve or disapprove a proposal, but not both. Currently approve function doesn't check if the user was already disapproved.
In the LockManager
contract, there are two functions for managing proposals: approveUSDPrice
and disapproveUSDPrice
. Users are expected to either approve or disapprove a proposal, but not both.
Disapprove Function:
The disapproveUSDPrice
function correctly checks if the user has already either approved or disapproved the proposal. If either condition is true, the function reverts to prevent the user from performing conflicting actions.
Approve Function:
The current implementation of the approveUSDPrice
function only checks if the user has already approved the proposal. It does not verify if the user has previously disapproved the proposal, creating an inconsistency.
This omission allows a user who has disapproved a proposal to later approve it, which contradicts the intended logic and can lead to inconsistent proposal states.
The lack of a disapproval check in the approveUSDPrice
function can result in a proposal being both approved and disapproved by the same user, leading to inconsistencies.
None
Manual Review
Add this line of code tot LockManager:approveUSDPrice
function:
if (usdUpdateProposal.disapprovals[msg.sender] == _usdProposalId) revert ProposalAlreadyDisapprovedError();
#0 - c4-judge
2024-06-05T12:33:29Z
alex-ppg marked the issue as duplicate of #495
#1 - c4-judge
2024-06-05T12:33:40Z
alex-ppg marked the issue as partial-25