Smart Contract Audit Checklist: 20 Things to Verify Before Mainnet Deployment
Deploying a smart contract to mainnet is a major milestone—but it’s also the point where mistakes become permanent. Unlike traditional applications, you can’t simply push a hotfix after users start interacting with your protocol.
Before deployment, every contract should go through a structured security review.
This checklist covers 20 critical areas that developers and auditors commonly verify before a smart contract is considered ready for production.
Why Use a Security Checklist?
Even experienced Solidity developers can overlook subtle issues.
A checklist helps ensure that important security considerations are reviewed consistently, reducing the risk of vulnerabilities reaching production.
Keep in mind that this checklist does not replace a professional audit—it helps you prepare for one.
1. Review Access Control
Identify every privileged function.
Ask yourself:
- Who can call this function?
- Should it be restricted?
- Is multi-signature control required?
Never expose administrative functionality unintentionally.
2. Validate Input Parameters
Every external function should validate its inputs.
Check for:
- Zero addresses
- Invalid amounts
- Overflow conditions
- Invalid IDs
- Duplicate values
Never assume user input is valid.
3. Protect Against Reentrancy
Review every external call.
Ensure that:
- State updates happen before interactions.
- ReentrancyGuard is used where appropriate.
- External callbacks cannot manipulate contract state.
4. Verify Oracle Usage
If your protocol depends on price feeds:
- Check stale prices.
- Verify decimal conversions.
- Validate oracle availability.
- Consider fallback mechanisms.
Incorrect oracle integration has caused major DeFi exploits.
5. Review Arithmetic Logic
Even with Solidity 0.8+, arithmetic bugs still happen.
Double-check:
- Fee calculations
- Reward distribution
- Percentage math
- Decimal conversions
- Token accounting
6. Check Upgradeability
If using proxy contracts:
- Protect initializer functions.
- Lock implementation contracts.
- Verify storage layout compatibility.
- Test upgrade scenarios.
7. Review External Calls
Every external call increases risk.
Ask:
- Can this call fail?
- What happens if it reverts?
- Can it trigger callbacks?
Never trust external contracts.
8. Test Permission Changes
Review how roles are granted, transferred, and revoked.
Verify:
- Ownership transfers
- Emergency roles
- Admin privileges
- Timelocks
9. Test Emergency Pause Logic
If your protocol supports emergency pauses:
Verify:
- Only authorized users can pause.
- Normal functionality resumes correctly.
- Funds remain recoverable.
10. Verify Token Transfers
Ensure your contract safely handles:
- ERC20 transfers
- Native ETH transfers
- Failed transfers
- Fee-on-transfer tokens (if supported)
11. Prevent Denial of Service
Avoid:
- Unbounded loops
- Large array iterations
- Dependence on every transaction succeeding
Scalability is also part of security.
12. Review Randomness
Avoid using:
- block.timestamp
- blockhash
- block.number
For secure randomness, use verifiable random sources such as Chainlink VRF.
13. Check Event Emission
Events improve transparency.
Verify that important actions emit events:
- Ownership changes
- Deposits
- Withdrawals
- Role updates
- Critical configuration changes
14. Review Gas Efficiency
High gas costs reduce usability.
Review:
- Storage writes
- Loop complexity
- Redundant calculations
- Variable packing
Optimization should never sacrifice security.
15. Write Unit Tests
Every important function should have tests.
Include:
- Success cases
- Failure cases
- Edge cases
- Permission checks
High test coverage increases confidence.
16. Perform Fuzz Testing
Fuzz testing generates thousands of random inputs to identify unexpected behavior.
Modern frameworks like Foundry make fuzz testing easy to integrate into your workflow.
17. Write Invariant Tests
Define properties that should always remain true.
Examples:
- Total supply never changes unexpectedly.
- Balances never become negative.
- Assets remain fully accounted for.
Invariant testing often uncovers issues that unit tests miss.
18. Review Documentation
Ensure your documentation matches the implementation.
Review:
- README
- Technical specifications
- Deployment instructions
- Role descriptions
Documentation reduces misunderstandings during audits.
19. Simulate Real User Flows
Test complete scenarios instead of isolated functions.
Examples:
- Deposit → Withdraw
- Stake → Claim Rewards
- Borrow → Repay
- Mint → Transfer → Burn
Real workflows often reveal hidden logic flaws.
20. Schedule an Independent Audit
Internal reviews are valuable, but fresh eyes often discover overlooked issues.
An independent audit provides:
- Objective analysis
- Security recommendations
- Increased user confidence
- Better investor trust
No checklist can replace an experienced security team.
Final Thoughts
Shipping secure smart contracts requires more than writing functional code.
By reviewing access control, external interactions, testing strategies, upgradeability, and protocol logic before deployment, you significantly reduce the likelihood of costly vulnerabilities.
Use this checklist during development, before testnet deployments, and again before every mainnet release.
Security is not a milestone—it's an ongoing process.