Blockchain Security & Smart Contract Best Practices background
Back to Journal
Application Security

Blockchain Security & Smart Contract Best Practices

Tashish RaiSinghani
August 8, 2026
19 min read

A technical guide to securing smart contracts, bridges and the infrastructure around them — the vulnerability classes that actually cause losses, why upgradeability trades one risk for another, and a testing methodology that goes beyond running a linter.

Blockchain security inverts two assumptions that most application security practice quietly depends on: that you can ship a fix, and that an attacker has to work to monetise a bug. On-chain, deployed code is often immutable, every function is public and permanently readable, and a successful exploit converts directly into transferable value in a single transaction.

The consequence is that the usual remediation loop does not exist. There is no hotfix window, no gradual rollout, and no way to un-send funds. Whatever is wrong with the contract when it is deployed is wrong forever, unless upgradeability was designed in beforehand — and upgradeability is itself one of the most reliably exploited features in the ecosystem.

This is a technical guide for engineers and security teams working on smart contracts, bridges and the infrastructure around them. It covers the vulnerability classes that actually cause losses, why bridges dominate the incident numbers, how upgrade patterns fail, what a serious testing methodology looks like beyond running a linter, and the operational controls that decide whether an incident costs you a function call or your treasury.

Key Takeaways
  • 01Immutability removes the remediation loop. Design-time correctness is the only control that scales.
  • 02The largest losses are not exotic cryptography — they are access control, bridges and key management.
  • 03Composability means your threat model includes protocols you did not write and cannot control.
  • 04Upgradeable proxies trade immutability risk for storage-layout and admin-key risk — a different problem, not a smaller one.
  • 05Automated tooling finds known patterns; economic and business-logic flaws still require manual review.

What Actually Differs From Conventional AppSec

Before the vulnerability classes, it is worth being precise about the structural differences, because they determine which controls are worth investing in.

PropertyConventional web applicationOn-chain contract
PatchingDeploy a fix within hoursImpossible unless upgradeability was built in
Source visibilityServer-side logic is privateBytecode is public; source usually verified and readable
Exploit economicsAttacker must monetise stolen dataExploit yields transferable value immediately
RollbackRestore from backupNone — settlement is final by design
Attack surfaceBounded by your own code and dependenciesExtends to any protocol yours composes with
OrderingRequest order is largely incidentalTransaction order is adversarially controllable

The last row deserves emphasis because it has no clean analogue elsewhere. Pending transactions sit in a public mempool before inclusion, and block producers choose ordering. Any logic whose outcome depends on being first is contestable by anyone willing to pay more in priority fees.

The Vulnerability Classes That Cause Losses

Public incident data has been fairly consistent for years: the large losses cluster in a small number of categories, and cryptographic breaks are not among them.

Reentrancy

The canonical class, and still live despite being understood since 2016. An external call hands control to an untrusted contract before the caller has finished updating its own state; the callee re-enters and observes stale values.

The durable fix is ordering, not a guard: apply checks, then effects, then interactions — validate inputs, write state, and only then make external calls. A reentrancy guard is a useful backstop but it does not protect against cross-function reentrancy, where the attacker re-enters through a different function that shares the same state, nor read-only reentrancy, where a view function returns inconsistent values mid-execution and a third protocol trusts it.

Access control failures

Consistently the largest loss category, and rarely subtle in hindsight: an initialiser left callable, a privileged function missing its modifier, an upgrade function reachable by anyone. Recurring specifics worth auditing explicitly:

  • Unprotected initialisers. Proxy implementations use an initialize() function rather than a constructor. If it is not disabled on the implementation contract itself, anyone can call it directly and take ownership of the logic contract.
  • tx.origin used for authorisation. It resolves to the externally-owned account that started the call chain, so any contract the user interacts with can act on their behalf. Use msg.sender.
  • Missing two-step ownership transfer. A single-step transfer to a mistyped address is unrecoverable. Two-step handover makes the mistake correctable.
  • Role sprawl. Roles granted during testing and never revoked, or an admin role that can grant itself every other role.

Oracle and price manipulation

If a contract reads a price, that price is an input an attacker can influence. Reading the spot reserves of an on-chain pool is the classic mistake: a flash loan lets an attacker borrow a large amount with no collateral, move the pool price within a single transaction, exploit whatever logic depends on it, and repay — all atomically, so it either succeeds profitably or reverts at no cost.

Mitigations are architectural: use time-weighted averages rather than spot values, prefer well-secured external oracle networks with multiple independent reporters, validate that reported values are fresh and within sane bounds, and require agreement across more than one source for high-value decisions. Note that a TWAP is harder to manipulate, not immune — it raises cost and duration rather than eliminating the attack.

A useful framing for flash loans: they are not a vulnerability, they are a capital requirement being removed. Any attack that was theoretically possible with a very large balance sheet is now possible for anyone with a gas fee. When threat modelling, assume every attacker is arbitrarily well capitalised for the duration of one transaction.

Transaction ordering and MEV

Because pending transactions are public before inclusion, an observer can insert their own transaction ahead of yours. For users this appears as sandwiching around swaps; for protocols it means any mechanism where being first confers advantage — auctions, liquidations, mint allocations — is exploitable.

Design responses include commit-reveal schemes so intent is not visible until it is settled, explicit user-supplied slippage bounds, batch or uniform-clearing auctions that remove the value of ordering, and deadline parameters so a stale transaction cannot be executed later at an unfavourable price.

Arithmetic and precision

Solidity 0.8 made arithmetic checked by default, which retired the classic overflow bug — but not the class. Overflow still occurs inside unchecked blocks, in assembly, and in casts that silently truncate. More common today is precision loss: integer division truncating toward zero, division performed before multiplication, or rounding that consistently favours the user over the protocol. Individually these look like dust; repeated in a loop or across many calls they drain value.

Signature handling

Off-chain signatures used for gasless approvals or meta-transactions introduce their own failure modes: replay across chains when the chain ID is not bound into the signed payload, replay across contracts when the verifying contract address is not bound, missing nonces allowing the same signature to be reused, and signature malleability where a second valid encoding of the same signature bypasses a naive replay check. Binding the domain — as structured signing standards do — addresses most of this, provided the domain separator is actually built from the current chain ID rather than cached at deployment.

Why Bridges Dominate the Loss Numbers

Cross-chain bridges have accounted for a disproportionate share of total value stolen — individual incidents including Ronin, Poly Network and Wormhole each reached the hundreds of millions of dollars. The concentration is structural rather than coincidental.

A bridge holds pooled assets on one chain and mints or releases representations on another, driven by a set of validators or a light-client proof. That produces three properties at once: a large, static pool of value; a validation step that must be trusted; and no single chain whose consensus secures the whole path. The recurring failure modes follow directly.

Failure modeMechanismControl
Validator key compromiseEnough signing keys obtained to authorise fraudulent withdrawalsDistributed key custody, HSMs, no single operator holding a quorum
Proof verification flawForged or malformed proof accepted as validFormal verification of the verifier; differential testing
Uninitialised stateA deployment step left a trusted root or implementation unsetPost-deploy invariant checks before funding the contract
Message replayOne valid message processed repeatedly across chainsPer-message nonces and processed-message registry
Unbounded outflowEntire pool drained in a single transactionRate limits, per-epoch withdrawal caps, circuit breakers

The last control is the most consistently under-implemented and the most valuable. A withdrawal rate limit does not prevent an exploit, but it converts a total loss into a bounded one and buys the hours needed to intervene.

Upgradeability: A Different Risk, Not Less Risk

Proxy patterns separate storage from logic: the proxy holds state and delegates execution to an implementation contract, so logic can be replaced. This solves the patching problem and creates three new ones.

Storage layout collisions. Because delegatecall executes implementation code against proxy storage, the two must agree on slot layout. Inserting a variable in the middle of an upgraded implementation shifts every subsequent slot and silently corrupts state. Append-only ordering, storage gaps in upgradeable base contracts, and namespaced storage all address this — but only if enforced, which means a layout-comparison check in CI rather than a code-review convention.

The admin key becomes the protocol. Whoever can upgrade can replace all logic with anything, including a function that transfers every asset. The contract's security is now bounded by the custody of that key, which is an operational problem, not a Solidity one. Multisig with a meaningful threshold, hardware-backed signers, and a timelock between proposal and execution are the baseline — the timelock in particular, because it gives users and monitoring a window to react to a malicious upgrade.

The uninitialised implementation. The logic contract itself is a deployed contract with its own storage. If its initialiser is not locked at construction, an attacker can initialise it, become its owner, and — in UUPS designs where the upgrade function lives in the implementation — potentially destroy or hijack it.

Testing Methodology That Goes Beyond a Linter

Running a static analyser and clearing its findings is a floor, not an audit. A defensible methodology layers techniques with different strengths, because each finds a different class of defect.

TechniqueFindsBlind to
Static analysisKnown unsafe patterns, missing modifiers, shadowingAnything requiring economic or protocol context
Unit testsIntended behaviour on paths you thought ofPaths you did not think of
Property-based fuzzingInvariant violations across random input sequencesInvariants you failed to state
Symbolic executionReachability of specific bad statesDeep state spaces; struggles to scale
Formal verificationMathematical proof of stated propertiesProperties outside the specification
Manual reviewBusiness-logic and economic design flawsExhaustive coverage; reviewer fatigue
Mainnet-fork testingComposability failures against real protocol stateFuture states of protocols you depend on

Two practical points. Fuzzing is only as good as your invariants — the work is writing properties such as "total supply always equals the sum of balances" or "no user can withdraw more than they deposited", and a fuzzer that finds nothing usually means the properties were too weak, not that the code is sound. And fork testing is where composability bugs surface, because your contract's behaviour against a mocked dependency tells you little about its behaviour against the live one.

The manual layer remains decisive for the same reason it does in conventional application security: no tool knows what your protocol is supposed to do economically. Our secure code review practice exists for precisely this class of finding, and the same principle we apply to web applications applies here — automation for breadth, humans for depth.

Off-Chain Is Still Where Many Breaches Start

It is easy to over-index on Solidity and neglect the conventional infrastructure that surrounds it, which is where a large share of real incidents originate.

  • Private key custody. Deployer keys, treasury keys and validator keys in environment variables, CI secrets or a developer laptop. Hardware-backed signing and multisig with geographically separated signers are the baseline for anything holding value.
  • Front-end compromise. Users interact through a web application. A compromised DNS record, a hijacked npm dependency, or a malicious script swaps the destination address in the transaction the user signs — the contract is untouched and the outcome is identical. This is a web application and supply chain problem.
  • Dependency risk. Contract libraries, build toolchains and front-end packages all enter your trust boundary. A per-release SBOM is what lets you answer "do we ship the compromised version" in minutes rather than days.
  • Node and RPC infrastructure. Nodes, indexers and RPC providers are ordinary cloud infrastructure with ordinary cloud security problems — over-privileged roles, exposed management ports, unmonitored logs.
  • Governance capture. Where token voting controls upgrades, acquiring voting power becomes an attack path. Flash-loan-assisted governance attacks are the on-chain version of this; quorum requirements, vote snapshots taken before proposal submission, and timelocks all raise the cost.

Operating Under Immutability

Because you cannot patch mid-incident, response has to be designed into the contract before deployment. The realistic controls are containment, not remediation.

Pause mechanisms let a defined role halt sensitive functions. They are genuinely useful and genuinely centralising — an honest design decision to make explicitly, with the pause role held under multisig and its existence documented for users rather than discovered during an incident.

Rate limits and circuit breakers bound the damage of any single exploit. A per-epoch withdrawal cap turns a drained treasury into a partial loss and creates the time window in which a pause is actually usable.

On-chain monitoring is the equivalent of SOC telemetry: alert on large or anomalous transfers, privileged function calls, upgrade proposals, oracle deviation beyond a threshold, and unexpected changes in protocol invariants. Detection has to be measured in seconds, since exploitation is a single transaction — feeding these signals into a monitored SOC with a defined escalation path is what turns an alert into an action.

Incident response also has an off-chain dimension: exchange and analytics contacts prepared in advance, a public communications position drafted before it is needed, and — for Indian entities — an understanding that CERT-In directions require specified incidents to be reported within six hours of detection. Our incident response practice covers that path, and our guide to six-hour reporting sets out the mechanics.

The Indian Regulatory Context

Teams building blockchain systems from India operate under the same obligations as any other technology business, and a few that are specific.

CERT-In's directions apply to virtual asset service providers alongside other entities, with incident reporting inside six hours and log retention obligations. The DPDP Act raises a genuine architectural question for anything writing personal data on-chain: immutability and the right to erasure are in direct tension, which is why identifying information generally belongs off-chain with only commitments or hashes written to the ledger. Deciding that after launch is not practical. Where a token or platform touches regulated financial activity, sector expectations from RBI and SEBI may also apply — our regulatory compliance practice covers the mapping.

Pre-Deployment Checklist

Before Mainnet
  • 01Enforce checks-effects-interactions ordering; treat guards as a backstop, not the control.
  • 02Enumerate every privileged function and its caller; confirm each has an explicit modifier.
  • 03Disable initialisers on implementation contracts at construction.
  • 04Add a storage-layout diff check to CI so an upgrade cannot silently shift slots.
  • 05Replace spot-price reads with TWAPs or multi-source oracles, with freshness and bounds validation.
  • 06Bind chain ID, contract address and a nonce into every off-chain signature.
  • 07Write protocol invariants explicitly and fuzz against them, not just unit-test happy paths.
  • 08Run mainnet-fork tests against live dependencies, not mocks.
  • 09Put upgrade and pause roles behind multisig plus a timelock, with hardware-backed signers.
  • 10Implement withdrawal rate limits so a single exploit is bounded.
  • 11Run post-deploy invariant checks before funding — confirm ownership, roots and implementation addresses are set.
  • 12Stand up on-chain monitoring and a rehearsed response path before value is at risk.

How Adayptus Helps

Related reading: our secure code review guide, threat modelling in the SDLC, and software bill of materials.

Frequently Asked Questions

Click any question to expand the answer.

QIs a smart contract audit the same as a penetration test?

No. A contract audit is white-box review of source and economic design before deployment, because after deployment there is usually nothing to fix. A penetration test exercises a running system and is still needed — for the dApp front end, the APIs, the node infrastructure and the key-management workflow, all of which are conventional targets. Most real programmes need both: audit the contracts, penetration test everything around them.

QDoes Solidity 0.8 mean overflow bugs are gone?

Checked arithmetic is now the default, which retired the classic unchecked-overflow bug, but not the class. Overflow still occurs inside unchecked blocks used for gas optimisation, in inline assembly, and in downcasts that truncate silently. In current code the more common arithmetic defect is precision loss — integer division truncating, division applied before multiplication, or rounding that consistently favours the user. Individually these look like dust; repeated at scale they drain value.

QAre flash loans a vulnerability we should block?

They are not a vulnerability and generally cannot be blocked meaningfully. A flash loan removes the capital requirement from an attack, so any exploit that was theoretically available to a very well-funded actor becomes available to anyone with a gas fee. The correct response is to remove the underlying dependency on manipulable state — most often a spot price read — rather than to detect borrowing. Assume every attacker is arbitrarily well capitalised for the duration of one transaction.

QShould contracts be upgradeable or immutable?

It is a trade, not an upgrade. Immutable contracts cannot be fixed but also cannot be subverted by an admin key. Upgradeable contracts can be patched but introduce storage-layout risk and, more significantly, make the upgrade key equivalent to full control of the protocol — anyone holding it can replace the logic with a function that drains everything. If you choose upgradeability, treat that key as the primary asset: multisig with a real threshold, hardware-backed signers, and a timelock so users and monitoring can react to a malicious proposal.

QWhy do bridges get exploited so often?

Structurally rather than coincidentally. A bridge concentrates a large pool of assets, depends on a validation step that must be trusted, and spans chains so no single consensus secures the whole path. That produces a high-value target guarded by validator keys or a proof verifier, and the recurring failures follow: key compromise, flawed proof verification, uninitialised deployment state, and message replay. The single most valuable control is a withdrawal rate limit — it does not prevent an exploit but converts a total loss into a bounded one.

QHow does the DPDP Act apply to data written on-chain?

It creates a direct architectural tension, because immutability and the right to erasure pull against each other — data written to a public ledger cannot practically be deleted. The workable pattern is to keep identifying information off-chain in systems you control and write only commitments, hashes or pseudonymous identifiers on-chain, so erasure is achievable by deleting the off-chain record. This has to be decided before launch; retrofitting it once personal data is on a public ledger is not practical.


Share this Insight
CybersecurityApplication SecurityAdayptus Intelligence
Tashish RaiSinghani

Tashish RaiSinghani

Blockchain, Web3 & RWA Tokenisation Specialist

Tashish RaiSinghani is a technology leader with 13+ years in the industry, ten of them building at the frontier of blockchain and Web3 and the last two focused on AI and automation. He helps businesses adopt blockchain-enabled infrastructure across tokenisation, smart contracts, DeFi and real-world assets, and has advised founders, family offices and C-suite leaders across the UAE, GCC, Europe and Southeast Asia. He has led delivery on 100+ engagements, structured SPVs for real-world asset tokenisation, and worked with regulators and compliance partners across DIFC and ADGM. He speaks regularly at Web3 and technology conferences across Asia and the Middle East, and splits his time between Delhi NCR and the UAE.

Connect on LinkedIn
Application Security

Get the Contract Reviewed Before It Is Immutable

Once deployed there is usually nothing to patch. Tell us what you are shipping and we will come back with scope, timeline, and an indicative quote — ideally before mainnet.

  • Manual review of logic, privilege model and economic assumptions
  • Findings automated tooling cannot reach
  • Front end, APIs and key handling covered too
  • Covered by NDA from the first conversation

Prefer email? [email protected]

Request a scoping call

No obligation. A senior consultant replies — not a sales sequence.

Your details stay confidential. No spam — a consultant replies, not a sales sequence.