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