
Faultline
Cross protocol liquidation contagion solver. Shock one protocol, see who else breaks.
Demo
About
Every lending protocol reports solvency in isolation. Aave shows Aave health factors. Compound shows Compound collateral ratios. Neither can see that a wallet leveraged against an asset on one protocol and a completely unrelated wallet leveraged against the same asset on another protocol are exposed to each other through the market, with no direct link between them. Faultline builds that cross-protocol exposure graph from live Aave v3 and Compound v3 positions, applies a price shock, recomputes health factors, liquidates breaches, and prices the forced collateral sale against real Uniswap v3 tick liquidity, iterating to a fixed point. It's a deterministic solver, not a model: every liquidation mechanic is a published protocol parameter, not a prediction. Live proof, not a synthetic example: a 43.67% oracle shock to wstETH liquidates a real Aave v3 wallet. Because the wstETH/WETH Uniswap v3 pool's liquidity falls to under 5% of its starting depth after just 4 ticks, that wallet's forced 196 wstETH sale alone crashes the pool price from $1,718.78 to $0.50, a near total collapse independently verified against Uniswap's own QuoterV2 contract. That crashed price liquidates a second wallet sharing no protocol and no debt asset with the first; its own forced sale drives the price down to a final $0.0028.
How it's made
Python/FastAPI backend, React + react-three-fiber frontend. No ML or LLM anywhere in the solve path, a hard rule from day one: liquidation mechanics are published protocol parameters, so we solve the system deterministically instead of predicting it. Data layer: live positions pulled from Aave v3's and Compound v3's own subgraphs via The Graph. Three of our first four subgraph IDs, pulled from search results with plausible display names, turned out dead or pointed at a completely different schema (Messari-standardized instead of the protocol team's own). We rebuilt the rule after that: every subgraph and contract ID now comes only from the protocol's own docs or README, verified live before use. Live balances were harder than expected to get right. Aave's UserReserve.currentVariableDebt and Compound's PositionAccounting.baseBalance both look like live numbers but are snapshots written only on a wallet's last interaction, understating real debt by however much interest accrued since. We derive live values ourselves (scaled amount times the reserve's current index), verified to four decimal places against DeBank on real wallets. A second bug: Aave's eMode liquidation thresholds only apply to assets that specific eMode category lists as eligible, and our first query for that list silently truncated to GraphQL's default first-100 rows across the whole deployment, dropping a real eligible asset that happened to land outside the window. That produced a plausible-looking wrong health factor before we caught it. Uniswap v3's tick-level liquidity is what prices the forced sale for real, not a flat slippage assumption. We implemented the actual v3 constant-liquidity-within-a-tick-range swap math ourselves and verified it against Uniswap's own deployed QuoterV2 contract via a raw eth_call, matching our simulation to four significant figures. That check caught a real off-by-one in our own tick-crossing boundary (excluding the pool's current tick from a downward walk, producing impossible negative liquidity). Pool selection is address-verified too, not sorted by raw reported TVL: Uniswap's subgraph doesn't sanitize that field, and a real query surfaced a spam pool claiming ~$1.1 trillion TVL. We only treat a pool as eligible if both its tokens are governance-listed reserve assets already pulled from Aave or Compound. The solver is a fixed-point cascade: apply a price shock, recompute every wallet's health factor across both protocols, liquidate anything under 1.0 using Aave's own close-factor and seizure formulas straight from LiquidationLogic.sol, price the resulting forced sale against the real pool depth above, repeat until nothing new breaches. The reference case is externally reconciled against DeBank on both wallets. The frontend renders that cascade live with react-three-fiber: a shock slider calls the real solver on every drag, not a replay, and a wallet-lookup feature runs the full pipeline against any address typed in. Its honest-failure path (an asset with no Aave oracle price configured) was verified against a real wallet holding COMP as Compound v3 collateral, since COMP isn't a listed Aave v3 reserve at all: the app reports "health factor not computed" by name instead of showing a number built on an incomplete price. Partner tech: The Graph is what makes composing two independent lending protocols' full position state into one cross-protocol query pattern possible at all, that's the actual product. Uniswap v3's tick-level depth is what turns "the price moved" into an actual, mechanically-priced forced sale, and is what surfaced the concentrated-liquidity finding that became the second half of the demo.