The problem: bugs on a blockchain are permanent and public
A bug in a normal app is embarrassing. A bug in a smart contract can be catastrophic. Contracts often hold real value, the code is public for attackers to study, and once deployed it usually can't be patched like a website.
Most exploits aren't exotic. They come from a short list of well-known mistakes. Work through this checklist before mainnet.
Design and access
1. Write the specification first
List every function, who may call it, and what must always be true (for example, "total supply never exceeds X"). Bugs hide in the gap between what you meant and what you wrote.
2. Lock down admin functions
Minting, pausing, upgrading, withdrawing treasury funds and changing owners must be restricted with access control, such as OpenZeppelin's Ownable or AccessControl. Test that other accounts are rejected.
3. Put admin keys behind a multisig
A single private key controlling a contract is a single point of failure. Use a multisig wallet and, where possible, a timelock on sensitive changes.
4. Use battle-tested libraries
Don't write your own token or access-control logic. OpenZeppelin's contracts have been reviewed and used far more widely than anything written from scratch.
Common vulnerabilities
5. Reentrancy
If your contract sends ETH or calls another contract before updating its own state, an attacker can call back in and repeat the action. Update state first (checks-effects-interactions) and add a reentrancy guard.
6. Authorisation with tx.origin
Use msg.sender for authorisation, never tx.origin.
7. Unchecked external calls
Low-level calls return a success flag. Check it, and assume any external contract can behave maliciously.
8. On-chain randomness
Block timestamps and hashes can be predicted or influenced. Use a verifiable randomness source such as Chainlink VRF for anything that needs to be fair.
9. Price oracles
Prices taken from a single liquidity pool can be manipulated within one transaction. Use robust oracles and sanity checks.
10. Arithmetic and rounding
Solidity 0.8+ checks overflow, but rounding in division can still leak value over many transactions. Test the edge cases.
Testing and review
11. Unit, fuzz and invariant tests
Unit tests check what you expect. Fuzz and invariant tests (Foundry makes them easy) throw thousands of random inputs at your contract to find what you didn't expect.
12. Static analysis
Tools such as Slither catch many common issues automatically. Run them and read every warning.
13. An independent review
The person who wrote the code is the worst person to review it. Get a second developer to read it line by line and, for contracts holding significant value, an independent audit firm.
Launch
14. Rehearse on a testnet
Deploy the exact build, run every function, and simulate admin operations before mainnet.
15. Verify, monitor and plan for incidents
Verify source code on the block explorer, set up monitoring and alerts, and write down what you'll do if something goes wrong, including who can pause the contract.
How EmmaForYou helps
Our smart contracts and Solidity service covers specification, implementation with OpenZeppelin, unit, fuzz and invariant testing, static analysis, manual review and deployment with multisig ownership. If you're heading for a formal audit, we'll prepare you for it and fix the findings.
Free tool
Free smart contract check
Paste Solidity code and get findings ranked by severity, each with a fix.
Want us to do this for you?
We help businesses across 3 continents implement everything you just read — and a lot more.
Related articles
Publishing
How to Launch a Book When You Don't Have a Big Audience Yet
7 min · Oct 28, 2026
AI Automation
Missed Messages Are Missed Money: How an AI Receptionist Books Customers While You Sleep
7 min · Oct 13, 2026
Website & SEO
11 Website Mistakes That Quietly Cost Small Businesses Customers (and How to Fix Each)
9 min · Oct 13, 2026