Model A — the other-asset attack — and the first trace where the system fights back. ~15 min.
Builds on: L50 · L20 · L27Anchor: borrow against inflated collateralNew: the other-asset extractable + a defense
Third trace, and it completes the at_risk attack taxonomy: NAV redemption (L50, model B), a pool drain
(L51), and now a DeFi-borrow oracle attack (L20 model A). It's the richest of the three — it hinges on an edge the
oracle bridger had to derive, its extractable is in a different asset than the one attacked, and for the first time
in a trace, the system actively refuses to propagate the attack instead of just modelling and recording it.
The scenario
Focus token FT is listed as collateral in an Aave-style lending market M, which prices it via a Chainlink
oracle O. An attacker who can influence O publishes an inflated price for FT, then deposits a little FT and
borrows as much USDC as the fake valuation allows — and walks off with the USDC. They never needed to "steal FT."
The counter-intuitive core (from L20)
In a NAV attack (L50) or a pool drain (L51) the attacker takes the focus token itself. Here they take a
different asset — the USDC they borrow against mispriced FT collateral. So this cell's extractable is measured
in borrowed-asset terms, not FT supply: min(at_stake × LTV, market_other_borrowable_usd) — the borrow the fake
price unlocks, capped by the actual cash sitting in M (L20/L21). That OTHER-asset framing is what makes the oracle path
the gnarliest of the three.
The stage — an edge the bridger had to derive
At build-time the system assembled exactly the structure model A needs — and one piece is special:
What was built
How
FT is collateral in M (LENDING_COLLATERAL), with M's LTV + borrowable depth
enrichment / lending refresher (L24)
O prices FT (ORACLE_DEP from FT)
oracle/NAV discovery (L26)
M consumes O (ORACLE_DEP from M) — model A's eligibility
the oracle bridger, transitively, in pure Cypher (L27)
O's admin (its owner()), role severity
admin discovery (L25)
No bridger, no cell
L20's model-A eligibility is "a market that both consumes the oracle and holds FT as collateral." A market doesn't
declare its oracle dependency on-chain — the oracle bridger (L27) derived M-[:ORACLE_DEP]->O transitively from
FT's. That single derived edge is what makes the per-(O, M) attack cell exist. This trace is where L27 finally earns
its keep — neither L50 nor L51 needed it.
So at_risk already emits the model-A cell (emitOracleCellsViaToken, L20): target_role=oracle, scoped to market M,
at_stake = max(M.avail, M.maxBorrow) for FT, extractable = min(at_stake × LTV, M.other_borrowable),
outcome_class = trigger (an oracle attack triggers a mispricing — the L23 invariant that oracle cells are always
"trigger"). The standing exposure is quantified before anything happens.
The trace — the bad price is published at block N
1
O publishes the inflated FT price.L1 · L7
the aggregator emits an AnswerUpdated-style log (a new round); block N is ingested and streamed.
2
Filter — O is monitored.L1 · L2
O was added to the monitored set as FT's price oracle (L26), so the update survives the drop.
3
The system reads the new price — and checks it.L26
the NAVLink/price refresher reads O's latestRoundData() and runs validateCrossSource: compare against an independent price (defillama / known_peg).
RPC-economy: a paced refresher read, not a per-block chain hit.
4a
DEFENSE — the wrong price is rejected.L26 · L29
if the inflated price diverges past tolerance from the independent source, the refresher refuses to stamp it (nav_oracle…/cross-source mismatch) and the NavlinkFreshnessVerifier alarms. The bad value never enters the graph; the fake collateral valuation is never trusted.
First trace with an active defense — the system doesn't just model/record, it declines to propagate a value it can disprove.
4b
…but if it slips through (subtle, or no independent price).L20 · L1/L7
a within-tolerance manipulation, or an asset with no cross-source price, passes the gate. The attacker then deposits FT and borrows USDC against it → Borrow events on M; those flow through ingest and update the lending edges.
5
at_risk recompute — the model-A cell was already right.L19 · L20 · L21
the per-(O, M) cell had quantified extractable = min(at_stake × LTV, other_borrowable) in USDC terms; the realized borrow matches the modelled surface. The cell collapses into FT's value pool (max-within, no double-count with any token-admin cell).
Combinator: the OTHER-asset extractable is bounded by two ceilings (LTV-borrow ∧ market cash), then MAX-pooled.
6
Exposure propagates — through the bridger's edge.L16 · L27 · L40
exposure-BFS flows FT's shock along M-[:ORACLE_DEP]->O and onward to any vault that allocates to M (the bridger's path C), each hop weighted (LTV / allocation share).
The contagion travels the very edges L27 derived.
7
Rule fires; alert delivered.L12 · L45 · L15 · L18
an oracle-/admin-risk rule on FT trips (conditionMet) → fire-once → AlertEvent with alert_type reflecting the oracle trigger → dedup by msg-id → notifier → human.
8
Self-checking confirms coverage.L29 verify_at_risk_oracle_coverage / verify_at_risk_oracle_extractable confirm every consuming market that holds FT has its oracle cell — the audit layer that guarantees model A didn't miss a (O, M) pair.
Three reactions, not two
L50/L51 had two clocks (preventive + recording). This trace adds a third: defense. The cross-source check (step
4a) can stop the attack from ever entering the graph — the system disproves the manipulated price against an independent
source and refuses it. When that net has nothing to compare against (step 4b), it falls back to quantifying and recording.
Preventive · defensive · recording — the full repertoire.
The taxonomy, now fully traced
L50 · NAV (model B)
L51 · pool drain
L52 · borrow (model A)
attacker takes
the focus token (redeem supply)
the focus token (sweep pool)
a different asset (borrowed USDC)
extractable bound
min(at_stake, exit_v2) / honest-$0
exit liquidity, self-subtract venue
min(at_stake × LTV, other_borrowable)
key upstream
NAV registry binding (L26)
HOLDS pool cell (L19 Path 3)
oracle bridger ORACLE_DEP (L27)
system reaction
preventive + record
preventive + record
preventive + defend + record
You've now traced all three at_risk attack shapes
Redeem-the-supply, drain-the-pool, borrow-the-other-asset — the three ways value leaves a focus token, each with its own
cell construction, its own extractable ceiling, and its own upstream edge. Hand yourself any DeFi exploit and you can now
classify it into one of these shapes and trace it. That's the working mastery the whole journey was for.
Run it yourself
1. In a model-A oracle attack, what does the attacker actually walk away with?
2. The model-A cell's extractable is min(at_stake × LTV, other_borrowable). Why these two terms?
3. The per-(O, M) attack cell exists only because of a derived edge. Which, and from where?
4. At step 4a the system does something L50 and L51's traces never did. What?
5. When does the cross-source defense (step 4a) fail to stop the attack, falling through to 4b?
6. The model-A cell carries outcome_class = trigger. What's the rule behind that (L23)?
7. At step 6 the FT shock propagates to a vault that allocates to market M. Along which edge, and why does it exist?
8. Across L50–L52, what distinguishes the three at_risk attack shapes?
↳ Ask your teacher
Try: "Trace a depeg cascade so exposure-BFS multi-hop is centre stage." ·
"What's the cross-source tolerance, and how is it tuned?" ·
"If O is owner()'d by a Safe, how does L22 expansion change the cell?" ·
"Quiz me cold: classify a random real-world exploit into a model and trace it." ·
"How does verify_at_risk_oracle_coverage decide a (O,M) pair is missing?"
What you can now do
Trace a model-A oracle attack end to end and explain the other-asset extractable.
Compute the extractable bound min(at_stake × LTV, other_borrowable) and say what each term limits.
Explain why the per-(O, M) cell depends on the bridger's transitive ORACLE_DEP edge (L27).
Describe the cross-source defense (4a) and when it falls through to record-only (4b).
Classify any DeFi exploit into one of the three at_risk shapes and trace it.
Three traces, one instrument
NAV redemption, pool drain, oracle borrow — the at_risk attack taxonomy is now fully traced, and you've seen the system's
whole repertoire: quantify ahead (preventive), refuse the bad value (defensive), and record what lands (recording). The
trace is no longer a lesson; it's a tool you carry.