Developer Resource
Free Checklist

Solidity Security Checklist

A practical, category-by-category checklist of security checks to run before deploying any Solidity smart contract. Each item links to a detailed guide explaining the vulnerability, real examples, and how to fix it.

Access Control

Deep dive
All state-changing functions have appropriate access control (onlyOwner, role-based)
Critical
msg.sender is used for authentication — not tx.origin
Critical
initialize() on upgradeable contracts uses the initializer modifier
Critical
upgradeTo() / _authorizeUpgrade() is restricted to owner or multisig
Critical
Emergency pause/unpause functions are access-controlled
Admin functions emit events for off-chain monitoring

Reentrancy

Deep dive
State updates happen BEFORE external calls (checks-effects-interactions)
Critical
ReentrancyGuard from OpenZeppelin is applied to all ETH-sending functions
Critical
Cross-function reentrancy is checked — not just same-function
Critical
External calls do not reenter any function that reads shared state
pull payment pattern used instead of push for ETH distribution

Arithmetic

Deep dive
Using Solidity 0.8+ (overflow/underflow reverts by default)
Critical
Any unchecked {} blocks have been manually verified as safe
Critical
Division before multiplication is avoided (rounding errors)
Fixed-point arithmetic uses appropriate precision (1e18 for ETH amounts)
No division by zero in any code path
Critical

External Calls

Deep dive
All .call() return values are checked
Critical
.transfer() is not used (fixed 2300 gas stipend can break on EIP-1884 contracts)
External calls do not loop over user-supplied arrays (DoS risk)
Critical
Fallback/receive functions in called contracts cannot cause issues
Interfaces match the actual external contract ABI

Oracle & Price Feeds

Deep dive
Spot price reads (getReserves, slot0) are not used for critical decisions
Critical
TWAP (time-weighted average price) is used for oracle-dependent logic
Critical
Multiple oracle sources or a circuit breaker is in place
Chainlink price feeds check for stale answers (updatedAt + heartbeat)
Critical
Flash loan price manipulation is considered for all AMM integrations
Critical

Randomness & Timestamps

Deep dive
block.timestamp is not used as the sole randomness source
Critical
block.prevrandao is not used as the sole randomness source
Critical
Chainlink VRF is used for any on-chain randomness requirement
Critical
Timestamp comparisons use hour/day tolerances — not second-level precision
No logic depends on exact block timestamps (validator manipulation risk)

Upgradeable Contracts

Deep dive
_disableInitializers() is called in the implementation constructor
Critical
Storage layout is identical between proxy and all implementation versions
Critical
New implementation versions append variables — never reorder existing ones
Critical
Upgrade function requires multisig + timelock (48-72 hours minimum)
Critical
OpenZeppelin's Initializable, UUPSUpgradeable, or TransparentUpgradeableProxy is used

Gas & DoS

Deep dive
No unbounded loops over arrays that users can grow
Critical
ETH distribution uses pull pattern — not push to multiple addresses
Critical
Array sizes are capped or iteration is paginated
Failed external calls in loops are handled with continue — not revert
Critical
gasleft() checks in long iterative functions to bail out gracefully

Skip the manual checklist — scan automatically

Our scanner checks your contract against most of these items automatically in under 30 seconds. Upload your Solidity and get a full findings report, free.

3 free tokens per day · No account required to start