Raviole Labs

A liquidation bot that survives its first bad block

The happy path of a liquidation bot fits in a weekend. Production is everything that happens when the chain, the RPC, or your own state machine lies to you.

hyperevmliquidationsbotsinfrastructuretrading

A liquidation bot is one of those projects where the demo takes a weekend and the production version takes a quarter. The happy path is trivial: scan positions, find one under the collateral threshold, call the liquidation function, collect the bonus. Every tutorial stops there. The chain does not.

Our Liquidator runs on HyperEVM, on our own capital, with no human in the execution loop. Here is what the happy path leaves out.

The RPC is not your friend

The first lie you discover is that “the current state of the chain” is a fiction served by whichever node you happen to be talking to.

  • A regular full node prunes history. The moment you need to reconstruct how a position got here, or replay a scan from three hundred blocks ago, you need an archive node. We learned to treat archive access as a hard dependency, not a nice-to-have.
  • Two RPCs will disagree with each other for a few blocks at a time. If your bot treats one provider as the truth, it will eventually act on a state that the rest of the network never saw. Read from more than one, and require agreement before you spend gas.
  • Latency spikes cluster exactly when you care most. Volatile markets mean busy nodes, busy nodes mean slow reads, and slow reads mean your health factor snapshot is stale precisely when positions are moving.

Bad blocks happen to good bots

Reorgs are rare until they’re not. The failure mode is nasty: you detected a liquidatable position in block N, fired the transaction, and block N stopped existing. Now your internal state says “liquidation in flight” for a position that is healthy again on the canonical chain.

The fix is unglamorous: the bot is an idempotent state machine. Every action is derived from chain state plus a monotonic log, never from what the bot remembers doing. Restart it mid-flight and it converges to the same place. Feed it the same block twice and nothing doubles. If the chain rewinds, the bot rewinds with it instead of arguing.

Simulate, then simulate again

No transaction leaves the bot without a local simulation first. Reverted liquidations are not neutral events: you pay gas to lose a race, and in a gas auction you can pay a lot to lose repeatedly. Simulation kills the obvious reverts (someone beat you by a block, the position moved back above water, the oracle updated mid-scan). It does not kill them all, so revert handling is a budget line, not an exception.

Dry runs with a control group

Before the bot touched real capital we ran it for weeks in shadow mode, and we ran it as an A/B pair: two configurations watching the same chain, logging what each one would have done. The point is not only “would it have worked”. It is “which assumptions diverge, and when”. Two dry runs that agree for ten days and split during one volatile hour teach you more than either run alone.

No human in the loop means more engineering, not less

“Fully automated” sounds like a reduction in effort. It is the opposite. A human operator is a walking exception handler; remove them and every exception needs a real answer. Our short list:

  • A kill switch the bot checks before every send, flippable from a phone in seconds.
  • Position caps so one bad assumption cannot size itself into a disaster.
  • Alerting on silence: the scariest bot is not the one throwing errors, it is the one that stopped logging and nobody noticed.

None of this is exotic. It is the same discipline that makes any unattended system boring, applied to software that spends money at machine speed. The desk is not for sale, but the lessons travel: if you are building anything that fires transactions without a human, build the boring parts first.