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.
Tip: Items marked
Critical
should be checked on every contract. Run an automated scan first — it catches most of these in under a minute.Access Control
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
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
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
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
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
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
_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
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
Full vulnerability library
Each checklist item above links to a detailed guide. Browse all vulnerability pages here: