PR: #32 feat/07-block-listener
File: crates/charon-scanner/src/listener.rs — BlockListener::run_once and run
Problem
BlockListener tracks no last_seen_block. After reconnect (up to 30s backoff), listener resumes from next block node pushes, no backfill of blocks produced during disconnect window.
BSC 3s block time × up to 30s backoff = up to ~10 blocks skipped silently per reconnect event. Each skipped block is a window where undercollateralised position could have been liquidated by competitor while Charon was blind.
Impact
Silent missed liquidation opportunities proportional to reconnect frequency × backoff duration. No metric, no log, no error surfaces the gap.
Fix
- Store
last_seen_block: Option<u64> on BlockListener.
- On re-entry to
run_once, after subscription established, call provider.get_block_number() to read current_head.
- If
last_seen_block < current_head - 1, iterate eth_getBlockByNumber from last_seen_block + 1 to current_head - 1 inclusive, emit synthetic ChainEvent::NewBlock entries.
- Emit
warn! with gap size for operator tuning.
May defer to follow-up issue but must be tracked. Current PR silently accepts data loss.
PR: #32 feat/07-block-listener
File: crates/charon-scanner/src/listener.rs —
BlockListener::run_onceandrunProblem
BlockListenertracks nolast_seen_block. After reconnect (up to 30s backoff), listener resumes from next block node pushes, no backfill of blocks produced during disconnect window.BSC 3s block time × up to 30s backoff = up to ~10 blocks skipped silently per reconnect event. Each skipped block is a window where undercollateralised position could have been liquidated by competitor while Charon was blind.
Impact
Silent missed liquidation opportunities proportional to reconnect frequency × backoff duration. No metric, no log, no error surfaces the gap.
Fix
last_seen_block: Option<u64>onBlockListener.run_once, after subscription established, callprovider.get_block_number()to readcurrent_head.last_seen_block < current_head - 1, iterateeth_getBlockByNumberfromlast_seen_block + 1tocurrent_head - 1inclusive, emit syntheticChainEvent::NewBlockentries.warn!with gap size for operator tuning.May defer to follow-up issue but must be tracked. Current PR silently accepts data loss.