The last gap in the risk engine: each edge's exposure weight is a real economic quantity. ~12 min.
Builds on: L16 · L21 · L39Anchor: LTV & vault allocation shareNew: weight = transmission coefficientNew: one quantity, three roles
L16 taught exposure as a weight that starts at 1.0 and is multiplied by each edge's weight (≤1) every hop —
and then took those per-edge weights as a given. This lesson closes that gap, and the answer is the most satisfying tie-up
in the whole risk strand: an edge's weight isn't a tuning knob. It's the real on-chain quantity that says what fraction
of a shock crosses that edge — and you've already met both of the important ones.
Your anchor: not all edges transmit equally
Intuitively, if a token a vault holds drops 50%, how much does the vault feel? It depends on the edge. If the vault put
30% of its capital there, it feels 30% of the shock. If a lending market accepts that token at 80% LTV, 80% of
the value transmits into borrow exposure. A pure "this pool contains that token" link, by contrast, transmits nothing on
its own. The BFS weight is exactly that per-edge transmission fraction — made of numbers you already know.
1 · The assignment — extractWeight, one switch
Every edge's weight is set at graph-load time by a single function (graph.goextractWeight),
switching on edge type:
switch edgeType {
case EdgeLendingCollateral: return g.extractLTV(r) // the market's LTV (continuous)case EdgeVaultAllocation: return sharePct(r) // the allocation fraction (continuous; %/100 if >1)case EdgePoolAsset, EdgeVaultAsset, EdgeWrapUnwrap:
return 0.0 // structural carriers — block propagationdefault: return 1.0 // e.g. HOLDS — full transmission
}
Edge type
Weight
Meaning
LENDING_COLLATERAL
LTV (≈0.0–0.9)
fraction of collateral value that becomes borrow exposure
VAULT_ALLOCATION
share_pct (0–1)
fraction of the vault's capital riding on that target
HOLDS & most others
1.0
full transmission — a direct value link, no attenuation
POOL_ASSET / VAULT_ASSET / WRAP_UNWRAP
0.0
structural carriers; topology only, transmit no exposure
So the "decay" is real, but selective
L16's multiplicative decay only actually attenuates on the continuous edges. Cross a HOLDS (×1.0) and the
weight is unchanged; cross a VAULT_ALLOCATION at 30% (×0.30) and it drops to a third; hit a structural carrier (×0.0)
and propagation dies on the spot (w = cur.weight × 0 < minDelta → dropped). The genuine hop-by-hop fade L16
described comes from the LTV and allocation-share edges; the 1.0 edges pass it through; the 0.0 edges stop it.
2 · One quantity, three roles (the tie-up)
Here's why this is satisfying. The two continuous weights aren't new numbers invented for the BFS — they're the exact
same quantities you've already studied driving other parts of the engine:
And the LTV extraction is literally the same multi-field dance: extractLTV reads ltv / lltv / cf / bcf /
max_ltv / lt — the very field-name priority that, when it was a single hardcoded ltv, caused L21's $2.85B Spark
phantom. One correctly-extracted economic coefficient, reused three ways.
A worked hop
Token T fails. A vault allocates 40% to a market that lends against T at 75% LTV; T is in a pool too (structural):
1.0T fails
×0.75LENDING COLLATERAL (LTV)
0.75market
×0.40VAULT_ ALLOCATION (share)
0.30vault
×0.0POOL_ ASSET
—stops
The vault carries weight 0.30 of T's shock; RiskUSD = 0.30 × vault price (L16). The pool link transmits nothing.
3 · Direction is also per-edge-type
One companion detail: alongside the weight, edgeDirection[edgeType] sets which way risk flows. Most edges
propagate dst_to_src (e.g. LENDING_COLLATERAL, ORACLE_DEP, VAULT_ALLOCATION — risk flows from the
thing-depended-on back to the dependent); HOLDS and a few others flow src_to_dst. The loader stamps each edge
into the Forward adjacency in its risk-direction, so the BFS (L16) just walks Forward and the direction is already
baked in. Weight says how much crosses; direction says which way.
L16 is now fully closed — and so is the risk engine
Exposure propagation had exactly one unexplained input: the per-edge weight. It's the edge's economic transmission
coefficient — LTV, allocation share, 1.0 for direct value links, 0.0 for structural carriers — sourced from the same
on-chain quantities that drive extractable (L20/L21) and concentration (L39). Bardoscia contagion (L16) modelled not with
invented constants but with the protocol's own risk parameters. There is no remaining black box in the risk math.
Check yourself
1. What is an edge's exposure-BFS weight, conceptually?
2. What weight does a LENDING_COLLATERAL edge carry?
3. A VAULT_ALLOCATION edge's weight is the allocation share_pct. Where have you seen that exact quantity before?
4. POOL_ASSET / VAULT_ASSET / WRAP_UNWRAP edges get weight 0.0. What's the effect on propagation?
5. L16 said exposure decays multiplicatively each hop. Given the weights, where does the genuine attenuation come from?
6. extractLTV reads ltv / lltv / cf / bcf / max_ltv / lt rather than a single ltv field. Why the multi-field read?
7. A vault allocates 40% to a market lending against T at 75% LTV. What exposure weight does the vault carry to T's failure?
8. Besides the weight, edgeDirection is set per edge type. What does it determine?
↳ Ask your teacher
Try: "Show me extractPropagationLTV's field-priority order." ·
"Why does HOLDS transmit at 1.0 but POOL_ASSET at 0.0 — aren't both 'value-bearing'?" ·
"How is edgeDirection used to build Forward vs Reverse adjacency?" ·
"Could a malformed share_pct > 1 inflate exposure, and what guards it?" ·
"Where do DebtRank / Impact reuse this same weighted graph?"
What you can now do
State that an edge's exposure weight is its real transmission coefficient, assigned per edge type by extractWeight.
Give each weight: LTV for lending collateral, share for vault allocation, 1.0 for direct links, 0.0 for structural carriers.
Explain that the multiplicative decay is real but selective — only continuous edges attenuate.
Connect LTV and allocation share to their other roles (L20/L21 extractable, L39 HHI) — one quantity, three uses.
Distinguish weight (how much crosses) from edgeDirection (which way), and explain the Forward-adjacency stamp.
Risk-math deep dive: complete
Three lessons closed the bucket: node_risk_score's governance noisy-OR and its stubs (L38), HHI concentration and
its accounting (L39), and now the BFS edge weights (L40) — which also closed L16's last deferral. Combined with L6, L13,
L16, and L19–L23, every number the risk engine produces is now derivable to its source, with no parameter left
unexplained.