Designing a multi-chain liquidation engine
Three chains. One liquidation queue. Zero double-spends. Here's how we built it, and the corner case that almost shipped.
Three chains. One liquidation queue. Zero double-spends. Here's how we built it, and the corner case that almost shipped.

Liquidation across one chain is a solved problem. Aave does it. Compound does it. We did it on Base in our first month. Liquidation across THREE chains, where the same collateral can in principle be referenced on any of them, that's the problem that almost shipped a double-spend in our staging environment. Here's how we caught it, and what we built instead.
The double-spend that almost happened. User borrows USDC on Base against fAAPL collateral. Bridges fAAPL to Arbitrum. Borrows again against the same fAAPL. Bridges back. The naive design, a separate liquidation queue per chain, doesn't see the cross-chain leverage until it's too late. Health factor on each chain individually looks fine. Aggregate health factor is underwater.
We caught it in staging two days before launch. Reverted the chain-isolated design. Rebuilt around a single global queue.
Single global queue, multi-chain triggers. Every position contributes to a single global health factor, regardless of which chain holds the collateral. The queue lives on Base (where settlement is fastest), but liquidation triggers can fire from any chain. The flow:
The 12-hour grace. Most lending protocols liquidate the moment health drops below 1. We hold for twelve hours. Long enough for a borrower to wake up, see a webhook, and top up. We chose this because our typical user is a fintech with a real ops team, not a retail wallet. Speed of liquidation isn't the goal. Avoiding wrongful liquidation is. Health-factor webhooks fire at 1.3, 1.15, and 1.05, so the borrower sees three escalations before the timer starts.
Most lending protocols liquidate the moment health drops below 1. We hold for twelve hours.
Corner cases worth knowing:
What we got wrong the first time. We initially priced the liquidation auction by gas-on-Base alone. Liquidators on Arbitrum couldn't compete. We rewrote the auction to publish the rate on Base but accept claims from any chain, with the gas differential subsidized from the liquidation insurance fund. Net effect: more liquidator competition, tighter spread to the borrower, lower realized losses.
The lesson. When you build cross-chain, every assumption about local cost has to become a global one.