fix(exchange-tx): mark stale pending Scrypt deposits as ok instead of failed#3777
fix(exchange-tx): mark stale pending Scrypt deposits as ok instead of failed#3777TaprootFreak wants to merge 1 commit into
Conversation
… failed Bank → Scrypt inbound transfers have never produced a chargeback in the historical record, so a Scrypt deposit that is still pending after a day means the funds did arrive and only the deposit status update was missed on Scrypt's side. The previous blanket flip to 'failed' kept the matching bank_tx unmatched in pending.toScrypt while Asset.balance.amount already reflected the funds via the FIX BALANCE stream — observed as a ~100k CHF overstatement in totalBalanceChf. Partition the stale set by exchange: Scrypt → 'ok' (funds confirmed by arrival history), everything else → 'failed' (unchanged default).
|
Closing in favor of a cleaner approach. WhyThis PR flipped stale pending Scrypt deposits to The right fix: don't manipulate Scrypt status at all in our cleanup. Pending stays pending until Scrypt itself flips it via the → Closing without merge. Replacement PR coming. |
|
Superseded by #3778 which fixes the root cause: the 24h stale deposit cleanup was racing against Scrypt's reconciliation window (can take >24h). Scrypt deposits now get 7 days before cleanup, allowing the sync to pick up the completed version. |
Problem
The hourly cron
cleanupStalePendingDepositsflips every still-pending DEPOSIT older than one day to'failed'— regardless of which exchange it belongs to. For Scrypt that is structurally wrong: Bank → Scrypt SEPA inbound transfers have never produced a chargeback /bank_tx_returnin the historical record. A stale pending row therefore does not mean the funds were rejected — it means Scrypt missed flipping the deposit status tookeven though the FIX BALANCE stream already shows the money on account.This is the root cause of the financial-log overstatement we saw on 2026-05-27 (~100k CHF). The matching OlkyPay
bank_tx#200493(30'006.71 EUR) andbank_tx#200504(80'006.76 EUR) couldn't pair with their Scrypt counterpartsexchange_tx#138339/#138379because the cleanup cron had already marked themfailed— so they kept counting inpending.toScryptwhile the same amount was simultaneously visible inAsset.balance.amount(FIX-stream live balance) → ~110k EUR / ~100k CHF double count.Fix
Partition the stale-deposit set in
ExchangeTxService.cleanupStalePendingDeposits:status = 'ok'(funds confirmed by arrival history; no chargebacks ever observed)status = 'failed'(unchanged behaviour)Two separate
repo.updatecalls, both guarded against empty ID lists. One-line English comment over the partition explains the why (no historical chargebacks on Scrypt inbound).Why this is the right place to fix it
PR #3775 (already open) tightens the FinancialDataLog receiver-filter so that even a
failedScrypt deposit gets matched against its bank_tx — that is a defensive aggregator-level patch. This PR fixes the root cause one layer down: stale Scrypt rows should never befailedto begin with. The two PRs are complementary (defense in depth) and can be merged in either order. After this PR, the filter in #3775 will mostly be there as a backstop.Risk
okthat were actually returned. Mitigation: monitorbank_tx_returnrows where the source bank_tx points to a Scrypt-type transfer. Today: zero such rows in the entire history.faileddefault — no behaviour change for them.Test plan
npm run lintnpm run format:checknpx tsc --noEmitnpx jest --testPathPattern="exchange-tx.service.spec"— 4/4 (new spec file: Scrypt → ok, non-Scrypt → failed, mixed batch partition, defined)okfailedScrypt deposits #138339 / #138379 will need a one-time manual fix-up (separate DB op) — this PR only changes go-forward behaviour