The Smart Contract Security Checklist Every Developer Needs

Before deploying any smart contract to mainnet, run through this checklist. These are the 20 most critical security checks based on real-world exploits that have cost the industry billions of dollars.

Critical — Contract draining risk High — Significant fund risk Medium — Logic/access issues

Access Control

✓
Use msg.sender not tx.origin for authentication
tx.origin is vulnerable to phishing attacks. Always use msg.sender for access control checks.
✓
All sensitive functions have access modifiers
Every function that modifies state or moves funds must have onlyOwner, onlyRole, or equivalent protection.
✓
Ownership transfer checks for zero address
transferOwnership() must require(newOwner != address(0)) to prevent accidentally burning ownership.
✓
Consider using 2-step ownership transfer
Use a pending owner pattern where the new owner must accept before the transfer completes.

Reentrancy

✓
Follow Checks-Effects-Interactions pattern
Always update state variables BEFORE making external calls. Never send ETH before zeroing balances.
✓
Use ReentrancyGuard on ETH-sending functions
Add OpenZeppelin's nonReentrant modifier to all functions that send ETH or call external contracts.

Arithmetic

✓
Use Solidity 0.8+ or SafeMath for arithmetic
Solidity 0.8+ has built-in overflow protection. For older versions, use OpenZeppelin's SafeMath library.
✓
Check for division by zero
Any division operation where the divisor could be zero must be guarded with a require() check.
✓
Be careful with multiplication before division
Always multiply before dividing to avoid precision loss. E.g. (a * b) / c not a / c * b.

External Calls

✓
Check return values of external calls
Always check the return value of low-level calls. Unchecked failures are a common vulnerability.
✓
Be cautious with delegatecall
delegatecall executes code in the context of your contract. Only delegatecall to trusted, audited contracts.
✓
Avoid flash loan vulnerabilities
Don't rely on token balances for security checks. Use internal accounting instead of address(this).balance.

Logic & State

✓
No selfdestruct unless absolutely necessary
selfdestruct is a rugpull vector. If needed, protect it with a timelock and multisig.
✓
Don't use block.timestamp for critical logic
Miners can manipulate timestamps by a few seconds. Don't use it for randomness or tight deadlines.
✓
Avoid on-chain randomness
block.hash and block.timestamp are predictable. Use Chainlink VRF for any randomness needs.

Deployment & Config

✓
Test on testnet first
Deploy and test thoroughly on Sepolia or Goerli before mainnet. Test all edge cases and failure modes.
✓
Verify source code on Etherscan
Always verify your contract source code. Unverified contracts are red flags to users and auditors.
✓
Run automated security analysis
Use tools like AuditAI, Slither, or MythX to automatically detect common vulnerabilities before deployment.
✓
Consider a professional audit for high-value contracts
For contracts holding significant funds, a professional audit from a reputable firm is strongly recommended.
Run automated checks on your contract
AuditAI checks all of the above automatically in seconds. Free security audit for any Solidity contract.
⬡ Run Free Audit