Skip to content
all posts
Writeup 4 min read

The five ways DeFi protocols get drained

Most post-mortems describe the same five failure families: access control, accounting drift, oracle failure, liquidation assumptions and integrations. Here is what each one looks like in code.

A dark editorial cover with the title "The five ways DeFi protocols get drained" in monospace type and an amber rule.

Most post-mortems read like they were written from the same template. A protocol loses money, the team pauses the contracts, and the retrospective names a single function that should have checked something. The function is different every time. The family is usually one of five.

I review contracts by following value through the system, not by reading functions in alphabetical order. These are the five families I look for first, and the questions that expose them.

Nothing here is a statistic. The failure families come from my own review work, and every incident I mention is cited to the team’s post-mortem or the on-chain transaction, not folded into a total.

1. Access control

The most expensive bugs are still the simplest: a function that moves money and does not check who called it.

It shows up in a few shapes. A missing modifier on an admin setter. An initializer that can run twice, or run on the implementation instead of the proxy. An upgrade authority that ends up with the deployer EOA because the handover was never tested. A onlyOwner path that skips the accounting the normal path enforces.

What to check:

  • Every state-changing function has an explicit caller check, or a written reason it does not need one.
  • Roles cannot grant themselves a stronger role without a timelock or a multisig.
  • Initializers are guarded and cannot be front-run at deploy.
  • The upgrade path is tested against the previous deployed version, not only a fresh deploy.

2. Accounting drift

Two contracts can each be correct and still disagree about who owns what. That gap is where value leaks.

The classic forms: a share price computed with the wrong denominator, rounding that always favours the user, a vault that assumes its token balance only changes through deposit and withdraw, or a fee-on-transfer token that makes the contract’s balance smaller than its books. A donation to the vault can move the share price just as effectively as a bug.

What to check:

  • Rounding direction is documented and always favours the protocol.
  • The share price cannot be manipulated by a first depositor or a direct transfer.
  • Fee-on-transfer and rebasing tokens are supported explicitly or rejected at the door.
  • An invariant test proves the sum of user balances equals the contract balance after every operation.

3. Oracle failure

A correct contract with a bad price is a broken contract.

Oracle failures are rarely exotic. A feed goes stale and liquidation keeps using the last price. A new asset has eight decimals and the code assumes eighteen. A spot price is read from a pool that an attacker can move in the same transaction. An RWA market stays open while the underlying market is closed. An L2 sequencer goes down and the protocol keeps accruing interest against a price nobody can trade.

What to check:

  • Every feed has a staleness check with a documented heartbeat.
  • Decimals and scaling are handled per feed, including non-18-decimal assets.
  • Spot prices are not used where a manipulation-resistant TWAP or a median is required.
  • Market-hours and sequencer-downtime behaviour is defined, not left to chance.

4. Liquidation assumptions

Liquidations are supposed to protect the protocol. In practice they are a set of assumptions about behaviour under stress.

The incentive has to survive a gap. The liquidation path has to work on the collateral it is meant to seize. The oracle has to be live exactly when the market is moving fastest. If any of those assumptions fails, the protocol accumulates bad debt while the code reports a healthy position.

What to check:

  • The liquidation bonus still pays after slippage and gas in the worst realistic market.
  • The liquidation path does not revert on the collateral it needs to liquidate.
  • Bad debt is accounted for, not hidden in a share price.
  • ADL, pause and emergency paths are tested, not only described.

5. Integration assumptions

Every external contract is a trust boundary. Most protocols treat integrations as libraries.

A token callback re-enters a half-updated state. A bridge message is replayed on another chain because the nonce is not bound to the source. A router passes arbitrary calldata to a target the protocol trusts. A pool returns a value the caller never checks. A governance executor calls an arbitrary address because “only the timelock can call it”.

What to check:

  • Every external call assumes the callee can be malicious or can call back.
  • Token transfers use a safe wrapper and handle non-standard return values.
  • Cross-chain messages include the source chain and a replay guard.
  • Delegatecall and arbitrary-call targets are immutable or behind a timelock.

Why the checklist is not the review

Each of these families has a checklist. The checklist catches the hygiene: the missing modifier, the unchecked return, the stale feed. It does not catch the system bug, the one that appears when two correct components meet.

That is the part an audit is for. You read the protocol as a whole, write down the invariants it believes, and then try to break them in the order an attacker would. The list gets you to the interesting part faster. It does not replace it.

If you want the list, it is here: the pre-audit checklist. If you want the review, the smart contract audit page has the scope, the process and the fixed fee. The cost guide explains what moves the number.

The short version: most drains are not a clever new bug. They are one of five old ones, in a system that was sure it had checked.

Related

Self-serve

Hugin is built

The intercepting proxy and scanner I build and use daily — one Rust binary, local-first, no account. Community is free; Pro is €10/month when you need the offensive tier.

Get Hugin