From 8671fd3c89a0851360494d8ac192725c10b086ae Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Thu, 23 Jul 2026 22:07:20 +0200 Subject: [PATCH] feat(log): show signed pending legs in the financial-log breakdown (#4349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The four pending leg fields (fromKraken/toKraken/fromScrypt/toScrypt) dropped negative values from the FinancialDataLog JSON breakdown, because getJsonValue defaults returnNegativeValue to false. Since #4348 lets the unfiltered legs net negative, a negative leg silently disappeared from the breakdown while still subtracting from totalPlusPending, so the visible components no longer summed to the total. Pass returnNegativeValue=true (matching this file's convention for plusBalanceChf/minusBalanceChf/totalBalanceChf/plusBalance/minusBalance/totalPlus/liquidity) so signed legs stay visible and the breakdown reconciles — restoring the diagnostic used to spot equity phantoms. --- .../log/__tests__/log-job.service.spec.ts | 41 +++++++++++++++++++ .../supporting/log/log-job.service.ts | 28 +++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/subdomains/supporting/log/__tests__/log-job.service.spec.ts b/src/subdomains/supporting/log/__tests__/log-job.service.spec.ts index b641ccbdec..c2c9e95469 100644 --- a/src/subdomains/supporting/log/__tests__/log-job.service.spec.ts +++ b/src/subdomains/supporting/log/__tests__/log-job.service.spec.ts @@ -1928,5 +1928,46 @@ describe('LogJobService', () => { // Per-leg unfiltered clamps must not exist — they would leave total = 881163.51 and skip this path. expect(verboseSpy.mock.calls.some((call) => String(call[0]).includes('totalPlusPending < 0'))).toBe(true); }); + + it('keeps signed negative unfiltered pending legs in the log when net totalPlusPending is positive', async () => { + // Active Yapeal/EUR custody asset (sellable keeps it in the asset-log reduce). + const yapealEurAsset = createCustomAsset({ + id: 8005, + blockchain: Blockchain.YAPEAL, + dexName: 'EUR', + sellable: true, + }); + + // Net-positive pending so the pending object is built (not aggregate-clamped away): + // fromKrakenUnfiltered = +900000 (unmatched Kraken WITHDRAWAL to bank) + // toKrakenUnfiltered = -100000 (unmatched Kraken DEPOSIT from bank) + // Net totalPlusPending = +800000 → pending is present; toKraken must stay negative for observability. + const fromKrakenWithdrawalTx = createCustomExchangeTx({ + id: 5003, + type: ExchangeTxType.WITHDRAWAL, + currency: 'EUR', + method: 'Bank Frick (SEPA) International', + address: 'YAPEAL AG', + amount: 900000, + }); + const toKrakenDepositTx = createCustomExchangeTx({ + id: 5004, + type: ExchangeTxType.DEPOSIT, + status: 'ok', + currency: 'EUR', + method: 'Bank Frick (SEPA) International', + address: yapealEUR.bic.padEnd(11, 'XXX'), + amount: 100000, + }); + setupUnfilteredKrakenNetting([fromKrakenWithdrawalTx, toKrakenDepositTx]); + + const assetLog = await service['getAssetLog']([yapealEurAsset]); + + // Pending must exist (net positive — not clamped away by totalPlusPending < 0). + expect(assetLog[yapealEurAsset.id].plusBalance.pending).toBeDefined(); + // Load-bearing: negative leg must be present and signed (getJsonValue returnNegativeValue=true). + expect(assetLog[yapealEurAsset.id].plusBalance.pending.toKraken).toBe(-100000); + expect(assetLog[yapealEurAsset.id].plusBalance.pending.fromKraken).toBe(900000); + }); }); }); diff --git a/src/subdomains/supporting/log/log-job.service.ts b/src/subdomains/supporting/log/log-job.service.ts index 77867dddd8..b7961638c2 100644 --- a/src/subdomains/supporting/log/log-job.service.ts +++ b/src/subdomains/supporting/log/log-job.service.ts @@ -1048,10 +1048,30 @@ export class LogJobService { exchangeOrder: this.getJsonValue(exchangeOrder, amountType(curr)), bridgeOrder: this.getJsonValue(bridgeOrder, amountType(curr)), fromOlky: this.getJsonValue(pendingOlkyYapealAmount, amountType(curr)), - fromKraken: this.getJsonValue(useUnfilteredTx ? fromKrakenUnfiltered : fromKraken, amountType(curr)), - toKraken: this.getJsonValue(useUnfilteredTx ? toKrakenUnfiltered : toKraken, amountType(curr)), - fromScrypt: this.getJsonValue(useUnfilteredTx ? fromScryptUnfiltered : fromScrypt, amountType(curr)), - toScrypt: this.getJsonValue(useUnfilteredTx ? toScryptUnfiltered : toScrypt, amountType(curr)), + fromKraken: this.getJsonValue( + useUnfilteredTx ? fromKrakenUnfiltered : fromKraken, + amountType(curr), + false, + true, + ), + toKraken: this.getJsonValue( + useUnfilteredTx ? toKrakenUnfiltered : toKraken, + amountType(curr), + false, + true, + ), + fromScrypt: this.getJsonValue( + useUnfilteredTx ? fromScryptUnfiltered : fromScrypt, + amountType(curr), + false, + true, + ), + toScrypt: this.getJsonValue( + useUnfilteredTx ? toScryptUnfiltered : toScrypt, + amountType(curr), + false, + true, + ), } : undefined, // monitoring: errors.length