Liquidation bot targeting xU3O8 (Uranium) collateral markets on Morpho Blue deployed on Etherlink.
tez_newPreconfirmedReceipts (WebSocket)
|
|-- Oracle events (ValueUpdate, PriceFeedUpdate)
| -> fetch oracle prices -> check all positions
|
+-- Morpho events (Borrow, Repay, SupplyCollateral, ...)
-> update local state -> check all positions
The bot maintains a full event-sourced replica of all position and market state from on-chain events. Health checks are pure local computation -- no per-borrower RPC calls.
- Rust bot -- event-sourced position tracking, oracle monitoring, liquidation detection and execution
main.rs-- orchestration, WS receipt loop, .env loadingstate.rs-- event-sourced State, position/market tracking, health checks, LIF computationexecutor.rs-- uranium-v3 swap encoding, transaction building/signing, liquidation executionrpc.rs-- Blockscout historical replay, oracle price fetching, market config loadingws.rs-- receipt stream types, oracle/Morpho log decodersconstants.rs-- contract addresses, market IDs, chain config
- Solidity contract (
src/MorphoFlashLiquidator.sol) -- flash liquidation via Morpho callback, swaps collateral for USDC through uranium-v3
6 xU3O8/USDC markets using RedStone + Pyth oracle feeds:
| LLTV | Oracle | Markets |
|---|---|---|
| 62.5% | Wrapper / Direct | #8, #11 |
| 77% | Wrapper / Direct | #7, #10 |
| 86% | Wrapper / Direct | #6, #9 |
- Rust 1.94+
- Foundry (forge, cast)
cargo build --release
forge install foundry-rs/forge-std
forge buildCreate a .env file at the project root:
ETHERLINK_WS_URL=wss://your-node/ws
ETHERLINK_RPC_URL=https://your-node
PRIVATE_KEY=0x...
LIQUIDATOR_CONTRACT=0x...
| Variable | Required | Description |
|---|---|---|
ETHERLINK_WS_URL |
Yes | WebSocket endpoint supporting tez_newPreconfirmedReceipts |
ETHERLINK_RPC_URL |
Yes | HTTP JSON-RPC endpoint |
PRIVATE_KEY |
No | EOA private key for signing liquidation txs |
LIQUIDATOR_CONTRACT |
No | Deployed MorphoFlashLiquidator address |
forge script script/Deploy.s.sol \
--rpc-url $ETHERLINK_RPC_URL \
--broadcast --private-key $PRIVATE_KEYCopy the deployed address into LIQUIDATOR_CONTRACT in .env.
cargo run --releaseWithout PRIVATE_KEY and LIQUIDATOR_CONTRACT, the bot runs in simulation mode: it monitors all positions, logs liquidation opportunities with expected profit, but does not send transactions.
With both set, the bot automatically executes flash liquidations when profitable opportunities are detected.
To see per-position health details:
RUST_LOG=morpho_liquidator=debug cargo run --release- Startup: queries market configs from Morpho, replays all historical events from Blockscout to reconstruct position state, fetches oracle prices
- Listening: subscribes to
tez_newPreconfirmedReceiptsWebSocket stream - On oracle price update: re-fetches oracle prices, checks all positions for liquidatability
- On Morpho event: updates local state (positions, market totals), re-checks health
- On liquidatable position: logs opportunity; if execution mode is enabled, encodes a uranium-v3 swap and sends a flash liquidation transaction
The MorphoFlashLiquidator contract enables zero-capital liquidations:
- Bot calls
liquidate()on our contract - Contract calls
morpho.liquidate()with callback data - Morpho transfers seized xU3O8 to our contract
- In
onMorphoLiquidatecallback: swap xU3O8 -> USDC via uranium-v3, approve Morpho to pull repayment - Profit (USDC) transferred to owner
morpho_blue_etherlink_markets.md-- full market data, oracle architecture, historical liquidationsliquidation_mechanics.md-- LLTV, LIF, bad debt, health check mathstrategy_decisions.md-- design decisions and tradeoffs