Smart contract security basics: common vulnerabilities and audit preparation
Smart contracts are immutable, public and hold value, which makes mistakes expensive and permanent. Security has to be designed in, tested hard and reviewed independently.By MI Technologies Engineering3 min read
A web application bug can be patched on Tuesday. A deployed smart contract usually cannot be changed, its code is public, and it may hold real value. That combination makes security the central engineering concern, not a final review.
Why the stakes are different
- Immutability. Once deployed, logic generally stays. Upgrade patterns exist, but they add their own risks.
- Public code and state. Attackers read your contract and can simulate attacks for free.
- Value at stake. A flaw can be exploited quickly and irreversibly.
- Composability. Your contract interacts with others, so risks arrive from outside your code.
Vulnerability classes to design against
Reentrancy. An external call lets an attacker call back into your contract before your state is updated, withdrawing repeatedly. Defend with the checks-effects-interactions pattern — update state *before* external calls — and reentrancy guards where appropriate.
Access control mistakes. Functions that should be restricted are callable by anyone, or ownership can be taken. Make privileges explicit, minimal and tested, and protect initialisation functions especially in upgradeable contracts.
Arithmetic errors. Modern compilers revert on overflow by default, but rounding, precision loss, unit mismatches and unchecked blocks still cause bugs, especially in financial calculations.
Oracle and price manipulation. Relying on a single spot price that an attacker can move within one transaction is a recurring cause of large losses. Use robust price sources, time-weighted values and sanity checks.
Front-running and ordering dependence. Transactions in the public queue can be observed and reordered. Be careful with designs where ordering reveals profit, and consider commit-reveal schemes or slippage limits.
Unsafe external calls and unchecked returns. Handle failures explicitly, and be cautious with arbitrary calls and untrusted contracts.
Denial of service. Unbounded loops over user-controlled data, or logic that depends on a call that can be made to revert, can lock functions.
Upgradeability risks. Proxy patterns introduce storage-layout hazards and admin-key risk. If you upgrade, protect the upgrade path with a multi-signature process and time delays.
Key management. Many incidents are about stolen administrator keys, not code. Use multi-signature or hardware-backed control for privileged roles.
Build security into the process
- Keep contracts simple. Complexity is where bugs hide. Prefer well-reviewed, widely used libraries over custom reinventions of standard components.
- Write thorough tests, including failure paths, edge cases and adversarial scenarios. Use property-based and fuzz testing for invariants such as "total supply equals the sum of balances".
- Use static analysis tools in continuous integration to catch known patterns.
- Document intended behaviour and invariants so reviewers can compare the code against intent.
- Test on a public testnet and consider staged launches with limits on value at first.
Preparing for an audit
An independent audit is valuable, not a stamp that guarantees safety. To get the most from one:
- Freeze the code before the audit begins, and share clear documentation, architecture notes and test instructions.
- Finish internal review and testing first. Auditors' time is best spent on hard problems.
- Allow time for fixes and a re-review. Findings are changed code, and changed code needs checking.
- Treat the report as one input. Audits sample risk at a point in time; they do not cover later changes.
Plan for incidents
Decide beforehand what you can do if something goes wrong: pausing mechanisms, rate limits, monitoring and alerts for unusual activity, and a communication plan. A circuit breaker that is itself a centralised power must be weighed honestly against your decentralisation claims.
What to take away
Keep contracts small, follow established patterns, test adversarially, protect keys, and get independent review before launching with real value. In this field, caution is cheaper than any fix.
MI Technologies Engineering
Software engineering team at Mohansh Innovations Pvt. Ltd.