Smart Contracts

Smart Contract Security: Common Vulnerabilities and Defensive Patterns

Working reference for the vulnerability patterns Red Asgard audits surface most often – reentrancy, integer overflow, access-control breaks, oracle manipulation – with defensive patterns and audit-finding examples.

Red Asgard Research·Updated May 2026

How smart contracts fail

A smart contract fails because of a gap between what the code does and what the developer thought it did. The gap is usually in one of about ten places. The catalog below names them.

We hunt these patterns first because they are the patterns the attackers hunt first. The cost of a missed bug is paid in user funds. The cost of finding it in audit is one extra week of work.

This paper focuses on the EVM ecosystem (Solidity). The methodology applies across the 37-chain scope; ecosystem-specific patterns for Solana, Move, Cairo, CosmWasm, and TON are covered in their own papers.

The vulnerability catalog

Reentrancy, single-contract and cross-protocol. Single-contract reentrancy is well understood, but not gone. New variants appear through receiver hooks (ERC-777, ERC-721), multicall patterns, and read-only reentrancy across composed protocols. We start anywhere an external call happens before accounting, authorization, or invariant settlement.

Read-only reentrancy specifically. A protocol that reads state from another protocol mid-update can observe an inconsistent intermediate state. Curve, Balancer, and other AMMs have been used as the vulnerable read source; the bug class survived years of audits because the standard reentrancy-guard check does not catch a callable function that only reads.

Integer overflow and underflow. Solidity 0.8+ checks arithmetic by default; this category dropped sharply with that change. It re-appears in unchecked blocks (which developers add for gas reasons) and in inline assembly (which never had the check). The audit question is: every unchecked block, why is it safe?

Access-control errors. The right person can do the right thing; the wrong person can also do it. The catalog: tx.origin instead of msg.sender; missing onlyOwner; modifier never applied to the function it gates; role granted to a contract that later upgraded; constructor-set permissions that survive on the implementation of a proxy.

Oracle manipulation. Any state-changing decision that reads a spot price is exposed to price manipulation. The mitigation is a TWAP or a Chainlink-style aggregated feed; the failure is implementing either of them wrong.

Signature replay. A signed message that the contract accepts can be replayed across chains, across contracts, or across calls if the nonce or chain-id scheme is missing or implemented wrong. Permits, meta-transactions, and order-book settlement are all signature-heavy and all see this class.

Initialization mistakes in upgradeable systems. Constructors do not initialize proxy storage. Initialization must run exactly once against the proxy, and implementation contracts must disable direct initialization. The failure modes are documented in our upgradeable-contracts paper.

Denial of service through gas exhaustion. A function that iterates over a user-supplied array, or pays out to a list of recipients in one call, can be forced into a state where one recipient's revert or one entry's gas cost prevents the function from ever completing. The mitigation is pull-over-push: each recipient claims, no one is forced to pay everyone.

Selfdestruct semantics changed. Solidity 0.8.21+ deprecated selfdestruct for new contracts (Cancun behavior change). Protocols that depend on contract destruction for cleanup or for state-reset patterns need to migrate. The audit question on any legacy code that uses selfdestruct is whether the post-Cancun semantics still satisfy the original assumption.

Front-running and MEV exposure. Any operation whose outcome depends on transaction ordering and that pays out a measurable reward is exposed. DEX trades are intentional; liquidations, refunds, rebases, and time-locked governance executions are accidental.

Flash-loan price manipulation. A specific MEV pattern. A reader-and-writer that reads spot price and writes state in the same transaction is exploitable through a flash loan if the spot price source is manipulable. The DeFi paper covers this in depth.

Storage layout collisions in proxies. A proxy is one contract's storage being interpreted by another contract's code through convention. Convention is enforced by humans. Storage layout changes across upgrades, gaps that close, slots that move, all fail at runtime.

ERC-4626 vault economics. Vault contracts have produced a recurring bug class around share-price manipulation through direct token transfers, virtual-share inflation attacks, and donation attacks that corrupt the share-to-asset ratio. We hunt for inflation patterns on every vault audit; OpenZeppelin's ERC-4626 implementation defends against the most common variants but does not eliminate the class.

Account abstraction surface (ERC-4337). Account-abstraction contracts ship a UserOperation validation path that runs in a constrained environment but still touches storage. Bundler trust, paymaster authorization, and validation-vs-execution-phase separation each introduce bug classes specific to AA. Increasingly important through 2025–2026 as more wallets adopt the pattern.

The catalog is not exhaustive. The catalog is what we hunt first.

Defensive patterns

A protocol that defends against the catalog above relies on a small set of patterns.

Checks-effects-interactions. Order matters. Check the invariants; update the state; then call out. Reentrancy attacks are about calling out before state is updated. Reversing the order closes the attack class on most paths.

OpenZeppelin's audited contracts. Standard implementations of ERC-20, ERC-721, AccessControl, ReentrancyGuard, and proxy patterns. Not perfect, but the bugs found in them are public and fixed. Implementing each of these from scratch is a way to ship a new bug.

Reentrancy guards on every external-call function. One storage slot per call; closes the reentrancy attack class on the guarded function. The audit question is: which functions are guarded, and why are the unguarded ones safe?

Pull-over-push for distribution. A recipient claims their share; the contract does not push to them. Removes the "one recipient's revert blocks everyone" denial-of-service.

TWAPs for price-sensitive logic. Reading spot price from a DEX inside a transaction that changes state is a flash-loan vulnerability. Reading a multi-block TWAP from an oracle moves the cost of manipulation from "one transaction" to "many blocks of capital."

Reentrancy-safe receiver design. Where a contract must receive tokens that trigger callbacks (ERC-777, ERC-721), the receiver path is guarded explicitly.

Time-locks on privileged operations. Pause, upgrade, parameter changes. Every privileged operation runs through a time-lock that the user community can react to.

Snapshotting for governance. Vote weight is taken at proposal-creation block, not at execution. Flash-loan voting attacks fail under this pattern.

None of these defenses are novel. They are the patterns that audited protocols have used for years. The protocols that get exploited usually missed one.

Tools that catch what

Tools are filters upstream of manual review. They catch the patterns they were written to catch.

Slither catches reentrancy on the simplest paths, integer-underflow markers, missing event emissions, dangerous low-level calls, and access-control patterns it has a detector for. Output is a triage list.

Mythril and Halmos do symbolic execution and bounded symbolic testing. They find paths that violate properties the auditor states. The cost is reviewer time to express the properties.

Echidna and Medusa fuzz invariants. The invariants come from the trust-boundary diagram. They find counterexamples where the invariant fails.

Certora Prover proves stated properties against the verified contract model and bytecode assumptions. Used surgically; expensive.

Tools do not replace manual review. Tools eliminate the obvious so the reviewer can focus on the non-obvious. The testing paper covers the layered strategy.

What we audit for

We audit for the catalog above, in the order above, on every smart contract engagement. The methodology is in our auditing methodology paper.

The deliverable is the audit report: findings ranked by severity, reproducible evidence for every Critical and High, residual-risk section, fix-confirmation pass after patches.

What clean means

A clean audit means we did not find what we looked for. It is not insurance. New attack patterns emerge; new variants of old patterns emerge. The catalog above is current as of this writing and will need to grow.

Tags
smart-contractssolidityvulnerabilitiesauditing