View Issue Details

IDProjectCategoryView StatusLast Update
0012088Talerwallet (iOS App)public2026-11-11 01:42
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 
Summary0012088: iOS deposit shows no fee / net amount before the deposit is made: checkDeposit is never called (patch attached)
DescriptionThe iOS deposit amount screen never asks wallet-core what the deposit will cost: checkDeposit is not called, so the fee line under the amount stays empty and the user commits the deposit ("Deposit KUDOS 1.00", which calls createDepositGroup directly) without having seen the fee or the amount the bank account will receive. The view already contains the pieces -- state checkDepositResult / feeAmount / feeStr, a "+ %@ fee" label with colour logic, a computeFeeDeposit() stub whose checkDeposit call is commented out and which has no caller, and WalletModel.checkDeposit4711() in Model+Deposit.swift with no caller. Android computes the fee and net amount live (DepositManager.checkDepositFees, DepositAmountComposable.kt:222-247) and the web wallet shows a review with "Bank account receives", "Wallet spends" and coin / wire / renewal fees before "Confirm deposit".

Screenshots (tripwire KUDOS exchange :8445, deposit of KUDOS:1 to an x-taler-bank account):
- before-ios-1.png: amount ク1.00, empty fee line.
- after-ios-1.png: with the patch, "+ ク0.00 fee" under the amount.
- before-web-1-review.png (reference): web review with all fee rows.
Limitation: the test exchanges charge no deposit or wire fee for these amounts, so the shots can only show a zero fee; on deployments with wire fees the difference is the actual amount.

Note: on current origin/master the iOS deposit amount screen is reached only after the 0011964 error (getMaxDepositAmount decode). Both iOS images were built from origin/master plus a reproduction of the 0011964 decode fix (attached as ios-base-11964-decode-fix.patch, for reference). The patch of this issue does not depend on it and applies to origin/master.

Code (taler-ios origin/master 674b7915, TalerWallet1/):
- Views/Actions/Banking/DepositAmountView.swift:30-33 (state never assigned), :59-79 (computeFeeDeposit: checkDeposit commented out, no caller), :128-134 (feeLabel(feeStr), always empty)
- Views/Actions/Banking/DepositAmountAction.swift:79-93 (button -> createDepositGroup), :144-151 (paytoUri not passed to DepositAmountView)
- Model/Model+Deposit.swift:197-207 (checkDeposit4711, unused)

Patch (attached, ios-deposit-fee-preview.patch): DepositAmountAction passes paytoUri to DepositAmountView; computeFeeDeposit() (replacing the stub) calls checkDeposit4711 for a non-zero amount within the available amount, stores the result and sets feeAmount = totalDepositCost - effectiveDepositAmount and feeStr; a .task(id: amountToTransfer.value) runs it on every amount change. The existing "+ %@ fee" label and colours then show it. No new strings.

Verification: wc-ios build (Release, iOS 27.2 simulator); after-ios-1.png.

Assisted by LLM
Steps To Reproduce1. Fund the wallet from https://tripwire.taler.net:8445/ (or any exchange with deposit fees), add an x-taler-bank account.
2. Actions -> Deposit -> account -> amount (requires the 0011964 fix to get here without an error).
3. No fee is shown before "Deposit KUDOS …"; wallet-core's checkDeposit is never called.
TagsNo tags attached.
Attached Files
before-ios-1.png (242,752 bytes)   
before-ios-1.png (242,752 bytes)   
after-ios-1.png (247,760 bytes)   
after-ios-1.png (247,760 bytes)   
before-web-1-review.png (127,534 bytes)   
before-web-1-review.png (127,534 bytes)   
ios-deposit-fee-preview.patch (3,406 bytes)   
diff --git a/TalerWallet1/Views/Actions/Banking/DepositAmountAction.swift b/TalerWallet1/Views/Actions/Banking/DepositAmountAction.swift
index a10d7759..f3e7b344 100644
--- a/TalerWallet1/Views/Actions/Banking/DepositAmountAction.swift
+++ b/TalerWallet1/Views/Actions/Banking/DepositAmountAction.swift
@@ -147,6 +147,7 @@ struct DepositAmountAction: View {
                          amountLastUsed: $amountLastUsed,
                        amountToTransfer: $amountToTransfer,
                         amountAvailable: amountAvailable,
+                               paytoUri: paytoUri,
                          depositStarted: depositStarted
                 ) {
                     if let balance {
diff --git a/TalerWallet1/Views/Actions/Banking/DepositAmountView.swift b/TalerWallet1/Views/Actions/Banking/DepositAmountView.swift
index c781061c..3578fc0d 100644
--- a/TalerWallet1/Views/Actions/Banking/DepositAmountView.swift
+++ b/TalerWallet1/Views/Actions/Banking/DepositAmountView.swift
@@ -18,6 +18,7 @@ struct DepositAmountView: View {
     @Binding var amountLastUsed: Amount
     @Binding var amountToTransfer: Amount
     let amountAvailable: Amount
+    let paytoUri: String?
     let depositStarted: Bool
     let action: () -> Void
 
@@ -56,26 +57,23 @@ struct DepositAmountView: View {
                                          : true // TODO: !(feeAmount?.isZero ?? false)
     }
 
+    /// Show what the deposit costs before it is made: wallet-core's checkDeposit
     @MainActor
-    private func computeFeeDeposit(_ amount: Amount) async -> ComputeFeeResult? {
-        if amount.isZero {
-            return ComputeFeeResult.zero()
+    private func computeFeeDeposit() async {
+        checkDepositResult = nil
+        feeAmount = nil
+        feeStr = EMPTYSTRING
+        guard let paytoUri, let balance, !amountToTransfer.isZero,
+              (try? amountToTransfer > amountAvailable) == false else {
+            return
         }
-        let insufficient = (try? amount > amountAvailable) ?? true
-        if insufficient {
-            return ComputeFeeResult.insufficient()
+        if let check = try? await model.checkDeposit4711(paytoUri, amount: amountToTransfer,
+                                                         viewHandles: true),
+           let fee = try? Amount.diff(check.totalDepositCost, check.effectiveDepositAmount) {
+            checkDepositResult = check
+            feeAmount = fee
+            feeStr = fee.formatted(balance.scopeInfo, isNegative: false).0
         }
-//    private func fee(ppCheck: CheckDepositResponse?) -> Amount? {
-        do {
-//            if let ppCheck {
-//                // Outgoing: fee = effective - raw
-//                feeAmount = try ppCheck.fees.coin + ppCheck.fees.wire + ppCheck.fees.refresh
-//                return feeAmount
-//            }
-        } catch {
-
-        }
-        return nil
     }
 
     private func buttonTitle(_ amount: Amount) -> String {
@@ -139,6 +137,7 @@ struct DepositAmountView: View {
                 .disabled(disabled || depositStarted)
                 .accessibilityHint(disabled ? hint : EMPTYSTRING)
                 .padding(.horizontal)
+                .task(id: amountToTransfer.value) { await computeFeeDeposit() }
         } else {    // no balance - Yikes
             Text("No balance. There seems to be a problem with the database...")
         }
ios-deposit-fee-preview.patch (3,406 bytes)   
ios-base-11964-decode-fix.patch (1,292 bytes)   
diff --git a/TalerWallet1/Model/Model+Deposit.swift b/TalerWallet1/Model/Model+Deposit.swift
index aace81a7..ce494e88 100644
--- a/TalerWallet1/Model/Model+Deposit.swift
+++ b/TalerWallet1/Model/Model+Deposit.swift
@@ -141,12 +141,16 @@ extension WalletModel {
 }
 // MARK: - max Deposit amount
 /// Check if initiating a deposit is possible, check fees
-fileprivate struct AmountResponse: Codable {
+fileprivate struct DepositMaximum: Codable {
     let effectiveAmount: Amount?
     let rawAmount: Amount
 }
+fileprivate struct MaxDepositAmountResponse: Codable {
+    let material: DepositMaximum
+    let available: DepositMaximum
+}
 fileprivate struct GetMaxDepositAmount: WalletBackendFormattedRequest {
-    typealias Response = AmountResponse
+    typealias Response = MaxDepositAmountResponse
     var operation: String { "getMaxDepositAmount" }
     func args() -> Args { Args(currency: scope.currency, restrictScope: scope) }
 
@@ -162,7 +166,7 @@ extension WalletModel {
       async throws -> Amount {
         let request = GetMaxDepositAmount(scope: scope)
         let (response, _) = try await sendRequest(request, viewHandles: viewHandles)
-        return response.rawAmount
+        return response.available.rawAmount
     }
 } // getMaxDepositAmount
 // MARK: - Deposit

Required notifications

Address or linkRemarkAdded byNotifiedActions

Relationships

related to 0011964 planning Deposit is impossible: getMaxDepositAmount's response is nested, the app decodes it as flat, and the whole flow dies with "Unknown error" 

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 01:42bot-cgNew Issue
2026-11-11 01:42bot-cgRelationship addedrelated to 0011964
2026-11-11 01:42bot-cgFile Added: before-ios-1.png
2026-11-11 01:42bot-cgFile Added: after-ios-1.png
2026-11-11 01:42bot-cgFile Added: before-web-1-review.png
2026-11-11 01:42bot-cgFile Added: ios-deposit-fee-preview.patch
2026-11-11 01:42bot-cgFile Added: ios-base-11964-decode-fix.patch