Forgeries contest - Bobface's results

A protocol for on-chain games with NFT prizes on Ethereum.

General Information

Platform: Code4rena

Start Date: 13/12/2022

Pot Size: $36,500 USDC

Total HM: 5

Participants: 77

Period: 3 days

Judge: gzeon

Total Solo HM: 1

Id: 191

League: ETH

Forgeries

Findings Distribution

Researcher Performance

Rank: 34/77

Findings: 2

Award: $71.66

QA:
grade-b
Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

45.7078 USDC - $45.71

Labels

bug
grade-b
QA (Quality Assurance)
Q-11

External Links

Forgeries QA Report

The codebase is secure and well-structured. A few minor code-style issues and execution ordering errors are listed below.

Low risk

redraw() can be called before startDraw()

VRFNFTRandomDraw.redraw() can be successfully called before the first draw has been started through startDraw() by transferrig the NFT to the contract and then calling redraw(). This does not cause any substantial risk to the NFT, but it causes the contract to not emit the SetupDraw event when the first draw starts, potentially causing off-chain software tracking the events to have issues.

redraw() does not emit an event

VRFNFTRandomDraw.redraw() does not emit an event, e.g. Redraw, making it hard for off-chain software to monitor for redraws.

Non-Critical

Best Practices: Prefer constant over immutable

constant values are known at compile time and unchangeable. immutable values can be changed in the constructor and are unchangeable thereafter. If an immutable value is not changed in the constructor, the constant keyword should be used.

FileLinesDescription
src/VRFNFTRandomDraw.sol22callbackGasLimit
src/VRFNFTRandomDraw.sol24minimumRequestConfirmations
src/VRFNFTRandomDraw.sol26wordsRequested
src/VRFNFTRandomDraw.sol29HOUR_IN_SECONDS
src/VRFNFTRandomDraw.sol31WEEK_IN_SECONDS
src/VRFNFTRandomDraw.sol33MONTH_IN_SECONDS

Best Practices: Specify state variable visibility

Visibility for state variables should be explicitly specified, even if the default internal is intended.

FileLinesDescription
src/VRFNFTRandomDraw.sol22callbackGasLimit
src/VRFNFTRandomDraw.sol24minimumRequestConfirmations
src/VRFNFTRandomDraw.sol26wordsRequested
src/VRFNFTRandomDraw.sol29HOUR_IN_SECONDS
src/VRFNFTRandomDraw.sol31WEEK_IN_SECONDS
src/VRFNFTRandomDraw.sol33MONTH_IN_SECONDS
src/VRFNFTRandomDraw.sol37coordinator

#0 - c4-judge

2022-12-17T17:22:58Z

gzeon-c4 marked the issue as grade-b

Awards

25.9485 USDC - $25.95

Labels

bug
G (Gas Optimization)
grade-b
G-12

External Links

Forgeries Gas Report

Computed value should be stored as constant

A constant value is dynamically computed. It should be stored as constant to avoid recomputation.

FileLinesDescription
src/VRFNFTRandomDraw.sol95(MONTH_IN_SECONDS * 12) can be stored as constant YEAR_IN_SECONDS

Redundant checks

A condition is unnecessarily checked multiple time. The duplicate check should be removed.

FileLinesDescription
src/VRFNFTRandomDraw.sol126 - 137initialize checks that the owner of the contract owns the NFT to be raffled. However, it does not transfer in the NFT. The NFT is only transferred in in startDraw. The check in initialize can be removed, since if the NFT is not owned or does not exist, the check in startDraw will catch this condition.
src/VRFNFTRandomDraw.sol175 - 177Checking for request.currentChainlinkRequestId != 0 is done again in _requestRoll().
src/utils/OwnableUpgradeable.sol95The statement first reads (SLOAD) the storage slots, checks if for zero, and if it is non-zero, deletes (SSTORE) it. Removing the if guard and just always executing delete _pendingOwner is cheaper, since the overhead through the if condition is removed.

Storing msg.sender in a local variable

The EVM offers the CALLER opcode which only costs 2 gas (see here). msg.sender is directly translated to the CALLER opcode, which makes it more expensive to store msg.sender in a local stack variable due to the required stack operations. Using msg.sender directly where it is requied is cheaper.

FileLinesDescription
src/VRFNFTRandomDraw.sol279address user = msg.sender;
src/VRFNFTRandomDrawFactory.sol42address admin = msg.sender;

Oversized types in storage structs

Using smaller types where possible in structs which are stored in storage decreases the type's size and thus requires less storage slots, making it cheaper to read and write the struct from and to storage.

FileLinesDescription
src/interfaces/IVRNFTRandomDraw.sol67drawTimelock can be uint128, saving one storage slot
src/interfaces/IVRNFTRandomDraw.sol83, 85drawBufferTime and recoverTimelock can be uint128, saving one storage slot

#0 - c4-judge

2022-12-17T17:44:30Z

gzeon-c4 marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter