Relayer recovery
How to diagnose the relayer’swritz-lend state, rebuild it from the chain, and start a new relayer from nothing.
Audience: whoever operates a relayer, including a third party running their own.
What the relayer holds
Positions and balances live inwritz-lend. Losing the relayer’s data loses no funds, and every position stays readable on chain. The relayer keeps three things of its own:
Without a cursor, the repay watcher starts from
WRITZ_LEND_DEPLOY_LEDGER, never from the chain tip. A lost database therefore delays a borrower’s release. It does not drop it, as long as that ledger is inside RPC retention.
Diagnose
Everything starts atGET /health:
Recover
Stop the relayer first. The command refuses to run while a live relayer on the same host holds the data directory.- Every
deposithas a position, and its status matches the replay:repay_fullgivesClosed,liquidategivesLiquidated, otherwiseActive. - For each supplier,
supplyminuswithdrawin scaled units equalsget_supply_position. - The sum over all suppliers equals
total_scaled_supply.
repay_full without a release is queued. Start the relayer: the sweep publishes those releases with no manual step. Existing pending rows are kept.
Start a new relayer
The rebuild needs public RPC access only, with no copy of anyone’s database, and a source of events never has to be trusted, because the chain proves or rejects the result.Limits
- RPC retention. Testnet RPC keeps 120,960 ledgers, 7 days. The rebuild reads from
WRITZ_LEND_DEPLOY_LEDGER, so past that window it stops withoutside RPC retention. - The archive does not replace the rebuild yet. The rebuild reads RPC only. Rebuilding from an archive file is tracked in #266.
- After each
writz-lendredeploy, updateWRITZ_LEND_IDandWRITZ_LEND_DEPLOY_LEDGERtogether. Rows of the old contract stay in the tables, and the sweep ignores them.
Past RPC retention
Until #266 ships, a relayer that lost its data after the window recovers the releases by hand. Positions and balances are still safe on chain.- List the
repay_fullevents from a source with longer history: a second RPC provider, Hubble or Galexie, or an explorer for a small gap. You need each position’s txid in internal byte order, as intopic[1]of the event. - Queue them.
handleRepayFullis idempotent, so a txid that already has its release is harmless, and the sweep does the rest.
- Start the relayer and confirm
get_release_psbtreturns a value for each one.
handleRepayFull also checks the position’s script against the protocol key, and refuses to co-sign on a mismatch.
Stuck releases
A stuck row has used its attempts. The usual cause is a deterministic failure, such as the script mismatch above after a protocol key change. Read the error in thepending_releases table: