Join our Newsletter — 33% off our NHI Course
Home› Guides› Smart Contract Security Guide
Guide Fraud Prevention

Smart Contract Security Guide

← All guides
By Lalit Choda, NHI Mgmt Group Updated 17 October 2026 15 min read
On this page

A smart contract is public, holds value directly and usually cannot be patched in place, so one flaw or one stolen admin key can empty it within a single block. The OWASP Smart Contract Top 10 (2025) cites over $1.42 billion lost across 149 incidents in its source data, and several of the largest recent losses came not from code bugs but from the privileged keys that control upgrades and parameters. This guide is for protocol engineers, security leads, auditors and the risk teams that approve on-chain deployments. It explains the main vulnerability classes at a conceptual level, the current taxonomies, a secure development lifecycle with named tools, the limits of audits, upgrade governance and admin key control, and monitoring. It is defensive: there is no exploit code and no attack walkthrough.

Key takeaways

  • Use a maintained taxonomy. The SWC Registry states it "is no longer actively maintained"; use the OWASP Smart Contract Top 10 (2025) and the EEA EthTrust Security Levels for current coverage.
  • Specify before you test. Fuzzing, invariant testing and formal verification are only as good as the properties you write, so the specification is a security artefact.
  • Audits are a snapshot. SEAL notes that external reviews are "not a certificate of safety"; pair them with internal testing, bug bounties and re-review of every change.
  • Admin keys are the biggest single risk. Upgrade, pause and parameter roles should sit behind a multisig and a timelock, with signers who verify calldata on trusted hardware.
  • Plan for the bad day. Circuit breakers, a scoped pause role, live monitoring and a rehearsed response limit losses when prevention fails.

Why smart contracts need their own discipline

  • Everything is visible: the Solidity documentation warns that "Everything you use in a smart contract is publicly visible", including variables marked private. Secrets, randomness seeds and pending logic cannot be hidden on-chain.
  • Value sits in the code: a contract is both application and vault. Solidity's guidance is to "Restrict the amount of Ether (or other tokens) that can be stored in a smart contract" so a bug has a ceiling.
  • Composability: contracts call other contracts, including untrusted ones, and borrow prices and liquidity from them. A safe contract can become unsafe when a dependency changes.
  • Adversarial ordering: pending transactions are visible before inclusion, and block builders choose ordering, so front-running and sandwich trades (forms of maximal extractable value, MEV) are part of the threat model on the Ethereum Virtual Machine (EVM) and similar chains.
  • Immutability cuts both ways: immutable code cannot be fixed; upgradeable code can be fixed, or replaced by whoever controls the upgrade key.

Vulnerability classes

The table maps each class to the OWASP 2025 ID, the legacy SWC ID where one exists, and the primary defence. Descriptions are conceptual.

ClassWhat goes wrongOWASP SC 2025 / SWCPrimary defence
Access controlA privileged function (mint, withdraw, upgrade, set price) is callable by the wrong party, or authorisation relies on tx.originSC01 / SWC-105Explicit role checks on every state-changing function, msg.sender not tx.origin, role review in audit scope
Price oracle manipulationThe contract trusts a price that can be moved, such as an AMM spot price in a thin poolSC02Manipulation-resistant feeds, liquidity checks, staleness and deviation bounds
Flash loan amplificationUncollateralised loans repaid in the same transaction give anyone temporary large capital to exploit a pricing or governance weaknessSC07Design as if every caller has unlimited capital for one transaction; avoid same-block price and vote reads
Logic errorsReward, interest, rounding or liquidation maths does not match the intended economicsSC03Written specification, invariant tests, economic review
Missing input validationUnchecked parameters, addresses or array lengths reach sensitive logicSC04Validate at entry points; reject zero addresses and out-of-range values
ReentrancyAn external call lets the callee re-enter before state is updatedSC05 / SWC-107Checks-Effects-Interactions, reentrancy guards, pull-based withdrawals
Unchecked external callsReturn values of low-level calls are ignored, so failures pass silentlySC06Check every return value; prefer audited wrappers
Integer overflow and underflowArithmetic wraps or truncates, often inside unchecked blocks, casts or older compiler versionsSC08 / SWC-101Solidity 0.8+ checked arithmetic, minimal unchecked use, safe casting
Insecure randomnessBlock data used as a random source can be predicted or influencedSC09Verifiable randomness or commit-reveal schemes
Denial of serviceUnbounded loops exceed the block gas limit, or one failing recipient blocks everyoneSC10Bounded iterations, pull payments, isolate per-user failures
Signature replayA signed message is accepted twice, on another chain or by another contractSWC-121Nonces, deadlines and an EIP-712 domain with chainId and verifyingContract
Unsafe delegatecall and upgradesCode runs in the caller's storage context, or a proxy is left uninitialised or misconfiguredSWC-112Audited proxy patterns, locked implementations, storage layout checks

Notes on the classes that cause the most damage

  • Access control: ranked first in OWASP's 2025 list. Use a role-based model (OpenZeppelin's AccessControl is a common choice) with one role per power rather than a single owner, deny by default, emit events on every role change so monitoring can see it, and add a unit test that calls each privileged function from an unprivileged account and expects a revert. Initialisers, migration helpers and "temporary" admin functions are frequent gaps.
  • Reentrancy: Solidity's rule is that "Interaction with other contracts should be the very last step in any function." It applies to any external call, not only Ether transfers, and across multiple contracts that share state. The 2016 DAO loss made the class famous; it still recurs in cross-contract and read-only forms.
  • Integer issues: since Solidity 0.8.0 arithmetic is checked by default and "overflows will always revert", but the documentation warns "do not assume you are protected from overflow bugs": a revert can lock a contract, and unchecked blocks wrap silently.
  • Signature replay: EIP-712 states that it "does not include replay protection". Its domain fields (name, version, chainId, verifyingContract, salt) separate signing contexts, but the application must still reject a repeated message or make the action idempotent, which in practice means nonces and expiry.
  • Oracles: Chainlink's own guidance tells integrators to "make sure that the asset has a sufficiently healthy level of liquidity", to detect "stale, delayed, missing, or unexpected values", and that developers "remain responsible for ensuring that protocol risk parameters are configured appropriately". Time-weighted averages, multiple sources and bounds reduce, but do not remove, manipulation risk.
  • MEV and front-running: protect users with slippage limits and deadlines on trades, commit-reveal for auctions, and avoid logic that rewards being first.

Taxonomies and standards

ReferenceStatusUse it for
SWC Registry (SWC-100 to SWC-136)Not maintained; "has not been thoroughly updated since 2020" and may contain "crucial omissions"Legacy IDs still printed by older tools and audit reports
OWASP Smart Contract Top 10 (2025)Current; built from SolidityScan Web3HackHub data and 2024 loss reportsPrioritising risks and training
EEA EthTrust Security LevelsMaintained; the SWC Registry points reviewers to itAudit and review requirements
Smart Contract Security Verification Standard (SCSVS)Maintained developer guidance, also referenced by the SWC RegistryDeveloper checklists and requirements

A secure development lifecycle for contracts

The general secure development practices in NIST SP 800-218 (SSDF) apply. What changes is the weight on pre-deployment assurance, because post-deployment fixes are slow, public and sometimes impossible.

  1. Specify: write down what the system must always and never do: solvency, access rules, supply limits, who can pause or upgrade. These become invariants.
  2. Threat model: list privileged roles, external dependencies (oracles, tokens, bridges) and economic assumptions. SEAL's audit preparation guide asks teams to document "all access control roles and their privileges" and known centralisation risks.
  3. Build on audited components: use well-reviewed libraries for tokens, access control, reentrancy guards and proxies rather than custom code.
  4. Analyse statically on every commit: Slither, "a Solidity & Vyper static analysis framework written in Python3", ships 100 detectors and its own GitHub Action for code scanning; its vars-and-auth printer lists which variables each function writes and what authorisation it checks.
  5. Test, then fuzz: unit and integration tests, then property-based fuzzing. Foundry's invariant testing "verifies properties that should always hold true, regardless of the sequence of actions taken", with runs and depth set under [invariant] in foundry.toml and handlers to constrain inputs. Echidna, a Haskell fuzzer, checks echidna_ properties or assertions and keeps a coverage-maximising corpus.
  6. Formally verify critical paths: ethereum.org lists Certora Prover, the Solidity SMTChecker, solc-verify and KEVM as program verifiers, and Mythril, hevm and Manticore for symbolic execution. Its stated limits: verification needs "considerable manual labor" and a poor specification lets real bugs through.
  7. Freeze and review: fix the commit, run an external audit, fix findings, and have fixes re-reviewed.
  8. Deploy with guard rails: deposit caps, rate limits and a pause role, raised gradually as confidence grows.
TechniqueExample toolsFinds wellMisses
Static analysisSlither, Semgrep smart contract rulesKnown patterns: reentrancy shapes, unchecked calls, missing access modifiersEconomic and business logic flaws
Symbolic executionMythril, hevm, ManticoreReachable assertion failures on bytecode pathsDeep multi-transaction state; slow on large systems
Fuzzing and invariantsFoundry, EchidnaSequences that break stated invariantsAnything without a property written for it
Formal verificationCertora Prover, SMTChecker, KEVMProof that specified properties always holdProperties nobody specified; off-chain and oracle behaviour
Manual auditIndependent reviewersDesign flaws, logic errors, privilege risksCode changed after the review; time-boxed coverage

Audits, contests and bug bounties

  • Prepare properly: SEAL's preparation guide asks for a plain-English overview, flow diagrams, design decisions, known issues, external dependencies and role lists, plus prior runs of Slither, Mythril and Semgrep. A narrow, defined scope beats a sprawling one.
  • Know the limits: SEAL states that "External reviews are not foolproof and cannot guarantee absolute security." An audit covers one commit for a fixed time; deployment scripts, configuration, admin keys and later upgrades are often out of scope unless you add them.
  • Choose the format: a private audit suits a complex protocol needing deep design review; a competitive audit contest adds breadth for a fixed window; a standing bug bounty covers live code continuously. Larger protocols use all three; a small team shipping a simple contract may start with one private audit and a bounty sized to the value at risk.
  • Disclose safely: publish a security contact and disclosure policy, and consider SEAL's Safe Harbor framework, which it describes as "Legal protection for whitehat rescues during active exploits."

Upgrades, timelocks and admin keys

Upgradeable contracts trade immutability for fixability. OpenZeppelin documents three main proxy patterns: Transparent (upgrades via a separate ProxyAdmin), UUPS (upgrade logic in the implementation, where _authorizeUpgrade "must be overridden to include access restriction") and Beacon (one upgrade changes every proxy pointing at the beacon). Proxy state sits in ERC-1967 storage slots to avoid layout collisions.

  • Lock implementations: OpenZeppelin warns "Avoid leaving a contract uninitialized" and recommends calling _disableInitializers in implementation constructors.
  • Do not mix patterns: a UUPS implementation behind a Transparent proxy "might allow non-admins to perform upgrade operations".
  • Check storage layout: compare layouts between versions in CI before any upgrade is proposed.
  • Put a timelock in front of power: OpenZeppelin's TimelockController separates Proposer, Executor, Canceller and Admin roles, and says the timelock should hold "any funds, ownership, and access control roles", with any setup admin told to "promptly renounce the role". SEAL adds that "Timelocks and review windows are intentional controls, not friction to bypass."
  • Keep emergency powers narrow: a pause or cancel role may bypass the delay, but should not be able to upgrade code or move funds.

Where identity fits: privileged admin keys

Owner, upgrader, pauser, minter and oracle-setter roles are privileged non-human identities, and they are usually held by human signers on ordinary laptops. Radiant Capital's incident update (16 October 2024, approximately $50M) describes a DPRK-linked actor compromising "multiple developer devices", so that interfaces showed benign data "while malicious transactions were signed in the background", despite simulation and payload checks. Radiant reports that Mandiant attributed the attack to UNC4736.

  • Inventory roles on-chain: list every address holding each role, what it can do, and who controls it. Remove deployer and test accounts after launch.
  • Use two-step ownership transfer: patterns such as OpenZeppelin's Ownable2Step require the new owner to accept, preventing transfer to a mistyped address.
  • Harden signers: dedicated signing devices, hardware wallets with clear signing, calldata decoded on a second independent tool, and same-day removal of departing signers. See the Privileged Access Management Guide and the Cryptographic Key Management Guide.
  • Protect the deployment path: deployer keys and CI secrets that publish contracts or front-ends are as sensitive as the admin key. See the CI/CD Pipeline Identity Security Guide.

Decision guidance

ChoicePrefer A whenPrefer B when
Immutable vs upgradeableLogic is simple, well tested and holds high value long termThe protocol is young or complex and must be fixable, with timelock and multisig control
Transparent vs UUPS proxyYou want upgrade logic outside the implementation and accept higher deployment costYou want cheaper proxies and can guarantee every implementation keeps a restricted _authorizeUpgrade
Short vs long timelockParameters need frequent tuning and a security council can cancelUsers need time to exit before any change to code or custody takes effect
Single audit vs layered assuranceSmall, standard contract with modest valueNovel mechanism or high total value locked: multiple audits, contest, formal verification and bounty

Monitoring and incident response

  • Monitor: role grants and revocations, ownership transfers, upgrade proposals and executions, pauses, large withdrawals, oracle deviations, failed-transaction spikes and changes in dependent protocols.
  • Circuit breakers: Chainlink describes breakers that react to "stale prices, drastic price changes, or prices approaching a predetermined min/max threshold" and a manual kill switch if an upstream contract is found vulnerable.
  • Fail-safe mode: Solidity's guidance suggests a mode the contract enters automatically when self-checks fail.
  • Respond: pause, alert users, coordinate with exchanges and stablecoin issuers, engage help such as SEAL 911, preserve evidence, and redeploy or upgrade only through the normal review path. See the Incident Response Guide.

Metrics that show it is working

  • Share of state-changing functions covered by at least one invariant or fuzz property.
  • Open high and medium findings from static analysis and audits, and median days to fix.
  • Privileged roles held by an externally owned account rather than a multisig or timelock (target zero).
  • Timelock delay on upgrades against the time users need to exit.
  • Minutes from a role change or large outflow on-chain to human acknowledgement.
  • Days from a signer leaving to removal from every multisig.

Rolling out the programme

  1. Baseline: inventory deployed contracts, proxies, roles and holders; run Slither across all repositories and triage findings.
  2. Move power behind controls: transfer owner and upgrade roles to a multisig, then to a timelock the multisig controls. Test on a testnet or fork first; keep the previous owner able to accept back until the new path has executed a no-op change.
  3. Add property testing: start with five to ten core invariants such as solvency and supply; run them in CI, then expand.
  4. Introduce monitoring in alert-only mode: tune thresholds for several weeks before wiring alerts to automated pausing; revert automated actions to alert-only if they fire on legitimate activity.
  5. Layer external assurance: audit before each major release, launch a bounty, and drill the pause and upgrade runbooks twice a year.

Common failure patterns

  • Treating the audit as the finish line: code, configuration or dependencies change after review and nobody re-reviews.
  • A single key with god-mode: upgrade, mint and withdraw rights held by one externally owned account, often the deployer.
  • Trusting a spot price: reading an AMM pool price in the same transaction that acts on it.
  • Uninitialised proxies and implementations: leaving initialize callable by anyone.
  • Signers who trust the screen: as at Radiant and the Bybit hack, where tampered interfaces led to approval of malicious transactions.
  • Stale signer and role lists: former staff or decommissioned services still holding roles.

Framework mapping

AreaNIST CSF 2.0NIST SP 800-53 Rev. 5CIS Controls v8.1ISO/IEC 27001:2022 Annex A
Secure development and testingPR.PSSA-11, SA-11(1), SA-1516.128.25, 8.28, 8.29
Upgrades and change controlPR.PSCM-316.18.32
Admin keys and rolesPR.AAAC-5, AC-6, SC-125.4, 6.58.2, 8.24
Bounties and disclosureID.RARA-5(11)16.28.8
Monitoring and responseDE.CM, RS.MASI-4, IR-417.48.16, 5.26

Practitioner checklist

  • Map findings and training to the OWASP Smart Contract Top 10 (2025), not the unmaintained SWC Registry.
  • Write a specification with explicit invariants before code freeze.
  • Run Slither in CI and fail builds on new high-impact findings.
  • Run Foundry invariant tests or Echidna properties for solvency, supply and access rules.
  • Formally verify the highest-value paths where the team can write sound specifications.
  • Re-review every change after an audit, and run a bug bounty sized to value at risk.
  • Hold upgrade, pause and parameter roles in a multisig behind a TimelockController; renounce setup admin roles.
  • Lock implementations with _disableInitializers and check storage layout before upgrades.
  • Use nonces, deadlines and an EIP-712 domain for every signed message.
  • Monitor role, upgrade and outflow events 24/7 with a drilled pause runbook.

Standards and references

Related NHI Mgmt Group resources: Privileged Access Management Guide · Cryptographic Key Management Guide · Vulnerability Management Guide · Open Source Security Guide · Crypto Scam and Drainer Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Written and reviewed by Lalit Choda, NHI Mgmt Group. Last updated 17 October 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org