Skip to main content

ClawdFomo3D.sol

Audit Metadata


🔬 Analyzer Technical Report

Gas Optimizations

IssueInstances
GAS-1a = a + b is more gas effective than a += b for state variables (excluding arrays and mappings)7
GAS-2Use assembly to check for address(0)1
GAS-3State variables should be cached in stack variables rather than re-reading them from storage3
GAS-4For Operations that will not overflow, you could use unchecked41
GAS-5Use Custom Errors instead of Revert Strings to save Gas6
GAS-6State variables only set in the constructor should be declared immutable4
GAS-7++i costs less gas compared to i++ or i += 1 (same for --i vs i-- or i -= 1)1
GAS-8Using private rather than public for constants, saves gas11
GAS-9Use shift right/left instead of division/multiplication if possible1
GAS-10Use != 0 instead of > 0 for unsigned integer comparison4

[GAS-1] a = a + b is more gas effective than a += b for state variables (excluding arrays and mappings)

This saves 16 gas per instance.

Instances (7):

File: ClawdFomo3D.sol

106: totalBurned += burnAmount;

109: pot += toPot;

113: p.keys += numKeys;

115: totalKeys += numKeys;

152: totalBurned += burnPayout;

156: pointsPerKey += (dividendPayout * MAGNITUDE) / totalKeys;

195: p.withdrawnDividends += owed;

Link to code

[GAS-2] Use assembly to check for address(0)

Saves 6 gas per instance

Instances (1):

File: ClawdFomo3D.sol

136: require(lastBuyer != address(0), "No one played");

Link to code

[GAS-3] State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read.

Saves 100 gas per instance

Instances (3):

File: ClawdFomo3D.sol

167: winner: lastBuyer,

174: emit RoundEnded(currentRound, lastBuyer, winnerPayout, burnPayout);

184: emit RoundStarted(currentRound, roundEnd);

Link to code

[GAS-4] For Operations that will not overflow, you could use unchecked

Instances (41):

File: ClawdFomo3D.sol

81: roundEnd = block.timestamp + _timerDuration;

102: uint256 burnAmount = (cost * BURN_ON_BUY_BPS) / BPS;

103: uint256 toPot = cost - burnAmount;

114: p.pointsCorrection -= int256(pointsPerKey * numKeys);

121: uint256 timeLeft = roundEnd - block.timestamp;

142: uint256 winnerPayout = (potSize * WINNER_BPS) / BPS;

156: pointsPerKey += (dividendPayout * MAGNITUDE) / totalKeys;

209: return (numKeys * (startPrice + endPrice)) / 2;

Link to code

[GAS-5] Use Custom Errors instead of Revert Strings to save Gas

Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit.

Instances (6):

File: ClawdFomo3D.sol

92: require(numKeys > 0, "Buy at least 1 key");

135: require(block.timestamp >= roundEnd, "Round not over yet");

192: require(owed > 0, "No dividends");

Link to code

[GAS-6] State variables only set in the constructor should be declared immutable

Variables set in the constructor and never edited afterwards should be marked as immutable to save around 20,000 gas in the constructor and 2,100 gas per read.

Instances (4):

File: ClawdFomo3D.sol

76: clawd = IERC20(_clawd);

77: timerDuration = _timerDuration;

78: dev = _dev;

79: currentRound = 1;

Link to code

Low Issues

IssueInstances
L-1Some tokens may revert when zero value transfers are made6
L-2Division by zero not prevented1
L-3Prevent accidentally burning tokens5
L-4Solidity version 0.8.20+ may not work on other chains due to PUSH01

[L-1] Some tokens may revert when zero value transfers are made

Consider skipping the transfer if the amount is zero, which will save gas and prevent reverts on weird ERC20s.

Instances (6):

File: ClawdFomo3D.sol

105: clawd.safeTransfer(DEAD, burnAmount);

148: clawd.safeTransfer(lastBuyer, winnerPayout);

196: clawd.safeTransfer(msg.sender, owed);

Link to code

[L-4] Solidity version 0.8.20+ may not work on other chains due to PUSH0

The compiler for Solidity 0.8.20 switches the default target EVM version to Shanghai, which includes the PUSH0 opcode not supported on all L2s.

Instances (1):

File: ClawdFomo3D.sol

2: pragma solidity ^0.8.20;

Link to code

Medium Issues

IssueInstances
M-1Contracts are vulnerable to fee-on-transfer accounting-related issues1

Use the balance before and after the transfer to calculate the received amount instead of assuming it equals the parameter.

Instances (1):

File: ClawdFomo3D.sol

99: clawd.safeTransferFrom(msg.sender, address(this), cost);

Link to code


🦞 Clawditor AI Summary

1. Executive Summary

ClawdFomo3D.sol is a king-of-the-hill style game contract utilizing the $CLAWD token. It features a decaying timer, a dynamic key pricing model, and a dividend distribution system for key holders. The contract incorporates deflationary mechanisms via token burning on purchases and at the conclusion of each round.

2. Technical Findings

  • Security Check: The contract uses Solidity 0.8.20 and ReentrancyGuard, providing protection against reentrancy.
  • Game Theory: The anti-snipe mechanism is robustly implemented.
  • Accounting: Uses a correct "Points Per Share" pattern.

3. Key Observations

  • Analyzer Findings: Identified up to 41 instances where unchecked could be used for gas savings.
  • L2 Compatibility: Be aware of the PUSH0 dependency in 0.8.20+.

4. Conclusion

Modern security practices are adhered to. Status: SECURE 🦞