View Issue Details

IDProjectCategoryView StatusLast Update
0012124Talerwallet (iOS App)public2026-11-12 02:30
Reporterbot-cg [agent] Assigned ToResolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicatorCM_7  
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeverityminorReproducibilityhave not tried
Status newResolutionopen 
Summary0012124: OIM history: transaction detail shows a negative balance ("Balance: -5.00" for the first withdrawal) while an outgoing payment is pending (patch attached)
DescriptionSteps (iOS 27 simulator, fresh wallet, developer mode on so that OIM is available):

1. "Get demo cash": withdraw KUDOS 25.
2. Actions -> Send -> KUDOS 5.00 -> "Send KUDOS 5.00 now" -> Done. Nobody collects it, so it
   stays pending. Balances now reads "Balance: KUDOS 20.00 / Pending outgoing KUDOS 5.00".
3. Rotate to landscape (OIM), open the chest, tap the 20 note: the river history opens.
4. Tap the lake of the withdrawal.

The detail card for the withdrawal (screenshot attached) shows the 25 that came in, as a 20
and a 5 note, under the line

    Balance: -5.00

A wallet whose first and only incoming transaction was +25 never had a balance of -5.

Cause: OIMtransactions.computeData (TalerWallet1/Views/OIM/OIMtransactions.swift:255) starts
from balance.available and walks the transaction list backwards, undoing each transaction to
get "the balance before that transaction". The list it is given (TransactionsListView.swift:161)
held the withdrawal and a refresh, but not the pending send -- yet the 5 of the pending send are
already gone from "available". So 20 - 25 = -5, and with more history every point before the
pending transaction is too low by the pending amount. The chart presentation (oimChart) is
drawn from the same computed items.

Separately, and I may be misreading the intent: the number shown is the balance *before* the
tapped transaction (comment at line 301). Even with the arithmetic fixed the card for this
withdrawal would say "Balance: 0.00" next to "25 SLE"; the balance after it might be what a
user expects there.

Suggested fix for the negative value (not compiled): put the pending outgoing amount back
before walking backwards.

diff --git a/TalerWallet1/Views/OIM/OIMtransactions.swift b/TalerWallet1/Views/OIM/OIMtransactions.swift
index 444c057c..d88eacd7 100755
--- a/TalerWallet1/Views/OIM/OIMtransactions.swift
+++ b/TalerWallet1/Views/OIM/OIMtransactions.swift
@@ -238,7 +238,8 @@ struct OIMtransactions: View {
             cash.setTarget(.history)
             debugTick += 1
             if computedItems.isEmpty {
- computeData(from: history, balance: balance.available.value) // set chartMaxY, computedData
+ computeData(from: history, balance: balance.available.value, // set chartMaxY, computedData
+ pendingOutgoing: balance.pendingOutgoing.value)
             }
             showHistoryItems(oimChart ? Animation.talerDelay0 : 0.0)
         }
@@ -252,7 +253,7 @@ struct OIMtransactions: View {
 @available(iOS 16.4, *)
 extension OIMtransactions {
 
- func computeData(from history: [TalerTransaction], balance: Double) {
+ func computeData(from history: [TalerTransaction], balance: Double, pendingOutgoing: Double = 0) {
         /// Algorithm for the X value of the river of time
         /// Endpoint (now) is 0
         /// same day transactions have no space between them
@@ -261,7 +262,10 @@ extension OIMtransactions {
         /// month differs: add one more spacer
         /// year differs: add yet another spacer
         var historyItems: [HistoryItem] = [ HistoryItem(distance: 0, marker: .none, balance: balance, talerTX: nil) ]
- var balance = balance
+ // Money in a pending outgoing transaction has already left `available`, but that
+ // transaction is not in `history`. Put it back before walking backwards, or every
+ // earlier balance is too low by that amount (and the oldest one negative).
+ var balance = balance + pendingOutgoing
         var maxBalance = balance
         let calendar = Calendar.current // or do we need (identifier: .gregorian) ?
         var lastTx = calendar.dateComponents([.year, .month, .day, .weekOfMonth], from: Date.now)

Found by the UI fuzzing harness (scenario oim-history-detail, 2026-11-12).
TagsNo tags attached.
Attached Files

Required notifications

Address or linkRemarkAdded byNotifiedActions

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-12 02:30bot-cgNew Issue
2026-11-12 02:30bot-cgFile Added: oim-history-detail-negative-balance.jpg