The staking-score pallet's audit-fix (commit 9afd99ae) added a dispute-window
model: receive_staking_details() only writes to PendingStakingDetails, and a
separate, permissionless finalize_staking_details() call promotes it to
CachedStakingDetails once DisputeWindow blocks have passed. No caller of that
promotion step existed anywhere in this bot (or anywhere else in this repo) -
confirmed live: Serok's and QaziM's real stake was correctly submitted to
Pending after the earlier NotRegisteredNoter/nonce fixes, but sat there
indefinitely with no path to ever reaching CachedStakingDetails/TrustScores.
Adds finalizeMaturedPending(), called once per periodic scan cycle (and once
at startup): scans PendingStakingDetails.entries(), finalizes whichever have
matured, batched into one utility.batchAll like the existing submission path,
routed through the same submitQueue so it can never race with a concurrent
submitStakingDetails call.
fullScan() processes accounts in concurrent batches of 10, and each account's
own data-gathering takes variable time, so multiple accounts' submitStakingDetails
calls landed close enough together to race on nonce assignment - the pool then
rejected most of them as duplicate-priority ("Priority is too low ... already in
the pool"). Observed live: 37 of 50 tracked accounts silently dropped in a single
scan cycle, including real accounts with genuine stake (Serok, QaziM) whose trust
scores stayed stuck at 0 as a result even after the separate NotRegisteredNoter
bond issue was fixed.
Routes every submission (both fullScan's batches and the live event listener)
through a single queue so exactly one signAndSend is ever in flight at a time,
in call order - data-gathering stays concurrent (unaffected), only the actual
chain submission is serialized.
processAccount() was comparing direct staking against cache BEFORE
checking pool data. This caused a flip-flop: when cache=121K and
direct=100K, the bot submitted 100K (missing pool). Next scan it
detected the difference and submitted 121K. Every 5 minutes, forever.
Worse, when direct staking went to 0, the bot submitted 0 which
caused the pallet to delete StakingStartBlock — permanently removing
the user from tracking and zeroing their trust score.
Fix: collect ALL staking data (direct + pool) first, combine into
a single total, then compare against cache. Also adds queryFailed
flag to prevent downgrading stake when pool RPC query fails.