Twelve Smart Contract Vulnerability Archetypes, and How We Harden Against Them

May 15, 2026 • 10 min read

Smart-contract security has a strange shape compared to the rest of software, because the code is public, the money sits right next to the logic, and a single missed assumption can drain a contract in one transaction with no way to roll it back. Over years of building and reviewing Solidity we have come to think of the failures not as an endless list of one-off bugs but as a small set of recurring archetypes that show up again and again under different names, and we maintain an internal reference we call hss-security-lab that captures twelve of them, each with a working exploit, a verified patch, a written walkthrough, and Foundry plus Echidna fuzzing harnesses so the failure and the fix are both reproducible. This post walks through those twelve archetypes at a high level and explains how we harden against each one, written for engineers who want to understand the classes of failure rather than memorize a catalog of incidents.

Why we think in archetypes instead of incidents

The public history of decentralized finance is full of dramatic losses, and it is tempting to study each one as its own story, but the more useful move is to notice that almost every headline collapses into one of a handful of underlying patterns. A reentrancy drain in 2016 and a reentrancy drain in 2023 are the same archetype wearing different clothing, and an oracle that was manipulated through a flash loan is the same archetype whether the target was a lending market or a derivatives vault. When we built hss-security-lab we deliberately organized it around the pattern rather than the protocol, because an engineer who internalizes the shape of reentrancy can recognize it in code they have never seen before, while an engineer who only memorized one famous incident will miss the next variation. Each archetype in the lab carries a minimal vulnerable contract, an exploit that proves the loss, a patched version that closes it, and fuzzing harnesses that let us assert the patched invariant holds across thousands of randomized inputs rather than the three cases a developer happened to think of.

The first six archetypes

The most famous archetype is reentrancy, where a contract makes an external call before it finishes updating its own state, and the called contract calls back in to exploit the stale balance. The defense is old and well understood, which is to follow the checks-effects-interactions ordering so that state is written before any external call, to add a reentrancy guard where the ordering alone is not enough, and to treat every external call as a place where control can leave and return in a worse condition than it left. Our harness for this archetype asserts that the contract balance and the sum of accounted user balances stay consistent no matter how a malicious receiver behaves during a withdrawal.

The second archetype is the access-control gap, where a function that should be restricted to an owner or a role is left callable by anyone, or a privileged setter has no guard at all. These are unglamorous and extremely common, and the defense is to apply role-based access control consistently, to make the privileged surface small and explicit, and to write tests that assert an unauthorized caller is reverted rather than only testing the happy path where the authorized caller succeeds. A surprising share of real losses trace back to a single initializer or upgrade function that nobody remembered to lock down.

The third archetype is oracle manipulation, where a contract reads a price from a source that an attacker can move within a single transaction, most often by using a flash loan to distort a spot price on a thin liquidity pool and then borrowing or redeeming against the distorted number. The defense is to avoid manipulable spot prices for anything that gates value, to prefer time-weighted or multi-source price feeds with sanity bounds, and to treat any single-block price as untrusted by default. Our walkthrough for this archetype shows the same vault failing under a flash-loaned price and then holding firm once the feed is replaced with a bounded, slower-moving source.

The fourth archetype is arithmetic and rounding error, which changed character after Solidity began reverting on overflow by default but did not disappear, because precision loss, truncating division, and unit-of-account mismatches still let value leak a little at a time or let an attacker round a share calculation in their favor. The defense is to be explicit about decimals and scaling, to order operations so that division comes last, to watch for the share-inflation pattern where the first depositor can manipulate an empty pool, and to fuzz the accounting so that randomized deposit and withdrawal sequences never let a user extract more than they put in.

The fifth archetype is the unchecked or mishandled external call, where a contract sends value or calls into another contract and ignores the return value, or assumes a token transfer reverted on failure when in fact it quietly returned false. The defense is to check return values, to use safe-transfer wrappers for tokens that do not conform to the expected interface, and to design so that a failed external interaction cannot silently leave the contract in a half-finished state that an attacker can exploit on the next call.

The sixth archetype is front-running and transaction ordering exposure, which is native to a public mempool where anyone can see a pending transaction and pay to be sequenced ahead of it. This shows up as sandwiching a trade, racing a profitable liquidation, or claiming a reward that a victim already broadcast. The defense is partly design and partly mitigation, including commit-reveal schemes for sensitive actions, slippage and deadline controls on swaps, and an honest acknowledgment in the threat model that ordering is not something a contract can fully control, only something it can refuse to depend on for safety.

The next six archetypes

The seventh archetype is denial of service through an unbounded loop or a single failing participant, where a contract iterates over a growing array or pushes funds to a list of recipients and one reverting or gas-hungry entry freezes the whole mechanism. The defense is the pull-over-push pattern, where each party withdraws their own funds rather than the contract pushing to everyone in one transaction, and a discipline of never letting an externally controlled list grow without bound inside a function that must complete for the system to keep working.

The eighth archetype is the upgradeability and proxy hazard, where the same flexibility that lets a team patch a contract also creates storage-collision bugs, uninitialized implementation contracts, and the governance question of who can swap the logic out from under users. The defense is to use a well-reviewed proxy pattern, to lock the implementation contract's initializer so it cannot be seized, to lay out storage with collisions in mind, and to be deliberate about the admin keys, because an upgrade mechanism is a loaded weapon pointed at the contract's own users if it is not controlled carefully.

The ninth archetype is signature and replay weakness, where a contract accepts a signed message but fails to bind it to a nonce, a chain id, a deadline, or the specific contract instance, so a valid signature can be replayed on another chain, against another deployment, or a second time on the same one. The defense is the discipline that the well-known typed-data and permit standards exist to enforce, which is to include a domain separator, a nonce, and an expiry in everything you sign, and to verify all of them on the way back in rather than trusting that a signature alone proves intent.

The tenth archetype is bad randomness, where a contract derives a supposedly unpredictable value from block data such as a timestamp or a block hash that a miner or validator can influence or simply read ahead of time. Anything of value decided this way can be gamed by an actor who controls or observes the inputs. The defense is to source randomness from a mechanism designed to resist prediction, such as a verifiable random function, and to never use on-chain block data as a secret, because nothing on a public chain is secret and very little of it is truly unpredictable.

The eleventh archetype is the logic and state-machine flaw, the broad family where the individual operations are each safe but the contract allows a sequence the designer never intended, such as claiming twice, exiting before a lock expires, or entering a state that no transition was meant to reach. These are the hardest to catch with a checklist because they live in the protocol's own rules rather than in a known dangerous primitive, and the defense is invariant thinking, where we write down the properties that must always hold, such as the sum of all claims never exceeding the funded amount, and then fuzz the contract hard against those invariants so the tool discovers the illegal sequence we did not imagine.

The twelfth archetype is the gas and griefing edge case, where an attacker who cannot steal funds can still make a contract expensive or impossible to use, by forcing it into worst-case gas paths, by stuffing storage, or by exploiting an assumption that a transaction will always have enough gas to finish a cleanup step. The defense is to bound the work any single transaction must do, to avoid designs where one user can inflate the cost borne by another, and to test the failure modes under adversarial gas conditions rather than only under the comfortable assumption that everyone behaves and pays.

How the lab and our tooling fit together

The twelve archetypes are only useful if they change how we actually review and build, and the way we connect the reference to the work is through tooling that runs on every contract rather than living in a document nobody opens. We run a custom Solidity static analyzer we call hss-sniffer that has executed more than seven hundred and seventy-six production scans, and we pair it with Slither in the same workflow, because two analyzers with different strengths catch a wider band of the easy and medium findings than either one alone, and clearing the noise they surface frees human attention for the logic flaws that no static analyzer can reason about. The static layer is fast and broad, the fuzzing layer is slower and deeper, and the lab is the shared memory that keeps a lesson learned once from being forgotten the next time a similar pattern appears.

None of this is a claim that any contract can be made perfectly safe, because the honest position in this field is that security is a practice and not a finish line, and a clean static scan is the floor rather than the ceiling. What the archetypes give us is a structured way to be suspicious, a vocabulary for talking about failure before it happens, and a set of reproducible exploits and patches we can point at when we explain to a client or a contracting officer why a particular design choice is safe or why we pushed back on one that was not. We maintain hss-security-lab as a defensive and educational reference precisely because the same patterns keep returning, and a team that has already built, broken, and fixed each archetype in a controlled setting is far less likely to ship the live version of it.

If there is one thing we want an engineer to take from this, it is that the dangerous bugs are mostly not exotic, they are well-known classes hiding behind unique business logic, and the work of hardening a contract is largely the disciplined application of defenses that the field already understands. The reason losses keep happening is not that the defenses are secret but that they are easy to skip under deadline pressure, and the value of a lab full of working exploits and verified patches is that it makes the cost of skipping them concrete enough to take seriously every single time.

Need a defensive review of your contracts?

We are an SBA-certified SDVOSB software, AI, and cybersecurity firm with a working smart-contract security lab and a static-analysis practice spanning hundreds of production scans. If you want a careful, archetype-driven look at your Solidity before it ships, start a conversation and look at the work first.

Start a Contract Conversation View Capability Statement