gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-ios] branch master updated (a123c3a -> 4167b6d)


From: gnunet
Subject: [taler-taler-ios] branch master updated (a123c3a -> 4167b6d)
Date: Fri, 19 Jan 2024 09:01:33 +0100

This is an automated email from the git hooks/post-receive script.

marc-stibane pushed a change to branch master
in repository taler-ios.

    from a123c3a  Bump version to 0.9.3 (31)
     new 6e44b57  WithdrawExchangeV
     new 2387c4f  LoadingView
     new d555622  CurrencyInfo from Exchange
     new 6ae5e98  AccountPicker, remove exchangePaytoUris
     new 318f15a  ToS language
     new 477a0e4  withdraw-exchange
     new fdc905d  Haptics
     new 1a49f45  fixes
     new 223a4fb  API 2:0:1
     new d548c17  Exchanges->Banking
     new f9af505  Landscape
     new 447a870  PRINT_CHANGES
     new d8d53cb  definitions
     new eb9f01f  remove Apple Sounds
     new 52494d6  hapticFeedback
     new 66adf65  remove about protocols
     new 0c0f3b1  Summary in ThreeAmountsV
     new 7dea9e4  ToS for P2P
     new 630e02a  cleanup
     new 9adda94  button color if disabled
     new c5107ee  TransactionDone popToRootView
     new 2a34df6  Summary in ThreeAmountsV
     new ca1e5b2  aborting Notification
     new dc28b36  dbg+cleanup
     new c6affde  colors adapted for WCAG AA
     new eaf3a05  Layout transactions
     new 0d10ba1  use minor for Status
     new 306b349  AccountRestriction
     new 356f482  clearDb (instead of reset)
     new 9729eec  Layout Balances
     new 10b9279  Withdraw only once
     new b1b30fa  Warnings for tx buttons
     new c8adbe5  bars relative to fontSize
     new d06f7ab  tintcolor (blue) for links
     new ab68cbf  Markdown for ToS
     new a436c14  b-i-withdrawal hint
     new 67bd256  dbg+cleanup
     new 6704c95  remove
     new c4b0616  Bump version to 0.9.3 (34)
     new c376385  Exchange -> Banking
     new 864e217  AppIcon blue
     new 439ae30  remove payto scheme
     new 2d8ca75  currency == ISO 4217
     new 5d28cd4  payment_received only for withdrawals
     new bab4ef3  remove SideBarView
     new 5ed30b8  AccountRestriction
     new bd89805  single routine for rendering amounts
     new 5e8eda4  fix fractions
     new 4eb6003  accessibilityDate
     new d243328  txStates
     new f5d399e  cleanup
     new eaa421c  return to Balances after tx
     new 4b236b4  Summary no longer twice
     new 71ec01c  Keyboard needs more time
     new 601bbf4  logging
     new 85b019d  remove "code", since "currency" IS ISO-4217 already for global
     new 20225fb  use cent initializer
     new fa377b5  use scopeInfo
     new c705832  bugfix
     new 62578f6  P2P Expiration Timestamp
     new ca2329e  InfiniteTransactionLoop
     new f6d7c04  cleanup, preview
     new f4599bb  Locale.preferredLanguageCode
     new ae7a063  show minor in tx details
     new 90c1ac1  Restrictions
     new 58b2bab  InterfaceOrientation
     new 32813bf  Bump version to 0.9.4 (0)
     new 90aa5ad  lowercased()
     new 4167b6d  Bump version to 0.9.4 (1)

The 69 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 GNU_Taler Info.plist                               |   1 -
 TalerWallet.xcodeproj/project.pbxproj              | 140 ++++++++++------
 .../Contents.json                                  |  19 +++
 .../AppIcon3.appiconset/appstore1024.png           | Bin 0 -> 916884 bytes
 .../AppIcon3.appiconset/ipad152.png                | Bin 0 -> 65123 bytes
 .../Assets.xcassets/AppIcon3.appiconset/ipad76.png | Bin 0 -> 20561 bytes
 .../AppIcon3.appiconset/ipadNotification20.png     | Bin 0 -> 4737 bytes
 .../AppIcon3.appiconset/ipadNotification40.png     | Bin 0 -> 8588 bytes
 .../AppIcon3.appiconset/ipadPro167.png             | Bin 0 -> 76971 bytes
 .../AppIcon3.appiconset/ipadSettings29.png         | Bin 0 -> 6320 bytes
 .../AppIcon3.appiconset/ipadSettings58.png         | Bin 0 -> 13825 bytes
 .../AppIcon3.appiconset/ipadSpotlight40.png        | Bin 0 -> 8588 bytes
 .../AppIcon3.appiconset/ipadSpotlight80.png        | Bin 0 -> 21534 bytes
 .../AppIcon3.appiconset/iphone120.png              | Bin 0 -> 41974 bytes
 .../AppIcon3.appiconset/iphone180.png              | Bin 0 -> 85131 bytes
 .../AppIcon3.appiconset/mac1024.png                | Bin 0 -> 739039 bytes
 .../Assets.xcassets/AppIcon3.appiconset/mac128.png | Bin 0 -> 46065 bytes
 .../Assets.xcassets/AppIcon3.appiconset/mac16.png  | Bin 0 -> 4224 bytes
 .../Assets.xcassets/AppIcon3.appiconset/mac256.png | Bin 0 -> 150980 bytes
 .../Assets.xcassets/AppIcon3.appiconset/mac32.png  | Bin 0 -> 7369 bytes
 .../Assets.xcassets/AppIcon3.appiconset/mac512.png | Bin 0 -> 432577 bytes
 .../Assets.xcassets/AppIcon3.appiconset/mac64.png  | Bin 0 -> 16145 bytes
 .../AppIcon3.appiconset/notification40.png         | Bin 0 -> 8588 bytes
 .../AppIcon3.appiconset/notification60.png         | Bin 0 -> 14846 bytes
 .../AppIcon3.appiconset/settings58.png             | Bin 0 -> 13825 bytes
 .../AppIcon3.appiconset/settings87.png             | Bin 0 -> 25602 bytes
 .../AppIcon3.appiconset/spotlight120.png           | Bin 0 -> 41974 bytes
 .../AppIcon3.appiconset/spotlight80.png            | Bin 0 -> 21534 bytes
 TalerWallet1/Backend/WalletBackendRequest.swift    |   4 +-
 TalerWallet1/Backend/WalletCore.swift              |  40 ++++-
 TalerWallet1/Controllers/Controller.swift          |  61 +++++--
 TalerWallet1/Controllers/DebugViewC.swift          |   6 +-
 TalerWallet1/Controllers/PublicConstants.swift     |   6 +-
 TalerWallet1/Controllers/TalerWallet1App.swift     |   2 +-
 TalerWallet1/Helper/Controller+playSound.swift     |  36 +++-
 TalerWallet1/Helper/CurrencySpecification.swift    |  34 +++-
 TalerWallet1/Helper/TalerDater.swift               |   9 +
 TalerWallet1/Helper/URL+id+iban.swift              |   4 +-
 TalerWallet1/Helper/View+dismissTop.swift          |  21 ++-
 TalerWallet1/Helper/WalletColors.swift             |  24 ++-
 TalerWallet1/Model/Model+Exchange.swift            |   5 +-
 TalerWallet1/Model/Model+P2P.swift                 |   5 +-
 TalerWallet1/Model/Model+Settings.swift            |  31 +++-
 TalerWallet1/Model/Model+Withdraw.swift            |  52 +++++-
 TalerWallet1/Model/Transaction.swift               |  71 +++++++-
 TalerWallet1/Model/WalletModel.swift               |   2 +-
 TalerWallet1/Quickjs/quickjs.swift                 |  11 +-
 TalerWallet1/Views/Balances/BalanceRowView.swift   |  83 +++++----
 TalerWallet1/Views/Balances/BalancesListView.swift |  16 +-
 .../Views/Balances/BalancesSectionView.swift       |  44 +++--
 TalerWallet1/Views/Balances/PendingRowView.swift   |  21 +--
 .../{Exchange => Banking}/ExchangeListView.swift   |  18 +-
 .../{Exchange => Banking}/ExchangeRowView.swift    |  39 +++--
 .../ExchangeSectionView.swift                      |  23 ++-
 .../{Exchange => Banking}/ManualWithdraw.swift     |  59 ++++---
 .../{Exchange => Banking}/ManualWithdrawDone.swift |  27 +--
 .../{Exchange => Banking}/QuiteSomeCoins.swift     |   0
 TalerWallet1/Views/HelperViews/AmountRowV.swift    | 136 +++++++--------
 TalerWallet1/Views/HelperViews/AmountV.swift       |  38 +++++
 TalerWallet1/Views/HelperViews/AmountView.swift    |  44 -----
 TalerWallet1/Views/HelperViews/BarGraph.swift      |  12 +-
 TalerWallet1/Views/HelperViews/Buttons.swift       |   8 +-
 TalerWallet1/Views/HelperViews/CopyShare.swift     |  11 +-
 TalerWallet1/Views/HelperViews/CurrencyField.swift |   2 +-
 .../Views/HelperViews/CurrencyInputView.swift      |   2 +-
 .../Views/HelperViews/LaunchAnimationView.swift    |   2 +-
 TalerWallet1/Views/HelperViews/LoadingView.swift   |  43 ++++-
 .../Views/HelperViews/QRCodeDetailView.swift       |  23 ++-
 .../Views/HelperViews/TransactionButton.swift      |  53 ++++--
 TalerWallet1/Views/Main/MainView.swift             | 111 +++---------
 TalerWallet1/Views/Main/SideBarView.swift          | 109 ------------
 TalerWallet1/Views/Main/WalletEmptyView.swift      |   2 +-
 TalerWallet1/Views/Peer2peer/P2PReadyV.swift       |   4 +-
 TalerWallet1/Views/Peer2peer/P2PSubjectV.swift     |   6 +-
 TalerWallet1/Views/Peer2peer/RequestPayment.swift  |   2 +-
 TalerWallet1/Views/Peer2peer/SendAmount.swift      |   4 +-
 TalerWallet1/Views/Settings/AboutView.swift        |  22 +--
 TalerWallet1/Views/Settings/SettingsItem.swift     |   4 +
 TalerWallet1/Views/Settings/SettingsView.swift     |  67 +++++---
 .../Views/Sheets/P2P_Sheets/P2pAcceptDone.swift    |   9 +-
 .../Views/Sheets/P2P_Sheets/P2pPayURIView.swift    |  34 ++--
 .../Sheets/P2P_Sheets/P2pReceiveURIView.swift      |  30 ++--
 .../Views/Sheets/Payment/PayTemplateView.swift     |  12 +-
 .../Views/Sheets/Payment/PaymentView.swift         |  17 +-
 TalerWallet1/Views/Sheets/QRSheet.swift            |   2 +-
 .../Views/Sheets/Refund/RefundURIView.swift        |   4 +-
 TalerWallet1/Views/Sheets/URLSheet.swift           |   6 +-
 .../WithdrawAcceptDone.swift                       |  11 +-
 .../WithdrawProgressView.swift                     |  32 ----
 .../WithdrawBankIntegrated/WithdrawTOSView.swift   | 162 +++++++++++-------
 .../WithdrawBankIntegrated/WithdrawURIView.swift   |  25 +--
 TalerWallet1/Views/Sheets/WithdrawExchangeV.swift  |  53 ++++++
 .../Views/Transactions/ManualDetailsV.swift        | 186 +++++++++++++++------
 .../Views/Transactions/ThreeAmountsV.swift         |  28 +++-
 .../Views/Transactions/TransactionDetailView.swift | 130 +++++++-------
 .../Views/Transactions/TransactionRowView.swift    | 167 +++++++++---------
 .../Views/Transactions/TransactionsListView.swift  |   4 +-
 Taler_Wallet Info.plist                            |   1 -
 TestFlight/WhatToTest.en-US.txt                    |  34 ++++
 taler-swift/Sources/taler-swift/Amount.swift       |   3 +-
 100 files changed, 1533 insertions(+), 1036 deletions(-)
 copy TalerWallet1/Assets.xcassets/{AppIcon.appiconset => 
AppIcon3.appiconset}/Contents.json (80%)
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/appstore1024.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad152.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad76.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification20.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification40.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadPro167.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings29.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings58.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight40.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight80.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone120.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone180.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac1024.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac128.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac16.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac256.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac32.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac512.png
 create mode 100644 TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac64.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification40.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification60.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings58.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings87.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight120.png
 create mode 100644 
TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight80.png
 rename TalerWallet1/Views/{Exchange => Banking}/ExchangeListView.swift (92%)
 rename TalerWallet1/Views/{Exchange => Banking}/ExchangeRowView.swift (84%)
 rename TalerWallet1/Views/{Exchange => Banking}/ExchangeSectionView.swift (69%)
 rename TalerWallet1/Views/{Exchange => Banking}/ManualWithdraw.swift (70%)
 rename TalerWallet1/Views/{Exchange => Banking}/ManualWithdrawDone.swift (77%)
 rename TalerWallet1/Views/{Exchange => Banking}/QuiteSomeCoins.swift (100%)
 create mode 100644 TalerWallet1/Views/HelperViews/AmountV.swift
 delete mode 100644 TalerWallet1/Views/HelperViews/AmountView.swift
 delete mode 100644 TalerWallet1/Views/Main/SideBarView.swift
 delete mode 100644 
TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift
 create mode 100644 TalerWallet1/Views/Sheets/WithdrawExchangeV.swift

diff --git a/GNU_Taler Info.plist b/GNU_Taler Info.plist
index b6dac8f..e6aba3e 100644
--- a/GNU_Taler Info.plist      
+++ b/GNU_Taler Info.plist      
@@ -18,7 +18,6 @@
                                <string>taler</string>
                                <string>ext+taler</string>
                                <string>web+taler</string>
-                               <string>payto</string>
                        </array>
                </dict>
        </array>
diff --git a/TalerWallet.xcodeproj/project.pbxproj 
b/TalerWallet.xcodeproj/project.pbxproj
index 058b9b6..1739f81 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -10,8 +10,9 @@
                4E16E12329F3BB99008B9C86 /* CurrencySpecification.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4E16E12229F3BB99008B9C86 /* 
CurrencySpecification.swift */; };
                4E2254972A822B8100E41D29 /* payment_received.m4a in Resources 
*/ = {isa = PBXBuildFile; fileRef = 4E2254952A822B8100E41D29 /* 
payment_received.m4a */; };
                4E2254982A822B8100E41D29 /* payment_sent.m4a in Resources */ = 
{isa = PBXBuildFile; fileRef = 4E2254962A822B8100E41D29 /* payment_sent.m4a */; 
};
-               4E2E5F7F2AEE30DA0027FA8A /* AmountRowV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E2E5F7E2AEE30DA0027FA8A /* AmountRowV.swift */; 
};
-               4E2E5F802AEE30DA0027FA8A /* AmountRowV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E2E5F7E2AEE30DA0027FA8A /* AmountRowV.swift */; 
};
+               4E2D8DD32B3F513800234039 /* MarkdownUI in Frameworks */ = {isa 
= PBXBuildFile; productRef = 4E2D8DD22B3F513800234039 /* MarkdownUI */; };
+               4E2D8DD52B45822A00234039 /* AmountV.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4E2D8DD42B45822A00234039 /* AmountV.swift */; };
+               4E2D8DD62B45822A00234039 /* AmountV.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4E2D8DD42B45822A00234039 /* AmountV.swift */; };
                4E3327BA2AD1635100BF5AD6 /* AsyncSemaphore.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4E3327B92AD1635100BF5AD6 /* 
AsyncSemaphore.swift */; };
                4E3327BB2AD1635100BF5AD6 /* AsyncSemaphore.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4E3327B92AD1635100BF5AD6 /* 
AsyncSemaphore.swift */; };
                4E363CBC2A237E0900D7E98C /* URL+id+iban.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E363CBB2A237E0900D7E98C /* URL+id+iban.swift 
*/; };
@@ -67,7 +68,7 @@
                4E3EAE482A990778009F1BE8 /* PayTemplateView.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EBA56402A7FF5200084948B /* 
PayTemplateView.swift */; };
                4E3EAE492A990778009F1BE8 /* ManualWithdrawDone.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB431662A1E55C700C5690E /* 
ManualWithdrawDone.swift */; };
                4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E753A072A0B6A5F002D9328 /* ShareSheet.swift */; 
};
-               4E3EAE4C2A990778009F1BE8 /* AmountView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountView.swift */; 
};
+               4E3EAE4C2A990778009F1BE8 /* AmountRowV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountRowV.swift */; 
};
                4E3EAE4D2A990778009F1BE8 /* P2pAcceptDone.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E3B4BC22A42252300CC88B8 /* P2pAcceptDone.swift 
*/; };
                4E3EAE4E2A990778009F1BE8 /* AnyTransition+backslide.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4E363CBD2A23CB2100D7E98C /* 
AnyTransition+backslide.swift */; };
                4E3EAE4F2A990778009F1BE8 /* TwoRowButtons.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB065432A4CD1A80039B91D /* TwoRowButtons.swift 
*/; };
@@ -85,14 +86,12 @@
                4E3EAE5B2A990778009F1BE8 /* View+Notification.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4E3B4BC62A429F2A00CC88B8 /* 
View+Notification.swift */; };
                4E3EAE5C2A990778009F1BE8 /* Model+Pending.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift 
*/; };
                4E3EAE5D2A990778009F1BE8 /* ExchangeListView.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EB095292989CBFE0043A8A1 /* 
ExchangeListView.swift */; };
-               4E3EAE5E2A990778009F1BE8 /* WithdrawProgressView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4EB0953F2989CBFE0043A8A1 /* 
WithdrawProgressView.swift */; };
                4E3EAE5F2A990778009F1BE8 /* QRSheet.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4EEC157929F9427F00D46A03 /* QRSheet.swift */; };
                4E3EAE602A990778009F1BE8 /* P2pReceiveURIView.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4E3B4BC02A41E6C200CC88B8 /* 
P2pReceiveURIView.swift */; };
                4E3EAE612A990778009F1BE8 /* ListStyle.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4E6EDD862A363D8D0031D520 /* ListStyle.swift */; 
};
                4E3EAE622A990778009F1BE8 /* TransactionDetailView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4EB095312989CBFE0043A8A1 /* 
TransactionDetailView.swift */; };
                4E3EAE632A990778009F1BE8 /* WalletCore.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0951C2989CBCB0043A8A1 /* WalletCore.swift */; 
};
                4E3EAE642A990778009F1BE8 /* LaunchAnimationView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4EB095432989CBFE0043A8A1 /* 
LaunchAnimationView.swift */; };
-               4E3EAE652A990778009F1BE8 /* SideBarView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095422989CBFE0043A8A1 /* SideBarView.swift 
*/; };
                4E3EAE682A990778009F1BE8 /* WalletModel.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095112989CBB00043A8A1 /* WalletModel.swift 
*/; };
                4E3EAE692A990778009F1BE8 /* URLSheet.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4EB095332989CBFE0043A8A1 /* URLSheet.swift */; };
                4E3EAE6A2A990778009F1BE8 /* ThreeAmountsV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmountsV.swift 
*/; };
@@ -208,15 +207,13 @@
                4EB095602989CBFE0043A8A1 /* BalancesSectionView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4EB0953A2989CBFE0043A8A1 /* 
BalancesSectionView.swift */; };
                4EB095612989CBFE0043A8A1 /* WithdrawURIView.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EB0953C2989CBFE0043A8A1 /* 
WithdrawURIView.swift */; };
                4EB095622989CBFE0043A8A1 /* Model+Withdraw.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EB0953D2989CBFE0043A8A1 /* 
Model+Withdraw.swift */; };
-               4EB095642989CBFE0043A8A1 /* WithdrawProgressView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4EB0953F2989CBFE0043A8A1 /* 
WithdrawProgressView.swift */; };
                4EB095652989CBFE0043A8A1 /* WithdrawTOSView.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EB095402989CBFE0043A8A1 /* 
WithdrawTOSView.swift */; };
-               4EB095662989CBFE0043A8A1 /* SideBarView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095422989CBFE0043A8A1 /* SideBarView.swift 
*/; };
                4EB095672989CBFE0043A8A1 /* LaunchAnimationView.swift in 
Sources */ = {isa = PBXBuildFile; fileRef = 4EB095432989CBFE0043A8A1 /* 
LaunchAnimationView.swift */; };
                4EB095682989CBFE0043A8A1 /* MainView.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4EB095442989CBFE0043A8A1 /* MainView.swift */; };
                4EB095692989CBFE0043A8A1 /* ErrorView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095452989CBFE0043A8A1 /* ErrorView.swift */; 
};
                4EB0956A2989CBFE0043A8A1 /* Buttons.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4EB095472989CBFE0043A8A1 /* Buttons.swift */; };
                4EB0956B2989CBFE0043A8A1 /* TextFieldAlert.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EB095482989CBFE0043A8A1 /* 
TextFieldAlert.swift */; };
-               4EB0956C2989CBFE0043A8A1 /* AmountView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountView.swift */; 
};
+               4EB0956C2989CBFE0043A8A1 /* AmountRowV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB095492989CBFE0043A8A1 /* AmountRowV.swift */; 
};
                4EB0956D2989CBFE0043A8A1 /* LoadingView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0954A2989CBFE0043A8A1 /* LoadingView.swift 
*/; };
                4EB0956E2989CBFE0043A8A1 /* Model+Pending.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift 
*/; };
                4EB3136129FEE79B007D68BC /* P2PReadyV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EB3136029FEE79B007D68BC /* P2PReadyV.swift */; 
};
@@ -237,10 +234,15 @@
                4ED2F94B2A278F5100453B40 /* ThreeAmountsV.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4ED2F94A2A278F5100453B40 /* ThreeAmountsV.swift 
*/; };
                4EDBDCD92AB787CB00925C02 /* CallStack.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */; 
};
                4EDBDCDA2AB787CB00925C02 /* CallStack.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EDBDCD82AB787CB00925C02 /* CallStack.swift */; 
};
+               4EE171882B49635800BF9FF5 /* MarkdownUI in Frameworks */ = {isa 
= PBXBuildFile; productRef = 4EE171872B49635800BF9FF5 /* MarkdownUI */; };
+               4EE171902B49FE2B00BF9FF5 /* OrderedCollections in Frameworks */ 
= {isa = PBXBuildFile; productRef = 4EE1718F2B49FE2B00BF9FF5 /* 
OrderedCollections */; };
+               4EE171922B49FE4E00BF9FF5 /* OrderedCollections in Frameworks */ 
= {isa = PBXBuildFile; productRef = 4EE171912B49FE4E00BF9FF5 /* 
OrderedCollections */; };
                4EEC157329F8242800D46A03 /* QRGeneratorView.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EEC157229F8242800D46A03 /* 
QRGeneratorView.swift */; };
                4EEC157629F8ECBF00D46A03 /* CodeScanner in Frameworks */ = {isa 
= PBXBuildFile; productRef = 4EEC157529F8ECBF00D46A03 /* CodeScanner */; };
                4EEC157829F9032900D46A03 /* Sheet.swift in Sources */ = {isa = 
PBXBuildFile; fileRef = 4EEC157729F9032900D46A03 /* Sheet.swift */; };
                4EEC157A29F9427F00D46A03 /* QRSheet.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4EEC157929F9427F00D46A03 /* QRSheet.swift */; };
+               4EEC3A712B2285A200D05F9D /* WithdrawExchangeV.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EEC3A702B2285A200D05F9D /* 
WithdrawExchangeV.swift */; };
+               4EEC3A722B2285A200D05F9D /* WithdrawExchangeV.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EEC3A702B2285A200D05F9D /* 
WithdrawExchangeV.swift */; };
                4EF840A72A0B85F400EE0D47 /* CopyShare.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EF840A62A0B85F400EE0D47 /* CopyShare.swift */; 
};
                4EFA39602AA7946B00742548 /* ToSButtonView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EFA395F2AA7946B00742548 /* ToSButtonView.swift 
*/; };
                4EFA39612AA7946B00742548 /* ToSButtonView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EFA395F2AA7946B00742548 /* ToSButtonView.swift 
*/; };
@@ -294,7 +296,7 @@
                4E16E12229F3BB99008B9C86 /* CurrencySpecification.swift */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.swift; path = CurrencySpecification.swift; sourceTree = "<group>"; };
                4E2254952A822B8100E41D29 /* payment_received.m4a */ = {isa = 
PBXFileReference; lastKnownFileType = file; path = payment_received.m4a; 
sourceTree = "<group>"; };
                4E2254962A822B8100E41D29 /* payment_sent.m4a */ = {isa = 
PBXFileReference; lastKnownFileType = file; path = payment_sent.m4a; sourceTree 
= "<group>"; };
-               4E2E5F7E2AEE30DA0027FA8A /* AmountRowV.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
AmountRowV.swift; sourceTree = "<group>"; };
+               4E2D8DD42B45822A00234039 /* AmountV.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmountV.swift; 
sourceTree = "<group>"; };
                4E3327B92AD1635100BF5AD6 /* AsyncSemaphore.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= AsyncSemaphore.swift; sourceTree = "<group>"; };
                4E363CBB2A237E0900D7E98C /* URL+id+iban.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= "URL+id+iban.swift"; sourceTree = "<group>"; };
                4E363CBD2A23CB2100D7E98C /* AnyTransition+backslide.swift */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.swift; path = "AnyTransition+backslide.swift"; sourceTree = 
"<group>"; };
@@ -383,15 +385,13 @@
                4EB0953A2989CBFE0043A8A1 /* BalancesSectionView.swift */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; 
path = BalancesSectionView.swift; sourceTree = "<group>"; };
                4EB0953C2989CBFE0043A8A1 /* WithdrawURIView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= WithdrawURIView.swift; sourceTree = "<group>"; };
                4EB0953D2989CBFE0043A8A1 /* Model+Withdraw.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= "Model+Withdraw.swift"; sourceTree = "<group>"; };
-               4EB0953F2989CBFE0043A8A1 /* WithdrawProgressView.swift */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.swift; path = WithdrawProgressView.swift; sourceTree = "<group>"; };
                4EB095402989CBFE0043A8A1 /* WithdrawTOSView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= WithdrawTOSView.swift; sourceTree = "<group>"; };
-               4EB095422989CBFE0043A8A1 /* SideBarView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= SideBarView.swift; sourceTree = "<group>"; };
                4EB095432989CBFE0043A8A1 /* LaunchAnimationView.swift */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; 
path = LaunchAnimationView.swift; sourceTree = "<group>"; };
                4EB095442989CBFE0043A8A1 /* MainView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= MainView.swift; sourceTree = "<group>"; };
                4EB095452989CBFE0043A8A1 /* ErrorView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= ErrorView.swift; sourceTree = "<group>"; };
                4EB095472989CBFE0043A8A1 /* Buttons.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= Buttons.swift; sourceTree = "<group>"; };
                4EB095482989CBFE0043A8A1 /* TextFieldAlert.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= TextFieldAlert.swift; sourceTree = "<group>"; };
-               4EB095492989CBFE0043A8A1 /* AmountView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= AmountView.swift; sourceTree = "<group>"; };
+               4EB095492989CBFE0043A8A1 /* AmountRowV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= AmountRowV.swift; sourceTree = "<group>"; };
                4EB0954A2989CBFE0043A8A1 /* LoadingView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= LoadingView.swift; sourceTree = "<group>"; };
                4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= "Model+Pending.swift"; sourceTree = "<group>"; };
                4EB3136029FEE79B007D68BC /* P2PReadyV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= P2PReadyV.swift; sourceTree = "<group>"; };
@@ -411,6 +411,7 @@
                4EEC157229F8242800D46A03 /* QRGeneratorView.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= QRGeneratorView.swift; sourceTree = "<group>"; };
                4EEC157729F9032900D46A03 /* Sheet.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= Sheet.swift; sourceTree = "<group>"; };
                4EEC157929F9427F00D46A03 /* QRSheet.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= QRSheet.swift; sourceTree = "<group>"; };
+               4EEC3A702B2285A200D05F9D /* WithdrawExchangeV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= WithdrawExchangeV.swift; sourceTree = "<group>"; };
                4EF840A62A0B85F400EE0D47 /* CopyShare.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= CopyShare.swift; sourceTree = "<group>"; };
                4EFA395F2AA7946B00742548 /* ToSButtonView.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
ToSButtonView.swift; sourceTree = "<group>"; };
                AB710490285995B6008B04F0 /* taler-swift */ = {isa = 
PBXFileReference; lastKnownFileType = text; path = "taler-swift"; sourceTree = 
SOURCE_ROOT; };
@@ -429,7 +430,9 @@
                                4E3EAE772A990778009F1BE8 /* AnyCodable in 
Frameworks */,
                                4E3EAE782A990778009F1BE8 /* SymLog in 
Frameworks */,
                                4E3EAE792A990778009F1BE8 /* 
FTalerWalletcore.framework in Frameworks */,
+                               4EE171882B49635800BF9FF5 /* MarkdownUI in 
Frameworks */,
                                4E3EAE7A2A990778009F1BE8 /* taler-swift in 
Frameworks */,
+                               4EE171922B49FE4E00BF9FF5 /* OrderedCollections 
in Frameworks */,
                                4E3EAE7B2A990778009F1BE8 /* CodeScanner in 
Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
@@ -441,7 +444,9 @@
                                ABE97B1D286D82BF00580772 /* AnyCodable in 
Frameworks */,
                                4EB094FD29897D280043A8A1 /* SymLog in 
Frameworks */,
                                4EB094F829897CA20043A8A1 /* 
FTalerWalletcore.framework in Frameworks */,
+                               4E2D8DD32B3F513800234039 /* MarkdownUI in 
Frameworks */,
                                ABC13AA32859962800D23185 /* taler-swift in 
Frameworks */,
+                               4EE171902B49FE2B00BF9FF5 /* OrderedCollections 
in Frameworks */,
                                4EEC157629F8ECBF00D46A03 /* CodeScanner in 
Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
@@ -613,7 +618,7 @@
                                4ECB627F2A0BA6DF004ABBB7 /* Model+P2P.swift */,
                                4EB0954C2989CBFE0043A8A1 /* Model+Pending.swift 
*/,
                                4EB0952C2989CBFE0043A8A1 /* Model+Payment.swift 
*/,
-                E37AA6292AF197E5003850CF /* Model+Refund.swift */,
+                               E37AA6292AF197E5003850CF /* Model+Refund.swift 
*/,
                                4EB095102989CBB00043A8A1 /* 
Model+Settings.swift */,
                                4EB095322989CBFE0043A8A1 /* 
Model+Transactions.swift */,
                                4EB0953D2989CBFE0043A8A1 /* 
Model+Withdraw.swift */,
@@ -638,7 +643,7 @@
                                4EB095412989CBFE0043A8A1 /* Main */,
                                4EB095342989CBFE0043A8A1 /* Balances */,
                                4EB0952E2989CBFE0043A8A1 /* Transactions */,
-                               4EB095272989CBFE0043A8A1 /* Exchange */,
+                               4EB095272989CBFE0043A8A1 /* Banking */,
                                4EB095242989CBFE0043A8A1 /* Settings */,
                                4ECB627E2A0BA4DA004ABBB7 /* Peer2peer */,
                                4EEC157129F7188B00D46A03 /* Sheets */,
@@ -657,7 +662,7 @@
                        path = Settings;
                        sourceTree = "<group>";
                };
-               4EB095272989CBFE0043A8A1 /* Exchange */ = {
+               4EB095272989CBFE0043A8A1 /* Banking */ = {
                        isa = PBXGroup;
                        children = (
                                4EB095292989CBFE0043A8A1 /* 
ExchangeListView.swift */,
@@ -667,7 +672,7 @@
                                4EBA82AC2A3F580500E5F39A /* 
QuiteSomeCoins.swift */,
                                4EB431662A1E55C700C5690E /* 
ManualWithdrawDone.swift */,
                        );
-                       path = Exchange;
+                       path = Banking;
                        sourceTree = "<group>";
                };
                4EB0952A2989CBFE0043A8A1 /* Payment */ = {
@@ -709,7 +714,6 @@
                        children = (
                                4EB0953C2989CBFE0043A8A1 /* 
WithdrawURIView.swift */,
                                4E5A88F62A3B9E5B00072618 /* 
WithdrawAcceptDone.swift */,
-                               4EB0953F2989CBFE0043A8A1 /* 
WithdrawProgressView.swift */,
                                4EB095402989CBFE0043A8A1 /* 
WithdrawTOSView.swift */,
                        );
                        path = WithdrawBankIntegrated;
@@ -719,7 +723,6 @@
                        isa = PBXGroup;
                        children = (
                                4EB095442989CBFE0043A8A1 /* MainView.swift */,
-                               4EB095422989CBFE0043A8A1 /* SideBarView.swift 
*/,
                                4EB095452989CBFE0043A8A1 /* ErrorView.swift */,
                                4EB095392989CBFE0043A8A1 /* 
WalletEmptyView.swift */,
                        );
@@ -730,12 +733,12 @@
                        isa = PBXGroup;
                        children = (
                                4E97968F2A3765ED006F73BC /* AgePicker.swift */,
-                               4E2E5F7E2AEE30DA0027FA8A /* AmountRowV.swift */,
                                4E605DB92AB05FB6002FB9A7 /* BarGraph.swift */,
                                4EB095472989CBFE0043A8A1 /* Buttons.swift */,
                                4EF840A62A0B85F400EE0D47 /* CopyShare.swift */,
                                4ECB62812A0BB01D004ABBB7 /* SelectDays.swift */,
                                4EA551242A2C923600FEC9A8 /* 
CurrencyInputView.swift */,
+                               4E2D8DD42B45822A00234039 /* AmountV.swift */,
                                4E53A33629F50B7B00830EC2 /* CurrencyField.swift 
*/,
                                4EEC157229F8242800D46A03 /* 
QRGeneratorView.swift */,
                                4E5A88F42A38A4FD00072618 /* 
QRCodeDetailView.swift */,
@@ -744,7 +747,7 @@
                                4E983C2B2ADC416800FA9CC5 /* 
View+fitsSideBySide.swift */,
                                4EB095482989CBFE0043A8A1 /* 
TextFieldAlert.swift */,
                                4EBA82AA2A3EB2CA00E5F39A /* 
TransactionButton.swift */,
-                               4EB095492989CBFE0043A8A1 /* AmountView.swift */,
+                               4EB095492989CBFE0043A8A1 /* AmountRowV.swift */,
                                4EB0954A2989CBFE0043A8A1 /* LoadingView.swift 
*/,
                                4EB095432989CBFE0043A8A1 /* 
LaunchAnimationView.swift */,
                                4EFA395F2AA7946B00742548 /* ToSButtonView.swift 
*/,
@@ -770,9 +773,10 @@
                                4EEC157929F9427F00D46A03 /* QRSheet.swift */,
                                4E753A072A0B6A5F002D9328 /* ShareSheet.swift */,
                                4EB095332989CBFE0043A8A1 /* URLSheet.swift */,
+                               4EEC3A702B2285A200D05F9D /* 
WithdrawExchangeV.swift */,
                                4EB0953B2989CBFE0043A8A1 /* 
WithdrawBankIntegrated */,
                                4EB0952A2989CBFE0043A8A1 /* Payment */,
-                E37AA62C2AF19BA6003850CF /* Refund */,
+                               E37AA62C2AF19BA6003850CF /* Refund */,
                                4E3B4BBF2A41E64000CC88B8 /* P2P_Sheets */,
                        );
                        path = Sheets;
@@ -851,6 +855,8 @@
                                4E3EAE162A990778009F1BE8 /* AnyCodable */,
                                4E3EAE182A990778009F1BE8 /* SymLog */,
                                4E3EAE1A2A990778009F1BE8 /* CodeScanner */,
+                               4EE171872B49635800BF9FF5 /* MarkdownUI */,
+                               4EE171912B49FE4E00BF9FF5 /* OrderedCollections 
*/,
                        );
                        productName = Taler;
                        productReference = 4E3EAE892A990778009F1BE8 /* 
GNU_Taler.app */;
@@ -875,6 +881,8 @@
                                ABE97B1C286D82BF00580772 /* AnyCodable */,
                                4EB094FC29897D280043A8A1 /* SymLog */,
                                4EEC157529F8ECBF00D46A03 /* CodeScanner */,
+                               4E2D8DD22B3F513800234039 /* MarkdownUI */,
+                               4EE1718F2B49FE2B00BF9FF5 /* OrderedCollections 
*/,
                        );
                        productName = Taler;
                        productReference = D14AFD1D24D232B300C51073 /* 
Taler_Wallet.app */;
@@ -955,6 +963,8 @@
                                ABE97B1B286D82BF00580772 /* 
XCRemoteSwiftPackageReference "AnyCodable" */,
                                4EB094FB29897D280043A8A1 /* 
XCRemoteSwiftPackageReference "SymLog" */,
                                4EEC157429F8ECBF00D46A03 /* 
XCRemoteSwiftPackageReference "CodeScanner" */,
+                               4E2D8DD12B3F513800234039 /* 
XCRemoteSwiftPackageReference "swift-markdown-ui-standalone" */,
+                               4EE1718C2B49EB7200BF9FF5 /* 
XCRemoteSwiftPackageReference "swift-collections" */,
                        );
                        productRefGroup = D14AFD1E24D232B300C51073 /* Products 
*/;
                        projectDirPath = "";
@@ -1079,11 +1089,12 @@
                                4E3EAE452A990778009F1BE8 /* P2PReadyV.swift in 
Sources */,
                                4E3EAE462A990778009F1BE8 /* 
TextFieldAlert.swift in Sources */,
                                4E3EAE472A990778009F1BE8 /* 
QuiteSomeCoins.swift in Sources */,
+                               4E2D8DD52B45822A00234039 /* AmountV.swift in 
Sources */,
                                4E3EAE482A990778009F1BE8 /* 
PayTemplateView.swift in Sources */,
                                4E3EAE492A990778009F1BE8 /* 
ManualWithdrawDone.swift in Sources */,
                                4E3EAE4B2A990778009F1BE8 /* ShareSheet.swift in 
Sources */,
                                4EC4008F2AE8019700DF72C7 /* 
ExchangeRowView.swift in Sources */,
-                               4E3EAE4C2A990778009F1BE8 /* AmountView.swift in 
Sources */,
+                               4E3EAE4C2A990778009F1BE8 /* AmountRowV.swift in 
Sources */,
                                4E605DBA2AB05FB6002FB9A7 /* BarGraph.swift in 
Sources */,
                                4E3EAE4D2A990778009F1BE8 /* P2pAcceptDone.swift 
in Sources */,
                                4E3EAE4E2A990778009F1BE8 /* 
AnyTransition+backslide.swift in Sources */,
@@ -1102,11 +1113,9 @@
                                4E3EAE592A990778009F1BE8 /* 
Model+Settings.swift in Sources */,
                                4EC4008C2AE5664100DF72C7 /* 
CharacterSet+contains.swift in Sources */,
                                4E3EAE5A2A990778009F1BE8 /* ErrorView.swift in 
Sources */,
-                               4E2E5F7F2AEE30DA0027FA8A /* AmountRowV.swift in 
Sources */,
                                4E3EAE5B2A990778009F1BE8 /* 
View+Notification.swift in Sources */,
                                4E3EAE5C2A990778009F1BE8 /* Model+Pending.swift 
in Sources */,
                                4E3EAE5D2A990778009F1BE8 /* 
ExchangeListView.swift in Sources */,
-                               4E3EAE5E2A990778009F1BE8 /* 
WithdrawProgressView.swift in Sources */,
                                E37AA62E2AF19BE0003850CF /* RefundURIView.swift 
in Sources */,
                                4E3EAE5F2A990778009F1BE8 /* QRSheet.swift in 
Sources */,
                                4E3EAE602A990778009F1BE8 /* 
P2pReceiveURIView.swift in Sources */,
@@ -1114,7 +1123,6 @@
                                4E3EAE622A990778009F1BE8 /* 
TransactionDetailView.swift in Sources */,
                                4E3EAE632A990778009F1BE8 /* WalletCore.swift in 
Sources */,
                                4E3EAE642A990778009F1BE8 /* 
LaunchAnimationView.swift in Sources */,
-                               4E3EAE652A990778009F1BE8 /* SideBarView.swift 
in Sources */,
                                E37AA62A2AF197E5003850CF /* Model+Refund.swift 
in Sources */,
                                4E3EAE682A990778009F1BE8 /* WalletModel.swift 
in Sources */,
                                4E3EAE692A990778009F1BE8 /* URLSheet.swift in 
Sources */,
@@ -1126,6 +1134,7 @@
                                4E3EAE6F2A990778009F1BE8 /* TalerStrings.swift 
in Sources */,
                                4E3EAE702A990778009F1BE8 /* 
CurrencyInputView.swift in Sources */,
                                4E3EAE712A990778009F1BE8 /* URL+id+iban.swift 
in Sources */,
+                               4EEC3A712B2285A200D05F9D /* 
WithdrawExchangeV.swift in Sources */,
                                4EDBDCD92AB787CB00925C02 /* CallStack.swift in 
Sources */,
                                4E3EAE722A990778009F1BE8 /* 
RequestPayment.swift in Sources */,
                                4E3EAE732A990778009F1BE8 /* SettingsItem.swift 
in Sources */,
@@ -1186,11 +1195,12 @@
                                4EB3136129FEE79B007D68BC /* P2PReadyV.swift in 
Sources */,
                                4EB0956B2989CBFE0043A8A1 /* 
TextFieldAlert.swift in Sources */,
                                4EBA82AD2A3F580500E5F39A /* 
QuiteSomeCoins.swift in Sources */,
+                               4E2D8DD62B45822A00234039 /* AmountV.swift in 
Sources */,
                                4EBA56412A7FF5200084948B /* 
PayTemplateView.swift in Sources */,
                                4EB431672A1E55C700C5690E /* 
ManualWithdrawDone.swift in Sources */,
                                4E753A082A0B6A5F002D9328 /* ShareSheet.swift in 
Sources */,
                                4EC400902AE8019700DF72C7 /* 
ExchangeRowView.swift in Sources */,
-                               4EB0956C2989CBFE0043A8A1 /* AmountView.swift in 
Sources */,
+                               4EB0956C2989CBFE0043A8A1 /* AmountRowV.swift in 
Sources */,
                                4E605DBB2AB05FB6002FB9A7 /* BarGraph.swift in 
Sources */,
                                4E3B4BC32A42252300CC88B8 /* P2pAcceptDone.swift 
in Sources */,
                                4E363CBE2A23CB2100D7E98C /* 
AnyTransition+backslide.swift in Sources */,
@@ -1209,11 +1219,9 @@
                                4EB095152989CBB00043A8A1 /* 
Model+Settings.swift in Sources */,
                                4EC4008D2AE5664100DF72C7 /* 
CharacterSet+contains.swift in Sources */,
                                4EB095692989CBFE0043A8A1 /* ErrorView.swift in 
Sources */,
-                               4E2E5F802AEE30DA0027FA8A /* AmountRowV.swift in 
Sources */,
                                4E3B4BC72A429F2A00CC88B8 /* 
View+Notification.swift in Sources */,
                                4EB0956E2989CBFE0043A8A1 /* Model+Pending.swift 
in Sources */,
                                4EB095522989CBFE0043A8A1 /* 
ExchangeListView.swift in Sources */,
-                               4EB095642989CBFE0043A8A1 /* 
WithdrawProgressView.swift in Sources */,
                                E37AA62F2AF19BE0003850CF /* RefundURIView.swift 
in Sources */,
                                4EEC157A29F9427F00D46A03 /* QRSheet.swift in 
Sources */,
                                4E3B4BC12A41E6C200CC88B8 /* 
P2pReceiveURIView.swift in Sources */,
@@ -1221,7 +1229,6 @@
                                4EB095582989CBFE0043A8A1 /* 
TransactionDetailView.swift in Sources */,
                                4EB095202989CBCB0043A8A1 /* WalletCore.swift in 
Sources */,
                                4EB095672989CBFE0043A8A1 /* 
LaunchAnimationView.swift in Sources */,
-                               4EB095662989CBFE0043A8A1 /* SideBarView.swift 
in Sources */,
                                E37AA62B2AF197E5003850CF /* Model+Refund.swift 
in Sources */,
                                4EB095162989CBB00043A8A1 /* WalletModel.swift 
in Sources */,
                                4EB0955A2989CBFE0043A8A1 /* URLSheet.swift in 
Sources */,
@@ -1233,6 +1240,7 @@
                                4EB0950A2989CB7C0043A8A1 /* TalerStrings.swift 
in Sources */,
                                4EA551252A2C923600FEC9A8 /* 
CurrencyInputView.swift in Sources */,
                                4E363CBC2A237E0900D7E98C /* URL+id+iban.swift 
in Sources */,
+                               4EEC3A722B2285A200D05F9D /* 
WithdrawExchangeV.swift in Sources */,
                                4EDBDCDA2AB787CB00925C02 /* CallStack.swift in 
Sources */,
                                4E9320452A1645B600A87B0E /* 
RequestPayment.swift in Sources */,
                                4EB095502989CBFE0043A8A1 /* SettingsItem.swift 
in Sources */,
@@ -1280,12 +1288,12 @@
                4E3EAE872A990778009F1BE8 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon2;
                                CLANG_ENABLE_MODULES = YES;
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 23;
+                               CURRENT_PROJECT_VERSION = 1;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
@@ -1296,14 +1304,14 @@
                                INFOPLIST_KEY_NSHumanReadableCopyright = "© 
Taler-Systems.com";
                                
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
                                INFOPLIST_KEY_UILaunchScreen_Generation = YES;
-                               INFOPLIST_KEY_UISupportedInterfaceOrientations 
= UIInterfaceOrientationPortrait;
+                               INFOPLIST_KEY_UISupportedInterfaceOrientations 
= "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight 
UIInterfaceOrientationPortrait";
                                
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
                                IPHONEOS_DEPLOYMENT_TARGET = 15.0;
                                LD_RUNPATH_SEARCH_PATHS = (
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               MARKETING_VERSION = 0.9.3;
+                               MARKETING_VERSION = 0.9.4;
                                PRODUCT_BUNDLE_IDENTIFIER = 
"com.taler-systems.talerwallet-1";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1311,6 +1319,7 @@
                                SUPPORTS_MACCATALYST = NO;
                                SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
                                SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG 
GNU_TALER PRINT_CHANGES";
                                SWIFT_EMIT_LOC_STRINGS = YES;
                                TARGETED_DEVICE_FAMILY = 1;
                                VALIDATE_WORKSPACE = YES;
@@ -1320,13 +1329,13 @@
                4E3EAE882A990778009F1BE8 /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon2;
                                ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME 
= AccentColor;
                                CLANG_ENABLE_MODULES = YES;
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 23;
+                               CURRENT_PROJECT_VERSION = 1;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
@@ -1337,14 +1346,14 @@
                                INFOPLIST_KEY_NSHumanReadableCopyright = "© 
Taler-Systems.com";
                                
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
                                INFOPLIST_KEY_UILaunchScreen_Generation = YES;
-                               INFOPLIST_KEY_UISupportedInterfaceOrientations 
= UIInterfaceOrientationPortrait;
+                               INFOPLIST_KEY_UISupportedInterfaceOrientations 
= "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight 
UIInterfaceOrientationPortrait";
                                
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
                                IPHONEOS_DEPLOYMENT_TARGET = 15.0;
                                LD_RUNPATH_SEARCH_PATHS = (
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               MARKETING_VERSION = 0.9.3;
+                               MARKETING_VERSION = 0.9.4;
                                PRODUCT_BUNDLE_IDENTIFIER = 
"com.taler-systems.talerwallet-1";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1352,6 +1361,7 @@
                                SUPPORTS_MACCATALYST = NO;
                                SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
                                SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = 
"GNU_TALER PRINT_CHANGES";
                                SWIFT_EMIT_LOC_STRINGS = YES;
                                TARGETED_DEVICE_FAMILY = 1;
                                VALIDATE_WORKSPACE = YES;
@@ -1479,12 +1489,12 @@
                D14AFD4824D232B500C51073 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon2;
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon3;
                                CLANG_ENABLE_MODULES = YES;
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 31;
+                               CURRENT_PROJECT_VERSION = 1;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
@@ -1495,14 +1505,14 @@
                                INFOPLIST_KEY_NSHumanReadableCopyright = "© 
Taler-Systems.com";
                                
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
                                INFOPLIST_KEY_UILaunchScreen_Generation = YES;
-                               INFOPLIST_KEY_UISupportedInterfaceOrientations 
= UIInterfaceOrientationPortrait;
+                               INFOPLIST_KEY_UISupportedInterfaceOrientations 
= "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight 
UIInterfaceOrientationPortrait";
                                
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
                                IPHONEOS_DEPLOYMENT_TARGET = 15.0;
                                LD_RUNPATH_SEARCH_PATHS = (
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               MARKETING_VERSION = 0.9.3;
+                               MARKETING_VERSION = 0.9.4;
                                PRODUCT_BUNDLE_IDENTIFIER = 
"com.taler-systems.talerwallet-2";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1510,7 +1520,7 @@
                                SUPPORTS_MACCATALYST = NO;
                                SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
                                SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
-                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG 
TABBAR";
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG 
TALER_WALLET";
                                SWIFT_EMIT_LOC_STRINGS = YES;
                                TARGETED_DEVICE_FAMILY = 1;
                                VALIDATE_WORKSPACE = YES;
@@ -1520,13 +1530,13 @@
                D14AFD4924D232B500C51073 /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon2;
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon3;
                                ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME 
= AccentColor;
                                CLANG_ENABLE_MODULES = YES;
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 31;
+                               CURRENT_PROJECT_VERSION = 1;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
@@ -1537,14 +1547,14 @@
                                INFOPLIST_KEY_NSHumanReadableCopyright = "© 
Taler-Systems.com";
                                
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
                                INFOPLIST_KEY_UILaunchScreen_Generation = YES;
-                               INFOPLIST_KEY_UISupportedInterfaceOrientations 
= UIInterfaceOrientationPortrait;
+                               INFOPLIST_KEY_UISupportedInterfaceOrientations 
= "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight 
UIInterfaceOrientationPortrait";
                                
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
                                IPHONEOS_DEPLOYMENT_TARGET = 15.0;
                                LD_RUNPATH_SEARCH_PATHS = (
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               MARKETING_VERSION = 0.9.3;
+                               MARKETING_VERSION = 0.9.4;
                                PRODUCT_BUNDLE_IDENTIFIER = 
"com.taler-systems.talerwallet-2";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1552,7 +1562,7 @@
                                SUPPORTS_MACCATALYST = NO;
                                SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
                                SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
-                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = TABBAR;
+                               SWIFT_ACTIVE_COMPILATION_CONDITIONS = 
TALER_WALLET;
                                SWIFT_EMIT_LOC_STRINGS = YES;
                                TARGETED_DEVICE_FAMILY = 1;
                                VALIDATE_WORKSPACE = YES;
@@ -1703,6 +1713,14 @@
 /* End XCConfigurationList section */
 
 /* Begin XCRemoteSwiftPackageReference section */
+               4E2D8DD12B3F513800234039 /* XCRemoteSwiftPackageReference 
"swift-markdown-ui-standalone" */ = {
+                       isa = XCRemoteSwiftPackageReference;
+                       repositoryURL = 
"https://github.com/Fesh-com/swift-markdown-ui-standalone";;
+                       requirement = {
+                               branch = main;
+                               kind = branch;
+                       };
+               };
                4E3EAE172A990778009F1BE8 /* XCRemoteSwiftPackageReference 
"AnyCodable" */ = {
                        isa = XCRemoteSwiftPackageReference;
                        repositoryURL = 
"https://github.com/Flight-School/AnyCodable";;
@@ -1735,6 +1753,14 @@
                                minimumVersion = 0.1.0;
                        };
                };
+               4EE1718C2B49EB7200BF9FF5 /* XCRemoteSwiftPackageReference 
"swift-collections" */ = {
+                       isa = XCRemoteSwiftPackageReference;
+                       repositoryURL = 
"https://github.com/Fesh-com/swift-collections";;
+                       requirement = {
+                               branch = main;
+                               kind = branch;
+                       };
+               };
                4EEC157429F8ECBF00D46A03 /* XCRemoteSwiftPackageReference 
"CodeScanner" */ = {
                        isa = XCRemoteSwiftPackageReference;
                        repositoryURL = 
"https://github.com/twostraws/CodeScanner";;
@@ -1754,6 +1780,11 @@
 /* End XCRemoteSwiftPackageReference section */
 
 /* Begin XCSwiftPackageProductDependency section */
+               4E2D8DD22B3F513800234039 /* MarkdownUI */ = {
+                       isa = XCSwiftPackageProductDependency;
+                       package = 4E2D8DD12B3F513800234039 /* 
XCRemoteSwiftPackageReference "swift-markdown-ui-standalone" */;
+                       productName = MarkdownUI;
+               };
                4E3EAE152A990778009F1BE8 /* taler-swift */ = {
                        isa = XCSwiftPackageProductDependency;
                        productName = "taler-swift";
@@ -1778,6 +1809,21 @@
                        package = 4EB094FB29897D280043A8A1 /* 
XCRemoteSwiftPackageReference "SymLog" */;
                        productName = SymLog;
                };
+               4EE171872B49635800BF9FF5 /* MarkdownUI */ = {
+                       isa = XCSwiftPackageProductDependency;
+                       package = 4E2D8DD12B3F513800234039 /* 
XCRemoteSwiftPackageReference "swift-markdown-ui-standalone" */;
+                       productName = MarkdownUI;
+               };
+               4EE1718F2B49FE2B00BF9FF5 /* OrderedCollections */ = {
+                       isa = XCSwiftPackageProductDependency;
+                       package = 4EE1718C2B49EB7200BF9FF5 /* 
XCRemoteSwiftPackageReference "swift-collections" */;
+                       productName = OrderedCollections;
+               };
+               4EE171912B49FE4E00BF9FF5 /* OrderedCollections */ = {
+                       isa = XCSwiftPackageProductDependency;
+                       package = 4EE1718C2B49EB7200BF9FF5 /* 
XCRemoteSwiftPackageReference "swift-collections" */;
+                       productName = OrderedCollections;
+               };
                4EEC157529F8ECBF00D46A03 /* CodeScanner */ = {
                        isa = XCSwiftPackageProductDependency;
                        package = 4EEC157429F8ECBF00D46A03 /* 
XCRemoteSwiftPackageReference "CodeScanner" */;
diff --git a/TalerWallet1/Assets.xcassets/AppIcon.appiconset/Contents.json 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/Contents.json
similarity index 80%
copy from TalerWallet1/Assets.xcassets/AppIcon.appiconset/Contents.json
copy to TalerWallet1/Assets.xcassets/AppIcon3.appiconset/Contents.json
index 2005c8a..dfcf466 100644
--- a/TalerWallet1/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/Contents.json
@@ -49,46 +49,55 @@
       "size" : "60x60"
     },
     {
+      "filename" : "ipadNotification20.png",
       "idiom" : "ipad",
       "scale" : "1x",
       "size" : "20x20"
     },
     {
+      "filename" : "ipadNotification40.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "20x20"
     },
     {
+      "filename" : "ipadSettings29.png",
       "idiom" : "ipad",
       "scale" : "1x",
       "size" : "29x29"
     },
     {
+      "filename" : "ipadSettings58.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "29x29"
     },
     {
+      "filename" : "ipadSpotlight40.png",
       "idiom" : "ipad",
       "scale" : "1x",
       "size" : "40x40"
     },
     {
+      "filename" : "ipadSpotlight80.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "40x40"
     },
     {
+      "filename" : "ipad76.png",
       "idiom" : "ipad",
       "scale" : "1x",
       "size" : "76x76"
     },
     {
+      "filename" : "ipad152.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "76x76"
     },
     {
+      "filename" : "ipadPro167.png",
       "idiom" : "ipad",
       "scale" : "2x",
       "size" : "83.5x83.5"
@@ -100,51 +109,61 @@
       "size" : "1024x1024"
     },
     {
+      "filename" : "mac16.png",
       "idiom" : "mac",
       "scale" : "1x",
       "size" : "16x16"
     },
     {
+      "filename" : "mac32.png",
       "idiom" : "mac",
       "scale" : "2x",
       "size" : "16x16"
     },
     {
+      "filename" : "mac32.png",
       "idiom" : "mac",
       "scale" : "1x",
       "size" : "32x32"
     },
     {
+      "filename" : "mac64.png",
       "idiom" : "mac",
       "scale" : "2x",
       "size" : "32x32"
     },
     {
+      "filename" : "mac128.png",
       "idiom" : "mac",
       "scale" : "1x",
       "size" : "128x128"
     },
     {
+      "filename" : "mac256.png",
       "idiom" : "mac",
       "scale" : "2x",
       "size" : "128x128"
     },
     {
+      "filename" : "mac256.png",
       "idiom" : "mac",
       "scale" : "1x",
       "size" : "256x256"
     },
     {
+      "filename" : "mac512.png",
       "idiom" : "mac",
       "scale" : "2x",
       "size" : "256x256"
     },
     {
+      "filename" : "mac512.png",
       "idiom" : "mac",
       "scale" : "1x",
       "size" : "512x512"
     },
     {
+      "filename" : "mac1024.png",
       "idiom" : "mac",
       "scale" : "2x",
       "size" : "512x512"
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/appstore1024.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/appstore1024.png
new file mode 100644
index 0000000..91c5976
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/appstore1024.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad152.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad152.png
new file mode 100644
index 0000000..a8422d2
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad152.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad76.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad76.png
new file mode 100644
index 0000000..0ecaea8
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipad76.png differ
diff --git 
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification20.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification20.png
new file mode 100644
index 0000000..199c374
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification20.png differ
diff --git 
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification40.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification40.png
new file mode 100644
index 0000000..b4d12b4
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadNotification40.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadPro167.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadPro167.png
new file mode 100644
index 0000000..c0d8790
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadPro167.png differ
diff --git 
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings29.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings29.png
new file mode 100644
index 0000000..ce30c8f
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings29.png differ
diff --git 
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings58.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings58.png
new file mode 100644
index 0000000..663db28
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSettings58.png differ
diff --git 
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight40.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight40.png
new file mode 100644
index 0000000..b4d12b4
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight40.png differ
diff --git 
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight80.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight80.png
new file mode 100644
index 0000000..e4121dd
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/ipadSpotlight80.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone120.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone120.png
new file mode 100644
index 0000000..907d180
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone120.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone180.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone180.png
new file mode 100644
index 0000000..cc557db
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/iphone180.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac1024.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac1024.png
new file mode 100644
index 0000000..f6efbc0
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac1024.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac128.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac128.png
new file mode 100644
index 0000000..f89fd8d
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac128.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac16.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac16.png
new file mode 100644
index 0000000..eb73f4e
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac16.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac256.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac256.png
new file mode 100644
index 0000000..2d75a91
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac256.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac32.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac32.png
new file mode 100644
index 0000000..d736095
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac32.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac512.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac512.png
new file mode 100644
index 0000000..69fe35d
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac512.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac64.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac64.png
new file mode 100644
index 0000000..83d6cb8
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/mac64.png differ
diff --git 
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification40.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification40.png
new file mode 100644
index 0000000..b4d12b4
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification40.png differ
diff --git 
a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification60.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification60.png
new file mode 100644
index 0000000..9ff0069
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/notification60.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings58.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings58.png
new file mode 100644
index 0000000..663db28
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings58.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings87.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings87.png
new file mode 100644
index 0000000..27a66ff
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/settings87.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight120.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight120.png
new file mode 100644
index 0000000..907d180
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight120.png differ
diff --git a/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight80.png 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight80.png
new file mode 100644
index 0000000..e4121dd
Binary files /dev/null and 
b/TalerWallet1/Assets.xcassets/AppIcon3.appiconset/spotlight80.png differ
diff --git a/TalerWallet1/Backend/WalletBackendRequest.swift 
b/TalerWallet1/Backend/WalletBackendRequest.swift
index 293269c..9a95db2 100644
--- a/TalerWallet1/Backend/WalletBackendRequest.swift
+++ b/TalerWallet1/Backend/WalletBackendRequest.swift
@@ -31,8 +31,8 @@ struct ScopeInfo: Codable, Hashable {
         case auditor
     }
     var type: ScopeInfoType
-    var url: String?    // only for "exchange"
-    var currency: String
+    var url: String?            // only for "exchange"
+    var currency: String        // 3-char ISO 4217 code for global currency. 
Regional MUST be >= 4 letters
 
     public static func == (lhs: ScopeInfo, rhs: ScopeInfo) -> Bool {
         if let lhsBaseURL = lhs.url {
diff --git a/TalerWallet1/Backend/WalletCore.swift 
b/TalerWallet1/Backend/WalletCore.swift
index 76ba1ab..f081aac 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -153,7 +153,7 @@ extension WalletCore {
                                  userInfo: [AnyHashable: Any]? = nil) {
         Task { // runs on MainActor
             await postNotificationM(aName, object: anObject, userInfo: 
userInfo)
-            logger.info("Notification sent: \(aName.rawValue)")
+//            logger.info("Notification sent: \(aName.rawValue, privacy: 
.public)")
         }
     }
 
@@ -193,26 +193,43 @@ extension WalletCore {
             if components.count >= 3 {  // txn:$txtype:$uid
                 if let type = TransactionType(rawValue: components[1]) {
                     guard type != .refresh else { return }
-                    switch decoded.newTxState.major {
+                    let newMajor = decoded.newTxState.major
+                    let newMinor = decoded.newTxState.minor
+                    switch newMajor {
                         case .done:
                             logger.info("Done: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
-                            Controller.shared.playSound(type.isIncoming ? 2 : 
1)
+                            if type.isWithdrawal {
+                                Controller.shared.playSound(2)  // 
payment_received only for withdrawals
+                            } else if !type.isIncoming {
+                                Controller.shared.playSound(1)  // 
payment_sent for all outgoing tx
+                            }
                             postNotification(.TransactionDone, userInfo: 
[TRANSACTIONTRANSITION: decoded])
                             return
+                        case .aborting:
+                            if let newMinor {
+                                if newMinor == .refreshExpired {
+                                    logger.warning("RefreshExpired: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
+                                    Controller.shared.playSound(0)
+                                    postNotification(.TransactionExpired, 
userInfo: [TRANSACTIONTRANSITION: decoded])
+                                    return
+                                }
+                            }
+                            logger.warning("Unknown aborting: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
+                            postNotification(.TransactionStateTransition, 
userInfo: [TRANSACTIONTRANSITION: decoded])
                         case .expired:
                             logger.warning("Expired: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
                             Controller.shared.playSound(0)
                             postNotification(.TransactionExpired, userInfo: 
[TRANSACTIONTRANSITION: decoded])
                             return
                         case .pending:
-                            if let newMinor = decoded.newTxState.minor {
+                            if let newMinor {
                                 if newMinor == .ready {
                                     logger.log("PendingReady: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
                                     postNotification(.PendingReady, userInfo: 
[TRANSACTIONTRANSITION: decoded])
                                     return
                                 } else if newMinor == .exchangeWaitReserve     
 // user did confirm on bank website
                                        || newMinor == .withdrawCoins {         
 // coin-withdrawal has started
-                                    logger.log("DismissSheet: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
+//                                    logger.log("DismissSheet: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
                                     postNotification(.DismissSheet, userInfo: 
[TRANSACTIONTRANSITION: decoded])
                                     return
                                 } else if newMinor == .kyc {       // user did 
confirm on bank website, but KYC is needed
@@ -220,11 +237,17 @@ extension WalletCore {
                                     postNotification(.KYCrequired, userInfo: 
[TRANSACTIONTRANSITION: decoded])
                                     return
                                 }
+                                logger.log("Pending:\(newMinor.rawValue, 
privacy: .public) \(decoded.transactionId, privacy: .private(mask: .hash))")
+                            } else {
+                                logger.log("Pending: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
                             }
-                            logger.log("Pending: \(decoded.transactionId, 
privacy: .private(mask: .hash))")
                             postNotification(.TransactionStateTransition, 
userInfo: [TRANSACTIONTRANSITION: decoded])
                         default:
-                            logger.warning("Unknow transition: 
\(decoded.transactionId, privacy: .private(mask: .hash))")
+                            if let newMinor {
+                                logger.warning("\(newMajor.rawValue, privacy: 
.public):\(newMinor.rawValue, privacy: .public) \(decoded.transactionId, 
privacy: .private(mask: .hash))")
+                            } else {
+                                logger.warning("\(newMajor.rawValue, privacy: 
.public): \(decoded.transactionId, privacy: .private(mask: .hash))")
+                            }
                             postNotification(.TransactionStateTransition, 
userInfo: [TRANSACTIONTRANSITION: decoded])
                     } // switch
                 } // type
@@ -345,6 +368,7 @@ print("\n❗️ WalletCore.swift:251 Notification: ", 
anyPayload, "\n")        /
                 self.requestsMade += 1
                 self.semaphore.signal()         // free requestsMade
               self.symLog.log(jsonString)
+              self.logger.log("sendRequest \(requestId, privacy: .public): 
\(request.operation, privacy: .public)")
                 self.quickjs.sendMessage(message: jsonString)
             } catch {       // call completion
                 self.semaphore.signal()
@@ -364,7 +388,7 @@ extension WalletCore {
             sendRequest(request: reqData) { requestId, timeSent, result, error 
in
                 let timeUsed = Date.now - timeSent
                 let millisecs = timeUsed.milliseconds
-                self.logger.info("Request \(requestId) took \(millisecs) ms")
+                self.logger.info("Request \(requestId, privacy: .public) took 
\(millisecs, privacy: .public) ms")
                 var err: Error? = nil
                 if let json = result {
                     do {
diff --git a/TalerWallet1/Controllers/Controller.swift 
b/TalerWallet1/Controllers/Controller.swift
index 05f5d77..78a3686 100644
--- a/TalerWallet1/Controllers/Controller.swift
+++ b/TalerWallet1/Controllers/Controller.swift
@@ -35,9 +35,10 @@ class Controller: ObservableObject {
     private let symLog = SymLogC()
 
     @Published var backendState: BackendState = .none       // only used for 
launch animation
-    @Published var currencyTicker: Int = 0
+    @Published var currencyTicker: Int = 0                  // updates 
whenever a new currency is added
     @AppStorage("useHaptics") var useHaptics: Bool = true   // extension 
mustn't define this, so it must be here
-    @AppStorage("playSounds") var playSounds: Int = 1       // extension 
mustn't define this, so it must be here
+    @AppStorage("playSoundsI") var playSoundsI: Int = 1     // extension 
mustn't define this, so it must be here
+    @AppStorage("playSoundsB") var playSoundsB: Bool = true
     @AppStorage("talerFont") var talerFont: Int = 0         // extension 
mustn't define this, so it must be here
     let hapticCapability = CHHapticEngine.capabilitiesForHardware()
     let logger = Logger(subsystem: "net.taler.gnu", category: "Controller")
@@ -59,25 +60,63 @@ class Controller: ObservableObject {
         currencyInfos = []
     }
 // MARK: -
-    func hasInfo(for currency: String) -> Bool {
+    func hasInfo(for currency: String) -> CurrencyInfo? {
         for info in currencyInfos {
             if info.scope.currency == currency {
-                return true
+                return info
             }
         }
-        return false
+        return nil
     }
 
+    func info(for currency: String) -> CurrencyInfo? {
+        for info in currencyInfos {
+            if info.scope.currency == currency {
+                return info
+            }
+        }
+        return nil
+    }
     func info(for currency: String, _ ticker: Int) -> CurrencyInfo {
         if ticker != currencyTicker {
             print("❗️Yikes")
         }
-        for info in currencyInfos {
-            if info.scope.currency == currency {
-                return info
+        return info(for: currency) ?? CurrencyInfo.zero(currency)
+    }
+
+    @MainActor
+    func getInfo(from exchangeBaseUrl: String, model: WalletModel) async -> 
CurrencyInfo? {
+        if let exchange = await model.getExchangeByUrl(url: exchangeBaseUrl) {
+//            let scopeInfo = exchange.scopeInfo
+//            if let info = hasInfo(for: scopeInfo.currency) {
+//                return info
+//            }
+//            do {
+//                let info = try await model.getCurrencyInfoM(scope: 
scopeInfo, delay: 0)
+//                await setInfo(info)
+//                return info
+//            } catch {
+//                return nil
+//            }
+
+            if let scopeInfo = exchange.scopeInfo {
+                if let info = hasInfo(for: scopeInfo.currency) {
+                    return info
+                }
+                do {
+                    let info = try await model.getCurrencyInfoM(scope: 
scopeInfo, delay: 0)
+                    await setInfo(info)
+                    return info
+                } catch {
+                    return nil
+                }
+            } else {
+                // TODO: Error "Can't get scopeInfo"
             }
+        } else {
+            // TODO: Error "Can't get Exchange Info"
         }
-        return CurrencyInfo.zero(currency)
+        return nil
     }
 
     @MainActor
@@ -127,7 +166,7 @@ extension Controller {
         symLog.log(url)
         guard let scheme = url.scheme else {return UrlCommand.unknown}
         var uncrypted = false
-        switch scheme {
+        switch scheme.lowercased() {
             case "taler+http":
                 uncrypted = true
                 fallthrough
@@ -157,7 +196,7 @@ extension Controller {
             symLog.log("uncrypted: taler://\(command)")
             // TODO: uncrypted
         }
-        switch command {
+        switch command.lowercased() {
             case "withdraw":
                 return .withdraw
             case "withdraw-exchange":
diff --git a/TalerWallet1/Controllers/DebugViewC.swift 
b/TalerWallet1/Controllers/DebugViewC.swift
index fd6551b..33a15d5 100644
--- a/TalerWallet1/Controllers/DebugViewC.swift
+++ b/TalerWallet1/Controllers/DebugViewC.swift
@@ -110,7 +110,7 @@ struct DebugViewV: View {
     @EnvironmentObject private var debugViewC: DebugViewC
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
 //        let _ = Self._printChanges()
 //        let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -161,10 +161,10 @@ class DebugViewC: ObservableObject {
             if viewID == 0 {
                 logger.log("switching ON, \(newID, privacy: .public)")
                 viewID = newID                              // publish ON
-                announce(this: "Current view is: \(newID).")
+//                announce(this: "Current view is: \(newID).")
             } else if viewID != newID {
                 logger.log("switching from \(self.viewID, privacy: .public) to 
\(newID, privacy: .public)")
-                announce(this: "View switched from \(self.viewID) to 
\(newID).")
+//                announce(this: "View switched from \(self.viewID) to 
\(newID).")
                 viewID = newID                              // publish new 
viewID
             } else {
                 logger.log("\(newID, privacy: .public) stays")
diff --git a/TalerWallet1/Controllers/PublicConstants.swift 
b/TalerWallet1/Controllers/PublicConstants.swift
index da8bb9a..248ce6c 100644
--- a/TalerWallet1/Controllers/PublicConstants.swift
+++ b/TalerWallet1/Controllers/PublicConstants.swift
@@ -24,8 +24,10 @@ public let DONE_OUTGOING = "minus.circle.fill"
 public let HTTPS = "https://";
 //public let DEMOBANK = HTTPS + "bAnK.dEmO.tAlEr.nEt"             // should be 
weird to read, but still work
 //public let DEMOEXCHANGE = HTTPS + "eXcHaNgE.dEmO.tAlEr.nEt"
-public let DEMO = ".demo.taler.net"
-public let TEST = ".test.taler.net"
+public let TALER = "taler.net"
+public let TALER_NET = HTTPS + TALER
+public let DEMO = ".demo." + TALER
+public let TEST = ".test." + TALER
 
 public let DEMOBANK = HTTPS + "bank" + DEMO
 public let DEMOSHOP = HTTPS + "shop" + DEMO
diff --git a/TalerWallet1/Controllers/TalerWallet1App.swift 
b/TalerWallet1/Controllers/TalerWallet1App.swift
index 043d646..694e05c 100644
--- a/TalerWallet1/Controllers/TalerWallet1App.swift
+++ b/TalerWallet1/Controllers/TalerWallet1App.swift
@@ -104,7 +104,7 @@ final class ViewState : ObservableObject {
     @Published var rootViewId = UUID()
     let logger = Logger(subsystem: "net.taler.gnu", category: "ViewState")
 
-    public func popToRootView() -> Void {
+    public func popToRootView(_ stack: CallStack) -> Void {
         logger.info("popToRootView")
         rootViewId = UUID() // setting a new ID will cause tableView 
popToRootView behaviour
     }
diff --git a/TalerWallet1/Helper/Controller+playSound.swift 
b/TalerWallet1/Helper/Controller+playSound.swift
index 7e76e36..c86858b 100644
--- a/TalerWallet1/Helper/Controller+playSound.swift
+++ b/TalerWallet1/Helper/Controller+playSound.swift
@@ -8,6 +8,32 @@ import UIKit
 
 extension Controller {
 
+    @MainActor
+    func hapticNotification(_ feedbackType: 
UINotificationFeedbackGenerator.FeedbackType) {
+        if useHaptics {
+            /// call when a notification is displayed, passing the 
corresponding type
+            
UINotificationFeedbackGenerator().notificationOccurred(feedbackType)
+        }
+    }
+
+    @MainActor
+    func hapticFeedback(_ feedbackStyle: 
UIImpactFeedbackGenerator.FeedbackStyle) {
+        /// call when your UI element impacts something else
+        UIImpactFeedbackGenerator(style: feedbackStyle).impactOccurred()
+    }
+
+    @MainActor
+    func hapticFeedback(intensity: CGFloat) {
+        /// call when your UI element impacts something else with a specific 
intensity [0.0, 1.0]
+        UIImpactFeedbackGenerator().impactOccurred(intensity: intensity)
+    }
+
+    @MainActor
+    func hapticFeedback() {
+        /// call when the selection changes (not on initial selection)
+        UISelectionFeedbackGenerator().selectionChanged()
+    }
+
     /// 0 = failure, 1 = received, 2 = sent
     @MainActor func playSound(_ number: Int) {
         let sysVolume = AVAudioSession.sharedInstance().outputVolume
@@ -31,7 +57,6 @@ extension Controller {
 //        logger.log("❗️sys:\(sysVolume)  vol:\(volume)")
 
         var soundID: SystemSoundID = 0
-        let notificationGenerator = useHaptics ? 
UINotificationFeedbackGenerator() : nil
         if number > 9 {
             soundID = UInt32(number)
         } else {
@@ -43,9 +68,9 @@ extension Controller {
             AudioServicesCreateSystemSoundID(fileURL as CFURL, &soundID)
             logger.log("\(sound, privacy: .public) \(soundID)")
         }
-        if number == 0 || number > 9 || playSounds < 0 {
+        if number == 0 || number > 9 || playSoundsI < 0 {
             AudioServicesPlaySystemSound(soundID);
-        } else if playSounds > 0 {
+        } else if playSoundsI > 0 && playSoundsB {
             if let url = Bundle.main.url(forResource: (number == 1) ? 
"payment_sent"
                                                                     : 
"payment_received",
                                        withExtension: "m4a") {
@@ -58,9 +83,6 @@ extension Controller {
                 AudioServicesPlaySystemSound(soundID);
             }
         }
-        if let notificationGenerator {
-            notificationGenerator.notificationOccurred(number == 0 ? .error : 
.success)
-        }
-
+        hapticNotification(number == 0 ? .error : .success)
     }
 }
diff --git a/TalerWallet1/Helper/CurrencySpecification.swift 
b/TalerWallet1/Helper/CurrencySpecification.swift
index 5b15d9a..5422183 100644
--- a/TalerWallet1/Helper/CurrencySpecification.swift
+++ b/TalerWallet1/Helper/CurrencySpecification.swift
@@ -5,6 +5,20 @@
 import Foundation
 import taler_swift
 
+extension Locale {
+    static var preferredLanguageCode: String {
+        guard let preferredLanguage = preferredLanguages.first,
+              let code = Locale(identifier: preferredLanguage).languageCode 
else {
+            return "en"
+        }
+        return code
+    }
+
+    static var preferredLanguageCodes: [String] {
+        return Locale.preferredLanguages.compactMap({Locale(identifier: 
$0).languageCode})
+    }
+}
+
 extension Locale {
     func leadingCurrencySymbol() -> Bool {
         let currencyFormatter = NumberFormatter()
@@ -56,7 +70,6 @@ public struct CurrencyInfo {
     public static func zero(_ currency: String) -> CurrencyInfo {
         let scope = ScopeInfo(type: .global, currency: currency)
         let specs = CurrencySpecification(name: currency,
-                                          code: currency,
                          fractionalInputDigits: 0,
                         fractionalNormalDigits: 0,
                   fractionalTrailingZeroDigits: 0,
@@ -155,7 +168,6 @@ public struct CurrencySpecification2: Codable, Sendable {
 public struct CurrencySpecification: Codable, Sendable {
     enum CodingKeys: String, CodingKey {
         case name = "name"
-        case code = "code"
         case fractionalInputDigits = "num_fractional_input_digits"
         case fractionalNormalDigits = "num_fractional_normal_digits"
         case fractionalTrailingZeroDigits = 
"num_fractional_trailing_zero_digits"
@@ -163,8 +175,6 @@ public struct CurrencySpecification: Codable, Sendable {
     }
     /// some name for this CurrencySpecification
     let name: String
-    /// 3-char ISO 4217 code for global currency. Regional MUST be >= 4 letters
-    let code: String?
     /// how much digits the user may enter after the decimal separator
     let fractionalInputDigits: Int
     /// €,$,£: 2;  some arabic currencies: 3,  ¥: 0
@@ -199,7 +209,7 @@ public class CurrencyFormatter: NumberFormatter {
         self.hasAltUnitName0 = false
         self.leadingCurrencySymbol = false
         super.init()
-        self.locale = Locale.current
+        self.locale = Locale.autoupdatingCurrent
         self.usesGroupingSeparator = true
         self.numberStyle = .currencyISOCode         // .currency
         self.maximumFractionDigits = 8              // ensure that formatter 
will not round
@@ -243,3 +253,17 @@ public class CurrencyFormatter: NumberFormatter {
         fatalError("init(coder:) has not been implemented")
     }
 }
+// MARK: -
+#if DEBUG
+func PreviewCurrencyInfo(_ currency: String, digits: Int) -> CurrencyInfo {
+    let unitName = digits == 0 ? "テ" : "ク"  // do not use real currency 
symbols like "¥" : "€"
+    let scope = ScopeInfo(type: .global, currency: currency)
+    let specs = CurrencySpecification(name: currency,
+                                      fractionalInputDigits: digits,
+                                      fractionalNormalDigits: digits,
+                                      fractionalTrailingZeroDigits: digits,
+                                      altUnitNames: [0 : unitName])
+    let previewFormatter = CurrencyFormatter.formatter(scope: scope, specs: 
specs)
+    return CurrencyInfo(scope: scope, specs: specs, formatter: 
previewFormatter)
+}
+#endif
diff --git a/TalerWallet1/Helper/TalerDater.swift 
b/TalerWallet1/Helper/TalerDater.swift
index 427e0f9..b3f5e19 100644
--- a/TalerWallet1/Helper/TalerDater.swift
+++ b/TalerWallet1/Helper/TalerDater.swift
@@ -80,6 +80,15 @@ public class TalerDater: DateFormatter {
         }
     }
 
+    public static func accessibilityDate(_ date: Date?) -> String? {
+        if let date {
+            let formatted = date.formatted(date: .long, time: .shortened)
+//            print(formatted)
+            return formatted
+        }
+        return nil
+    }
+
     public static func dateString() -> String {
         return shared.string(from: Date())
     }
diff --git a/TalerWallet1/Helper/URL+id+iban.swift 
b/TalerWallet1/Helper/URL+id+iban.swift
index ca4689c..643c493 100644
--- a/TalerWallet1/Helper/URL+id+iban.swift
+++ b/TalerWallet1/Helper/URL+id+iban.swift
@@ -23,11 +23,11 @@ extension URL {
     }
 
     /// SwifterSwift: Dictionary of the URL's query parameters.
-    var queryParameters: [String: String]? {
+    var queryParameters: [String:String]? {
         guard let components = URLComponents(url: self, 
resolvingAgainstBaseURL: false),
               let queryItems = components.queryItems else { return nil }
 
-        var items: [String: String] = [:]
+        var items: [String:String] = [:]
 
         for queryItem in queryItems {
             items[queryItem.name] = queryItem.value
diff --git a/TalerWallet1/Helper/View+dismissTop.swift 
b/TalerWallet1/Helper/View+dismissTop.swift
index 3d11bd0..2e2a92a 100644
--- a/TalerWallet1/Helper/View+dismissTop.swift
+++ b/TalerWallet1/Helper/View+dismissTop.swift
@@ -35,12 +35,31 @@ extension View {
             if gotPresented {
                 topController.dismiss(animated: animated)
             } else {
-                print("Yikes❗️ Trying to dismiss the rootViewController!")
+                Self.findNavigationController(viewController: 
topController)?.popToRootViewController(animated: animated)
             }
         } else {
             print("Yikes❗️ There is no window/rootViewController!")
         }
     }
+    @MainActor static func findNavigationController(viewController: 
UIViewController?) -> UINavigationController? {
+        guard let viewController = viewController else {
+            return nil
+        }
+
+        if let navigationController = viewController as? UITabBarController {
+            return findNavigationController(viewController: 
navigationController.selectedViewController)
+        }
+
+        if let navigationController = viewController as? 
UINavigationController {
+            return navigationController
+        }
+
+        for childViewController in viewController.children {
+            return findNavigationController(viewController: 
childViewController)
+        }
+
+        return nil
+    }
 }
 // MARK: -
 extension View {
diff --git a/TalerWallet1/Helper/WalletColors.swift 
b/TalerWallet1/Helper/WalletColors.swift
index a358caf..11e192e 100644
--- a/TalerWallet1/Helper/WalletColors.swift
+++ b/TalerWallet1/Helper/WalletColors.swift
@@ -4,15 +4,21 @@
  */
 import SwiftUI
 
+fileprivate let grouped = true
+
 public struct WalletColors {
 
     let tint = Color(.tintColor)
-    let gray1 = Color(.systemGray)
-    let gray2 = Color(.systemGray2)
+    let gray1 = Color(.systemGray)          // uncompleted
+    let gray2 = Color(.systemGray2)         // disabled Fore
     let gray3 = Color(.systemGray3)
     let gray4 = Color(.systemGray4)
     let gray5 = Color(.systemGray5)
     let gray6 = Color(.systemGray6)
+    let gray7 = grouped ? Color(.tertiarySystemGroupedBackground)
+                        : Color(.tertiarySystemBackground)        // enabled 
Back
+    let gray8 = grouped ? Color(.secondarySystemGroupedBackground)
+                        : Color(.secondarySystemBackground)       // disabled 
Back
 
     func buttonForeColor(pressed: Bool, disabled: Bool,
                        prominent: Bool = false, balance: Bool = false) -> 
Color {
@@ -25,27 +31,29 @@ public struct WalletColors {
     func buttonBackColor(pressed: Bool, disabled: Bool,
                        prominent: Bool = false, balance: Bool = false) -> 
Color {
              balance ? (pressed ? gray5 : gray6)
-          : disabled ? gray5
+          : disabled ? gray7
          : prominent ? tint
-           : pressed ? gray5 : gray4
+           : pressed ? gray4 : gray5
     }
 
     var backgroundColor: Color {
-        gray6
+        grouped ? Color(.systemGroupedBackground)
+                : Color(.systemBackground)
     }
 
     var sideBackground: Color {
-        gray5
+        gray6
     }
 
     var fieldForeground: Color {              // text color
         Color.primary
     }
     var fieldBackground: Color {
-        Color(.systemBackground)
+        gray8
     }
 
-    var incompleteColor: Color {
+    var uncompletedColor: Color {
+        // used in TransactionRowView
         gray1
     }
     func pendingColor(_ incoming: Bool) -> Color {
diff --git a/TalerWallet1/Model/Model+Exchange.swift 
b/TalerWallet1/Model/Model+Exchange.swift
index 4764116..5b362f0 100644
--- a/TalerWallet1/Model/Model+Exchange.swift
+++ b/TalerWallet1/Model/Model+Exchange.swift
@@ -175,7 +175,7 @@ extension WalletModel {
       async -> Exchange? {
         do {
             let request = GetExchangeByUrl(exchangeBaseUrl: url)
-            logger.info("query for exchange: \(url, privacy: .public)")
+//            logger.info("query for exchange: \(url, privacy: .public)")
             let response = try await sendRequest(request)
             return response
         } catch {
@@ -199,7 +199,8 @@ extension WalletModel {
         _ = try await sendRequest(request)
     }
 
-    func getCurrencyInfo(scope: ScopeInfo, delay: UInt = 0)
+    @MainActor
+    func getCurrencyInfoM(scope: ScopeInfo, delay: UInt = 0)
       async throws -> CurrencyInfo {
         let request = GetCurrencySpecification(scope: scope)
         let response = try await sendRequest(request, ASYNCDELAY + delay)
diff --git a/TalerWallet1/Model/Model+P2P.swift 
b/TalerWallet1/Model/Model+P2P.swift
index bff8aeb..28ad60d 100644
--- a/TalerWallet1/Model/Model+P2P.swift
+++ b/TalerWallet1/Model/Model+P2P.swift
@@ -41,10 +41,10 @@ extension WalletModel {
 } // getMaxPeerPushAmountM
 // - - - - - -
 struct CheckPeerPushDebitResponse: Codable {
-    let exchangeBaseUrl: String?
+    let exchangeBaseUrl: String?                // API "2:0:1"
     let amountRaw: Amount
     let amountEffective: Amount
-    let maxExpirationDate: Timestamp?          // TODO: limit expiration (30 
days or 7 days)
+    let maxExpirationDate: Timestamp?           // API "2:0:1" TODO: limit 
expiration (30 days or 7 days)
 }
 fileprivate struct CheckPeerPushDebit: WalletBackendFormattedRequest {
     typealias Response = CheckPeerPushDebitResponse
@@ -166,7 +166,6 @@ struct PreparePeerPushCreditResponse: Codable {
     let amountRaw: Amount
     let amountEffective: Amount
     let exchangeBaseUrl: String
-//    let peerPushCreditId: String
     // the dialog transaction is already created in the local DB - must either 
accept or delete
     let transactionId: String
 }
diff --git a/TalerWallet1/Model/Model+Settings.swift 
b/TalerWallet1/Model/Model+Settings.swift
index 288e22b..eb3b019 100644
--- a/TalerWallet1/Model/Model+Settings.swift
+++ b/TalerWallet1/Model/Model+Settings.swift
@@ -35,7 +35,7 @@ fileprivate struct WithdrawTestBalanceRequest: 
WalletBackendFormattedRequest {
 extension WalletModel {
     @MainActor func loadTestKudosM(test: Bool)
     async throws {          // M for MainActor
-        let amount = Amount(currency:  test ? TESTCURRENCY : DEMOCURRENCY, 
integer: 11, fraction: 0)
+        let amount = Amount(currency:  test ? TESTCURRENCY : DEMOCURRENCY, 
cent: 1100)
         let request = WithdrawTestBalanceRequest(amount: amount,
 //                                                 bankBaseUrl: test ? 
TESTBANK : DEMOBANK,
                                                  corebankApiBaseUrl: test ? 
TESTBANK : DEMOBANK,
@@ -80,8 +80,8 @@ fileprivate struct RunIntegrationTest: 
WalletBackendFormattedRequest {
 extension WalletModel {
     @MainActor func runIntegrationTestM(newVersion: Bool, test: Bool)
     async throws {               // M for MainActor
-        let amountW = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY, 
integer: 3, fraction: 0)
-        let amountS = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY, 
integer: 1, fraction: 0)
+        let amountW = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY, 
cent: 300)
+        let amountS = Amount(currency: test ? TESTCURRENCY : DEMOCURRENCY, 
cent: 100)
         let request = RunIntegrationTest(newVersion: newVersion,
                                          exchangeBaseUrl: test ? TESTEXCHANGE 
: DEMOEXCHANGE,
 //                                         bankBaseUrl: (test ? TESTBANK : 
DEMOBANK),
@@ -92,4 +92,29 @@ extension WalletModel {
                                          amountToSpend: amountS)
         let _ = try await sendRequest(request, ASYNCDELAY)
     }
+} // runIntegrationTestM()
+  // MARK: -
+/// A request to add a test balance to the wallet.
+fileprivate struct InfiniteTransactionLoop: WalletBackendFormattedRequest {
+    struct Response: Decodable {}   // no result - getting no error back means 
success
+    func operation() -> String { "testingInfiniteTransactionLoop" }
+    func args() -> Args { Args(delayMs: delayMs,
+                               shouldFetch: shouldFetch)
+    }
+
+    let delayMs: Int32
+    let shouldFetch: Bool
+
+
+    struct Args: Encodable {
+        let delayMs: Int32
+        let shouldFetch: Bool
+    }
+}
+extension WalletModel {
+    func testingInfiniteTransaction(delayMs: Int32, shouldFetch: Bool)
+      async throws {
+        let request = InfiniteTransactionLoop(delayMs: delayMs, shouldFetch: 
shouldFetch)
+        let _ = try await sendRequest(request, ASYNCDELAY)
+    }
 } // runIntegrationTestM()
diff --git a/TalerWallet1/Model/Model+Withdraw.swift 
b/TalerWallet1/Model/Model+Withdraw.swift
index 52f8355..a2d3ca9 100644
--- a/TalerWallet1/Model/Model+Withdraw.swift
+++ b/TalerWallet1/Model/Model+Withdraw.swift
@@ -7,17 +7,25 @@ import taler_swift
 import SymLog
 fileprivate let ASYNCDELAY: UInt = 0   //set e.g to 6 or 9 seconds for 
debugging
 
-enum AccountRestrictionType: String, Decodable {
+enum AccountRestrictionType: String, Codable {
     case deny
     case regex
 }
-struct AccountRestriction: Decodable {
+
+typealias HintDict = [String:String]
+
+struct AccountRestriction: Codable, Hashable {
     var type: AccountRestrictionType
     var payto_regex: String?
     var human_hint: String?
-    var human_hint_i18n: String?
+    var human_hint_i18n: HintDict?
 }
+extension AccountRestriction: Identifiable {
+    var id: AccountRestriction {self}
+}
+
 struct WithdrawalExchangeAccountDetails: Decodable {
+    var status: String
     var bankName: String?
     var paytoUri: String
     var transferAmount: Amount
@@ -43,6 +51,23 @@ fileprivate struct GetWithdrawalDetailsForURI: 
WalletBackendFormattedRequest {
     }
 }
 // MARK: -
+/// The result from prepareWithdrawExchange
+struct WithdrawExchangeResponse: Decodable {
+    var exchangeBaseUrl: String
+    var amount: Amount?
+}
+/// A request to get an exchange's withdrawal details.
+fileprivate struct PrepareWithdrawExchange: WalletBackendFormattedRequest {
+    typealias Response = WithdrawExchangeResponse
+    func operation() -> String { "prepareWithdrawExchange" }
+    func args() -> Args { Args(talerUri: talerUri) }
+
+    var talerUri: String
+    struct Args: Encodable {
+        var talerUri: String
+    }
+}
+// MARK: -
 /// The result from getWithdrawalDetailsForAmount
 struct WithdrawalAmountDetails: Decodable {
     var amountRaw: Amount               // Amount that the user will transfer 
to the exchange
@@ -75,20 +100,24 @@ struct ExchangeTermsOfService: Decodable {
     var currentEtag: String
     var acceptedEtag: String?
     var tosStatus: ExchangeTosStatus
+    var tosAvailableLanguages: [String]
     var contentType: String
+    var contentLanguage: String?
     var content: String
 }
 /// A request to query an exchange's terms of service.
 fileprivate struct GetExchangeTermsOfService: WalletBackendFormattedRequest {
     typealias Response = ExchangeTermsOfService
     func operation() -> String { "getExchangeTos" }
-    func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl, 
acceptedFormat: acceptedFormat) }
+    func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl, 
acceptedFormat: acceptedFormat, acceptLanguage: acceptLanguage) }
 
     var exchangeBaseUrl: String
     var acceptedFormat: [String]?
+    var acceptLanguage: String?
     struct Args: Encodable {
         var exchangeBaseUrl: String
         var acceptedFormat: [String]?
+        var acceptLanguage: String?
     }
 }
 /// A request to mark an exchange's terms of service as accepted.
@@ -150,6 +179,14 @@ fileprivate struct AcceptManualWithdrawal: 
WalletBackendFormattedRequest {
 }
 // MARK: -
 extension WalletModel {
+    /// load withdraw-exchange details. Networking involved
+    @MainActor
+    func loadWithdrawalExchangeForUriM(_ talerUri: String)               // M 
for MainActor
+      async throws -> WithdrawExchangeResponse {
+        let request = PrepareWithdrawExchange(talerUri: talerUri)
+        let response = try await sendRequest(request, ASYNCDELAY)
+        return response
+    }
     /// load withdrawal details. Networking involved
     @MainActor
     func loadWithdrawalDetailsForUriM(_ talerWithdrawUri: String)              
 // M for MainActor
@@ -167,9 +204,12 @@ extension WalletModel {
         return response
     }
     @MainActor
-    func loadExchangeTermsOfServiceM(_ exchangeBaseUrl: String, 
acceptedFormat: [String])             // M for MainActor
+    func loadExchangeTermsOfServiceM(_ exchangeBaseUrl: String, 
acceptedFormat: [String], acceptLanguage: String)
+                                // M for MainActor
       async throws -> ExchangeTermsOfService {
-        let request = GetExchangeTermsOfService(exchangeBaseUrl: 
exchangeBaseUrl, acceptedFormat: acceptedFormat)
+        let request = GetExchangeTermsOfService(exchangeBaseUrl: 
exchangeBaseUrl,
+                                                 acceptedFormat: 
acceptedFormat,
+                                                 acceptLanguage: 
acceptLanguage)
         let response = try await sendRequest(request, ASYNCDELAY)
         return response
     }
diff --git a/TalerWallet1/Model/Transaction.swift 
b/TalerWallet1/Model/Transaction.swift
index 75c88a3..4fb492b 100644
--- a/TalerWallet1/Model/Transaction.swift
+++ b/TalerWallet1/Model/Transaction.swift
@@ -16,7 +16,7 @@ enum TransactionDecodingError: Error {
 }
 
 enum TransactionMinorState: String, Codable {
-      // Placeholder until D37 is fully implemented
+    // Placeholder until D37 is fully implemented
     case unknown
     case deposit
     case kyc        // KycRequired
@@ -26,6 +26,7 @@ enum TransactionMinorState: String, Codable {
     case submitPayment = "submit-payment"
     case rebindSession = "rebind-session"
     case refresh
+    case refreshExpired = "refresh-expired"
     case pickup
     case autoRefund = "auto-refund"
     case user
@@ -50,6 +51,46 @@ enum TransactionMinorState: String, Codable {
     case proposed
     case refundAvailable = "refund-available"
     case acceptRefund = "accept-refund"
+
+    var localizedState: String? {
+        switch self {
+            case .unknown:                  return self.rawValue
+            case .deposit:                  return self.rawValue
+            case .kyc:                      return String(localized: 
"MinorState.kyc", defaultValue: "KYC required", comment: "TxMinorState heading")
+            case .aml:                      return String(localized: 
"MinorState.aml", defaultValue: "AML required", comment: "TxMinorState heading")
+            case .mergeKycRequired:         return String(localized: 
"MinorState.mergekyc", defaultValue: "KYC merge", comment: "TxMinorState 
heading")
+            case .track:                    return self.rawValue
+            case .submitPayment:            return self.rawValue
+            case .rebindSession:            return self.rawValue
+            case .refresh:                  return self.rawValue
+            case .refreshExpired:           return self.rawValue
+            case .pickup:                   return self.rawValue
+            case .autoRefund:               return self.rawValue
+            case .user:                     return self.rawValue
+            case .bank:                     return self.rawValue
+            case .exchange:                 return self.rawValue    // in 
aborted
+            case .claimProposal:            return self.rawValue
+            case .checkRefund:              return self.rawValue
+            case .createPurse:              return self.rawValue
+            case .deletePurse:              return self.rawValue
+            case .ready:                    return self.rawValue
+            case .merge:                    return self.rawValue
+            case .repurchase:               return self.rawValue
+            case .bankRegisterReserve:      return self.rawValue
+            case .bankConfirmTransfer:      return String(localized: 
"MinorState.bankConfirmTransfer", defaultValue: "Waiting for bank transfer", 
comment: "TxMinorState heading")
+            case .withdrawCoins:            return self.rawValue
+            case .exchangeWaitReserve:      return self.rawValue
+            case .abortingBank:             return self.rawValue
+            case .aborting:                 return self.rawValue
+            case .refused:                  return self.rawValue
+            case .withdraw:                 return self.rawValue
+            case .merchantOrderProposed:    return self.rawValue
+            case .proposed:                 return self.rawValue
+            case .refundAvailable:          return self.rawValue
+            case .acceptRefund:             return self.rawValue
+//            default: return nil
+        }
+    }
 }
 
 enum TransactionMajorState: String, Codable {
@@ -222,7 +263,7 @@ struct TransactionCommon: Decodable, Sendable {
         do {
             return try Amount.diff(amountEffective, amountRaw)
         } catch {}
-        return Amount(currency: amountRaw.currencyStr, integer: 0, fraction: 0)
+        return Amount.zero(currency: amountRaw.currencyStr)
     }
 
     func incoming() -> Bool {
@@ -245,8 +286,7 @@ struct WithdrawalDetails: Decodable {
     var reserveIsReady: Bool
 
   /// Details for manual withdrawals:
-    /// The payto URIs that the exchange supports.
-    var exchangePaytoUris: [String]?
+    var exchangeCreditAccountDetails: [WithdrawalExchangeAccountDetails]?
 
   /// Details for bank-integrated withdrawals:
     /// Whether the bank has confirmed the withdrawal.
@@ -265,13 +305,20 @@ struct WithdrawalTransaction : Sendable{
     var details: WithdrawalTransactionDetails
 }
 
+struct RefundInfo: Decodable {
+    var amountEffective: Amount
+    var amountRaw: Amount
+    var transactionId: String
+    var timestamp: Timestamp
+}
+
 struct PaymentTransactionDetails: Decodable {
     var info: OrderShortInfo
     var proposalId: String
     var totalRefundRaw: Amount
     var totalRefundEffective: Amount
     var refundPending: Amount?
-    var refunds: [String]?           // array of refund txIDs for this payment
+    var refunds: [RefundInfo]?           // array of refund txIDs for this 
payment
     var refundQueryActive: Bool?
     var posConfirmation: String?
 }
@@ -313,15 +360,24 @@ enum RefreshReason: String, Decodable {
     case refund
     case abortPay = "abort-pay"
     case abortDeposit = "abort-deposit"
+    case abortPeerPushDebit = "abort-peer-push-debit"
     case recoup
     case backupRestored = "backup-restored"
     case scheduled
 }
+struct RefreshError: Decodable {
+    var code: Int
+    var when: Timestamp
+    var hint: String
+    var numErrors: Int
+    var errors: [HTTPError]
+}
 struct RefreshTransactionDetails: Decodable {
     var refreshReason: RefreshReason
     var originatingTransactionId: String?
     var refreshInputAmount: Amount
     var refreshOutputAmount: Amount
+    var error: RefreshError?
 }
 
 struct RefreshTransaction : Sendable{
@@ -438,8 +494,8 @@ enum Transaction: Decodable, Hashable, Identifiable, 
Sendable {
     var isP2pIncoming: Bool { isSendInvoice || isRcvCoins}
 
     var isPending    : Bool { common.txState.major == .pending }
-    var isPendingReady : Bool { common.txState.minor == .ready }
-    var isPendingKYC   : Bool { common.txState.minor == .kyc }
+    var isPendingReady : Bool { isPending && common.txState.minor == .ready }
+    var isPendingKYC   : Bool { isPending && common.txState.minor == .kyc }
     var isDone       : Bool { common.txState.major == .done }
     var isAborting   : Bool { common.txState.major == .aborting }
     var isAborted    : Bool { common.txState.major == .aborted }
@@ -452,6 +508,7 @@ enum Transaction: Decodable, Hashable, Identifiable, 
Sendable {
     var isAbortable  : Bool { common.txActions.contains(.abort) }
     var isFailable   : Bool { common.txActions.contains(.fail) }
     var isDeleteable : Bool { common.txActions.contains(.delete) }
+    var isRetryable  : Bool { common.txActions.contains(.retry) }
     var isResumable  : Bool { common.txActions.contains(.resume) }
     var isSuspendable: Bool { common.txActions.contains(.suspend) }
 
diff --git a/TalerWallet1/Model/WalletModel.swift 
b/TalerWallet1/Model/WalletModel.swift
index 288f35d..b309c38 100644
--- a/TalerWallet1/Model/WalletModel.swift
+++ b/TalerWallet1/Model/WalletModel.swift
@@ -158,7 +158,7 @@ extension WalletModel {
 // MARK: -
 ///  A request to initialize Wallet-core
 fileprivate struct ResetRequest: WalletBackendFormattedRequest {
-    func operation() -> String { "reset" }
+    func operation() -> String { "clearDb" }
     func args() -> Args { Args() }
 
     struct Args: Encodable {}                           // no arguments needed
diff --git a/TalerWallet1/Quickjs/quickjs.swift 
b/TalerWallet1/Quickjs/quickjs.swift
index 54f42bd..89728ac 100644
--- a/TalerWallet1/Quickjs/quickjs.swift
+++ b/TalerWallet1/Quickjs/quickjs.swift
@@ -23,20 +23,21 @@ func logging_callback(userdata: 
Optional<UnsafeMutableRawPointer>,
                             tag: Optional<UnsafePointer<Int8>>,
                         message: Optional<UnsafePointer<Int8>>) {
     let native = Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue()
+    let logger = native.logger
     let theTag = String(cString: tag!)
     let theMessage = String(cString: message!)
 
     switch level {
         case TALER_WALLET_LOG_ERROR:
-            native.logger.error("\(theTag, privacy: .public)  \(theMessage, 
privacy: .public)")
+            logger.error("\(theTag, privacy: .public)  \(theMessage, privacy: 
.public)")
         case TALER_WALLET_LOG_WARN:
-            native.logger.warning("\(theTag, privacy: .public)  \(theMessage, 
privacy: .public)")
+            logger.warning("\(theTag, privacy: .public)  \(theMessage, 
privacy: .public)")
         case TALER_WALLET_LOG_MESSAGE:
-            native.logger.notice("\(theTag, privacy: .public)  \(theMessage, 
privacy: .public)")
+            logger.notice("\(theTag, privacy: .public)  \(theMessage, privacy: 
.public)")
         case TALER_WALLET_LOG_INFO:
-            native.logger.info("\(theTag, privacy: .public)  \(theMessage, 
privacy: .public)")
+            logger.info("\(theTag, privacy: .public)  \(theMessage, privacy: 
.public)")
         case TALER_WALLET_LOG_TRACE:
-            native.logger.trace("\(theTag, privacy: .public)  \(theMessage, 
privacy: .public)")
+            logger.trace("\(theTag, privacy: .public)  \(theMessage, privacy: 
.public)")
         default: break
     }
 }
diff --git a/TalerWallet1/Views/Balances/BalanceRowView.swift 
b/TalerWallet1/Views/Balances/BalanceRowView.swift
index c851611..130c379 100644
--- a/TalerWallet1/Views/Balances/BalanceRowView.swift
+++ b/TalerWallet1/Views/Balances/BalanceRowView.swift
@@ -5,38 +5,53 @@
 import SwiftUI
 import taler_swift
 
-struct BalanceButton: View {
-    let amountStr: String
+struct BalanceCell: View {
+    let amount: Amount
     let sizeCategory: ContentSizeCategory
     let rowAction: () -> Void
+    let balanceDest: LazyView<TransactionsListView>?
 
     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
+    /// Renders the Balance button. "Balance" leading, amountStr trailing. If 
it doesn't fit in one row then
+    /// amount (trailing) goes underneath "Balance" (leading).
     var body: some View {
-        let balanceTitleStr = String(localized: "Balance:", comment: "Main 
view")
-        Button(action: rowAction) {
-            SingleAxisGeometryReader { width in         // e.g. 301 instead of 
313 => padding = 6
-                Group {
-                    let title = iconOnly ? "" : balanceTitleStr
-                    let titles = [(balanceTitleStr, TalerFont.uiFont(.title2)),
-                                  (amountStr,    TalerFont.uiFont(.title))]
-                    let fitsSideBySide = iconOnly || 
Self.fitsSideBySide(titles, availableWidth: width,
-                                                                  
sizeCategory: sizeCategory,
-                                                                  padding: 20, 
sameSize: false)
-                    AmountRowV(amountStr: amountStr, amountColor: .primary, 
doneOrPending: true, largeAmountFont: true,
-                               fitsHorizontal: fitsSideBySide, vertAlignment: 
.lastTextBaseline) {
-                        Text(title)
-                            .accessibilityFont(.title2)
-                            .foregroundColor(colorSchemeContrast == .increased 
? .primary : .secondary)
-                    }
+        let amountV = AmountV(amount: amount, large: true)
+            .foregroundColor(.primary)
+        let hLayout = HStack(spacing: 0) {
+                Spacer(minLength: 0)
+                amountV
+            }
+        let balanceCell = Group {
+            if iconOnly {
+                hLayout
+            } else {
+                let balanceText = Text("Balance:", comment: "Main view")
+                    .accessibilityFont(.title2)
+                    .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
+                let vLayout = VStack(alignment: .leading, spacing: 0) {
+                    balanceText
+                    hLayout
                 }
+
+                if #available(iOS 16.0, *) {
+                    ViewThatFits(in: .horizontal) {
+                        HStack(spacing: HSPACING) {
+                            balanceText
+                            hLayout
+                        }
+                        vLayout
+                    }
+                } else { vLayout } // view for iOS 15
             }
-        }   .disabled(false)
-            .buttonStyle(TalerButtonStyle(type: iconOnly ? .plain : .balance, 
aligned: .trailing))
-            .accessibilityElement(children: .combine)
-            .accessibilityHint("Will go to main transactions list.")
+        }
+        NavigationLink { balanceDest } label: {
+            balanceCell
+                .accessibilityElement(children: .combine)
+                .accessibilityHint("Will go to main transactions list.")
 //            .accessibilityLabel(balanceTitleStr + " " + amountStr)    // 
TODO: CurrencyFormatter!
+        }
     }
 }
 
@@ -45,10 +60,12 @@ struct BalanceButton: View {
 ///  Balance:               amount
 /// [Send Money]  [Request Payment]
 struct BalanceRowView: View {
+    let stack: CallStack
     let amount: Amount
     let sendAction: () -> Void
     let recvAction: () -> Void
     let rowAction: () -> Void
+    let balanceDest: LazyView<TransactionsListView>?
 
     @Environment(\.sizeCategory) var sizeCategory
     @EnvironmentObject private var controller: Controller
@@ -65,12 +82,12 @@ struct BalanceRowView: View {
     let requestTitle1 = String(localized: "RequestButton_Full", defaultValue: 
"Request\tPayment",
                                  comment: "`Request Payment´ in Balances - set 
exactly 1 \\t for line break")
     var body: some View {
-        VStack (alignment: .trailing) {
-            let currencyInfo = controller.info(for: amount.currencyStr, 
controller.currencyTicker)
-            let amountStr = amount.string(currencyInfo)
-            BalanceButton(amountStr: amountStr,
-                       sizeCategory: sizeCategory,
-                          rowAction: rowAction)
+        VStack (alignment: .trailing, spacing: 6) {
+            BalanceCell(amount: amount,
+                     sizeCategory: sizeCategory,
+                        rowAction: rowAction,
+                      balanceDest: balanceDest)
+//            .border(.red)
 
             let sendTitle = iconOnly ? sendTitle0 : sendTitle1
             let requTitle = iconOnly ? requestTitle0 : requestTitle1
@@ -86,7 +103,9 @@ struct BalanceRowView: View {
                     HStack(spacing: HSPACING) {
                         twoRowButtons.makeCopy(fitsSideBySide: true)
                     }
+//                        .border(.red)
                     VStack { twoRowButtons }
+//                        .border(.red)
                 }
             } else { // view for iOS 15
                 VStack { twoRowButtons }
@@ -102,14 +121,14 @@ struct BalanceRowView_Previews: PreviewProvider {
     var body: some View {
         let test = Amount(currency: TESTCURRENCY, cent: 123)
         let demo = Amount(currency: DEMOCURRENCY, cent: 123456)
-//        let testStr = test.string(testInfo)
-//        let demoStr = demo.string(demoInfo)
 
         List {
             Section {
-                BalanceRowView(amount: demo, sendAction: {}, recvAction: {}, 
rowAction: {})
+                BalanceRowView(stack: CallStack("Preview"), amount: demo,
+                          sendAction: {}, recvAction: {}, rowAction: {}, 
balanceDest: nil)
             }
-            BalanceRowView(amount: test, sendAction: {}, recvAction: {}, 
rowAction: {})
+            BalanceRowView(stack: CallStack("Preview"), amount: test, 
+                      sendAction: {}, recvAction: {}, rowAction: {}, 
balanceDest: nil)
         }
     }
   }
diff --git a/TalerWallet1/Views/Balances/BalancesListView.swift 
b/TalerWallet1/Views/Balances/BalancesListView.swift
index b425948..43cceb9 100644
--- a/TalerWallet1/Views/Balances/BalancesListView.swift
+++ b/TalerWallet1/Views/Balances/BalancesListView.swift
@@ -14,10 +14,6 @@ struct BalancesListView: View {
     let navTitle: String
     @Binding var balances: [Balance]
     @Binding var shouldReloadBalances: Int
-#if TABBAR  // Taler Wallet
-#else       // GNU Taler
-    let hamburgerAction: () -> Void
-#endif
 
     @EnvironmentObject private var model: WalletModel
 
@@ -93,21 +89,15 @@ struct BalancesListView: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
-#endif
-#if TABBAR  // Taler Wallet
-        let hamburger: HamburgerButton? = nil
-#else       // GNU Taler
-        let hamburger: HamburgerButton = HamburgerButton(action: 
hamburgerAction)
 #endif
         Content(symLog: symLog, stack: stack.push(), balances: $balances,
                 amountToTransfer: $amountToTransfer, summary: $summary,
                 reloadBalances: reloadBalances)
             .navigationTitle(navTitle)
-            .navigationBarItems(leading: hamburger,
-                                trailing: QRButton(action: 
checkCameraAvailable))
+            .navigationBarItems(trailing: QRButton(action: 
checkCameraAvailable))
             .alert("Scanning QR-codes requires access to the camera",
                    isPresented: $showCameraAlert,
                        actions: {   openSettingsButton
@@ -137,7 +127,7 @@ extension BalancesListView {
         var reloadBalances: (_ stack: CallStack, _ invalidateCache: Bool) 
async -> Int
 
         var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
             let _ = Self._printChanges()
             let _ = symLog?.vlog()       // just to get the # to compare it 
with .onAppear & onDisappear
 #endif
diff --git a/TalerWallet1/Views/Balances/BalancesSectionView.swift 
b/TalerWallet1/Views/Balances/BalancesSectionView.swift
index f6306d1..0667225 100644
--- a/TalerWallet1/Views/Balances/BalancesSectionView.swift
+++ b/TalerWallet1/Views/Balances/BalancesSectionView.swift
@@ -26,6 +26,7 @@ struct BalancesSectionView {
     @EnvironmentObject private var controller: Controller
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
+    @State private var showSpendingHint = true
     @State private var isShowingDetailView = false
     @State private var transactions: [Transaction] = []
     @State private var completedTransactions: [Transaction] = []
@@ -53,7 +54,7 @@ struct BalancesSectionView {
 
 extension BalancesSectionView: View {
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -61,11 +62,14 @@ extension BalancesSectionView: View {
         let currencyInfo = controller.info(for: currency, 
controller.currencyTicker)
 
         Section {
-            if "KUDOS" == currency && !balance.available.isZero {
-                Text(iconOnly ? "Visit the [Demo 
Shop](https://shop.demo.taler.net)" :
-                        "You can spend these KUDOS in the [Demo 
Shop](https://shop.demo.taler.net), or send them to another wallet.")
-                    .accessibilityFont(.body)
-                    .multilineTextAlignment(.leading)
+            let showSpendingButton = "KUDOS" == currency && 
!balance.available.isZero
+            if showSpendingButton {
+                if !iconOnly && showSpendingHint {
+                    Text("You can spend these KUDOS in the Demo Shop, or send 
them to another wallet.")
+                        .accessibilityFont(.body)
+                        .multilineTextAlignment(.leading)
+                        .listRowSeparator(.hidden)
+                }
             }
             BalancesNavigationLinksView(symLog: symLog,
                                          stack: stack.push(),
@@ -75,6 +79,16 @@ extension BalancesSectionView: View {
                          completedTransactions: $completedTransactions,
                                reloadAllAction: reloadCompleted,
                                reloadOneAction: reloadOneAction)
+            if showSpendingButton {
+                let title = String(localized: "LinkTitle_DEMOSHOP", 
defaultValue: "Spend test money")
+                Button(title) {
+                    showSpendingHint = false
+                    UIApplication.shared.open(URL(string:DEMOSHOP)!, options: 
[:])
+                }
+                .buttonStyle(TalerButtonStyle(type: .bordered, narrow: false, 
aligned: .center))
+                .accessibilityHint("Will go to the demo shop website.")
+                .listRowSeparator(.hidden)
+            }
             if pendingTransactions.count > 0 {
                 BalancesPendingRowView(symLog: symLog,
                                         stack: stack.push(),
@@ -225,7 +239,7 @@ fileprivate struct BalancesNavigationLinksView: View {
             }, tag: 2, selection: $buttonSelected
             ) { EmptyView() }.frame(width: 0).opacity(0).hidden()           // 
RequestPayment
 
-            NavigationLink(destination: LazyView {
+            let balanceDest = LazyView {
                 TransactionsListView(stack: stack.push(),
                                   navTitle: String(localized: "Transactions", 
comment: "ViewTitle of TransactionList"),
                                   currency: currency,
@@ -233,17 +247,19 @@ fileprivate struct BalancesNavigationLinksView: View {
                                 showUpDown: true,
                            reloadAllAction: reloadAllAction,
                            reloadOneAction: reloadOneAction)
-            }, tag: 3, selection: $buttonSelected
-            ) { EmptyView() }.frame(width: 0).opacity(0).hidden()           // 
TransactionsListView
+            }
+            NavigationLink(destination: balanceDest, tag: 3, selection: 
$buttonSelected)
+                { EmptyView() }.frame(width: 0).opacity(0).hidden()           
// TransactionsListView
 
-            BalanceRowView(amount: balance.available,
-                       sendAction: {
+            BalanceRowView(stack: stack.push(),
+                          amount: balance.available,
+                      sendAction: {
                             selectAndUpdate(1)      // trigger SendAmount 
NavigationLink
-                    }, recvAction: {
+                   }, recvAction: {
                             selectAndUpdate(2)      // trigger RequestPayment 
NavigationLink
-                     }, rowAction: {
+                    }, rowAction: {
                             buttonSelected = 3      // trigger TransactionList 
NavigationLink
-                     })
+                  }, balanceDest: balanceDest)
         }
     }
 }
diff --git a/TalerWallet1/Views/Balances/PendingRowView.swift 
b/TalerWallet1/Views/Balances/PendingRowView.swift
index 62f3292..65bf5d6 100644
--- a/TalerWallet1/Views/Balances/PendingRowView.swift
+++ b/TalerWallet1/Views/Balances/PendingRowView.swift
@@ -12,8 +12,7 @@ struct PendingRowView: View {
     let shouldConfirm: Bool
     let needsKYC: Bool
 
-    @Environment(\.sizeCategory) var sizeCategory
-    @EnvironmentObject private var controller: Controller
+//    @Environment(\.sizeCategory) var sizeCategory
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
     let inTitle0 = String(localized: "TitleIncoming_Short", defaultValue: 
"Incoming",
@@ -34,11 +33,8 @@ struct PendingRowView: View {
         let outTitle = iconOnly ? outTitle0 : outTitle1
         let pendingTitle = incoming ? inTitle : outTitle
 
-        let currencyInfo = controller.info(for: amount.currencyStr, 
controller.currencyTicker)
-        let amountText = Text(amount.string(currencyInfo))
+        let amountText = AmountV(amount)
             .foregroundColor(pendingColor)
-            .accessibilityFont(.title2)
-            .monospacedDigit()
 
         // this is the default view for iOS 15
         let vStack = VStack {
@@ -72,19 +68,6 @@ struct PendingRowView: View {
 }
 // MARK: -
 #if DEBUG
-func PreviewCurrencyInfo(_ currency: String, digits: Int) -> CurrencyInfo {
-    let unitName = digits == 0 ? "テ" : "ク"  // do not use real currency 
symbols like "¥" : "€"
-    let scope = ScopeInfo(type: .global, currency: currency)
-    let specs = CurrencySpecification(name: currency,
-                                      code: currency,
-                     fractionalInputDigits: digits,
-                    fractionalNormalDigits: digits,
-              fractionalTrailingZeroDigits: digits,
-                              altUnitNames: [0 : unitName])
-    let previewFormatter = CurrencyFormatter.formatter(scope: scope, specs: 
specs)
-    return CurrencyInfo(scope: scope, specs: specs, formatter: 
previewFormatter)
-}
-
 @MainActor
 fileprivate struct Preview_Content: View {
     var body: some View {
diff --git a/TalerWallet1/Views/Exchange/ExchangeListView.swift 
b/TalerWallet1/Views/Banking/ExchangeListView.swift
similarity index 92%
rename from TalerWallet1/Views/Exchange/ExchangeListView.swift
rename to TalerWallet1/Views/Banking/ExchangeListView.swift
index 75b7156..718291f 100644
--- a/TalerWallet1/Views/Exchange/ExchangeListView.swift
+++ b/TalerWallet1/Views/Banking/ExchangeListView.swift
@@ -12,10 +12,6 @@ struct ExchangeListView: View {
     let stack: CallStack
     @Binding var balances: [Balance]
     let navTitle: String
-#if TABBAR  // Taler Wallet
-#else       // GNU Taler
-    var hamburgerAction: () -> Void
-#endif
     @EnvironmentObject private var model: WalletModel
 
     @State var showAlert: Bool = false
@@ -35,11 +31,6 @@ struct ExchangeListView: View {
     }
 
     var body: some View {
-#if TABBAR  // Taler Wallet
-        let hamburger: HamburgerButton? = nil
-#else       // GNU Taler
-        let hamburger = HamburgerButton(action: hamburgerAction)
-#endif
         let accessibilityLabelStr = String(localized: "Add Exchange", comment: 
"accessibilityLabel for the + button")
         let plusButton = PlusButton(accessibilityLabelStr: 
accessibilityLabelStr) {
             showAlert = true
@@ -49,7 +40,7 @@ struct ExchangeListView: View {
         if #available(iOS 16.0, *) {
             ExchangeListCommonV(symLog: symLog, stack: stack.push(), balances: 
$balances)
                 .navigationTitle(navTitle)
-                .navigationBarItems(leading: hamburger, trailing: plusButton)
+                .navigationBarItems(trailing: plusButton)
                 .alert(addTitleStr, isPresented: $showAlert) {
                     TextField("Exchange address", text: $newExchange)
 //                    .textFieldStyle(.roundedBorder)   Yikes: when adding 
style the alert will stop showing the textfield! Don't do this.
@@ -63,7 +54,7 @@ struct ExchangeListView: View {
         } else {    // iOS 15 cannot have a textfield in an alert, so we must
             ExchangeListCommonV(symLog: symLog, stack: stack.push(), balances: 
$balances)
                 .navigationTitle(navTitle)
-                .navigationBarItems(leading: hamburger, trailing: plusButton)
+                .navigationBarItems(trailing: plusButton)
                 .textFieldAlert(isPresented: $showAlert,
                                       title: addTitleStr,
                                    doneText: addButtonStr,
@@ -91,7 +82,7 @@ struct ExchangeListCommonV: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog?.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -129,7 +120,8 @@ extension ExchangeListCommonV {
             var currencies: [String : [Exchange]] = [:]
 
             for exchange in exchanges {
-                let currency = exchange.currency ?? "Unknown"
+                let currency = exchange.scopeInfo?.currency
+                            ?? exchange.currency ?? "Unknown"
                 if currencies[currency] != nil {
                     currencies[currency]!.append(exchange)
                 } else {
diff --git a/TalerWallet1/Views/Exchange/ExchangeRowView.swift 
b/TalerWallet1/Views/Banking/ExchangeRowView.swift
similarity index 84%
rename from TalerWallet1/Views/Exchange/ExchangeRowView.swift
rename to TalerWallet1/Views/Banking/ExchangeRowView.swift
index aa93dff..a16e6ad 100644
--- a/TalerWallet1/Views/Exchange/ExchangeRowView.swift
+++ b/TalerWallet1/Views/Banking/ExchangeRowView.swift
@@ -34,7 +34,7 @@ struct ExchangeRowView: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
         let delay: UInt = 0     // set to 5 to test delayed currency 
information
@@ -73,18 +73,23 @@ struct ExchangeRowView: View {
                                          sendAction: { selectAndUpdate(1) },
                                          recvAction: { selectAndUpdate(2) })
         Group {
-            HStack(spacing: 0) {    // can't use the built in Label because it 
adds the accessory arrow
-                Text(baseURL.trimURL())
-                    .accessibilityFont(.headline)
+            NavigationLink(destination: showToS) {
+                VStack(alignment: .leading) {
+                    Text(baseURL.trimURL())
+                        .accessibilityFont(.headline)
+                    if !iconOnly {
+                        Text("Terms of Service")  // VIEW_WITHDRAW_TOS
+                            .accessibilityFont(.body)
+                    }
+                }
+            }
+            .background( Group {
                 NavigationLink(destination: deposit, tag: 1, selection: 
$buttonSelected)
-                    { EmptyView() }.frame(width: 0).opacity(0)
+                { EmptyView() }.frame(width: 0).opacity(0).hidden()
                 NavigationLink(destination: manualWithdraw, tag: 2, selection: 
$buttonSelected)
-                    { EmptyView() }.frame(width: 0).opacity(0)
-            }.listRowSeparator(.hidden)
-            NavigationLink(destination: showToS) {
-                Text("Terms of Service")  // VIEW_WITHDRAW_TOS
-                    .accessibilityFont(.body)
-            }.listRowSeparator(.hidden)
+                { EmptyView() }.frame(width: 0).opacity(0).hidden()
+            })
+            .listRowSeparator(.hidden)
             if #available(iOS 16.0, *) {
                 ViewThatFits(in: .horizontal) {
                     HStack(spacing: HSPACING) {
@@ -97,12 +102,13 @@ struct ExchangeRowView: View {
             }
         }
         .task {
-            if !controller.hasInfo(for: currency) {
+            if controller.hasInfo(for: currency) == nil {
                 symLog.log("fetching info for \(currency)")
                 // FIXME: remove fake ScopeInfo once the REAL one is in 
exchange.scopeInfo
-                let scopeInfo = exchange.scopeInfo ?? ScopeInfo(type: .global, 
currency: currency)
+                let scopeInfo = exchange.scopeInfo
+                             ?? ScopeInfo(type: .global, currency: currency)
                 do {
-                    let info = try await model.getCurrencyInfo(scope: 
scopeInfo, delay: delay)
+                    let info = try await model.getCurrencyInfoM(scope: 
scopeInfo, delay: delay)
 //                    logger.info("got info: \(scope.currency, privacy: 
.public)")
                     await controller.setInfo(info)
                 } catch {    // TODO: error handling - couldn't get 
CurrencyInfo
@@ -119,15 +125,16 @@ fileprivate struct ExchangeRow_Container : View {
 
 //    let amount = Amount(currency: LONGCURRENCY, cent: 123456)
     var body: some View {
+        let scopeInfo = ScopeInfo(type: .exchange, currency: LONGCURRENCY)
         let exchange1 = Exchange(exchangeBaseUrl: ARS_AGE_EXCHANGE,
-                                        currency: LONGCURRENCY,
+                                       scopeInfo: scopeInfo,
                                        paytoUris: [],
                                        tosStatus: .pending,
                              exchangeEntryStatus: .preset,
                             exchangeUpdateStatus: .initial,
                            ageRestrictionOptions: [12,16])
         let exchange2 = Exchange(exchangeBaseUrl: ARS_EXP_EXCHANGE,
-                                        currency: LONGCURRENCY,
+                                       scopeInfo: scopeInfo,
                                        paytoUris: [],
                                        tosStatus: .proposed,
                              exchangeEntryStatus: .ephemeral,
diff --git a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift 
b/TalerWallet1/Views/Banking/ExchangeSectionView.swift
similarity index 69%
rename from TalerWallet1/Views/Exchange/ExchangeSectionView.swift
rename to TalerWallet1/Views/Banking/ExchangeSectionView.swift
index 0f48133..09bfa7e 100644
--- a/TalerWallet1/Views/Exchange/ExchangeSectionView.swift
+++ b/TalerWallet1/Views/Banking/ExchangeSectionView.swift
@@ -13,9 +13,10 @@ struct ExchangeSectionView: View {
     let currency: String                        // this is the currency to be 
used
     let exchanges: [Exchange]
     @Binding var amountToTransfer: Amount       // does still have the wrong 
currency
+    @AppStorage("iconOnly") var iconOnly: Bool = false
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
 //        let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -25,6 +26,21 @@ struct ExchangeSectionView: View {
                              exchange: exchange,
                              currency: currency,            // TODO: 
(balance.available) amount.isZero to disable Deposit-button
                      amountToTransfer: $amountToTransfer)   // does still have 
the wrong currency
+                .listRowSeparator(.hidden)
+            }
+            if "KUDOS" == currency {
+                let bankingHint = String(localized: "Since the demo bank 
supports the Taler integration, you can start a withdrawal directly on the")
+                let linkTitle = String(localized: "LinkTitle_DEMOBANK", 
defaultValue: "Demo Bank Website")
+                VStack {
+                    if !iconOnly {
+                        Text(bankingHint)
+                    }
+                    Link(linkTitle, destination: URL(string: DEMOBANK)!)
+                        .buttonStyle(TalerButtonStyle(type: .bordered, narrow: 
false, aligned: .center))
+                }
+                .accessibilityElement(children: .combine)
+                .accessibilityLabel(bankingHint + " " + linkTitle)
+                .padding(.top)
             }
         } header: {
             BarGraphHeader(stack: stack.push(), currency: currency)
@@ -39,15 +55,16 @@ fileprivate struct ExchangeRow_Container : View {
 
 //    let amount = Amount(currency: LONGCURRENCY, cent: 123456)
     var body: some View {
+        let scopeInfo = ScopeInfo(type: .exchange, currency: LONGCURRENCY)
         let exchange1 = Exchange(exchangeBaseUrl: ARS_AGE_EXCHANGE,
-                                        currency: LONGCURRENCY,
+                                       scopeInfo: scopeInfo,
                                        paytoUris: [],
                                        tosStatus: .pending,
                              exchangeEntryStatus: .preset,
                             exchangeUpdateStatus: .initial,
                            ageRestrictionOptions: [12,16])
         let exchange2 = Exchange(exchangeBaseUrl: ARS_EXP_EXCHANGE,
-                                        currency: LONGCURRENCY,
+                                       scopeInfo: scopeInfo,
                                        paytoUris: [],
                                        tosStatus: .proposed,
                              exchangeEntryStatus: .ephemeral,
diff --git a/TalerWallet1/Views/Exchange/ManualWithdraw.swift 
b/TalerWallet1/Views/Banking/ManualWithdraw.swift
similarity index 70%
rename from TalerWallet1/Views/Exchange/ManualWithdraw.swift
rename to TalerWallet1/Views/Banking/ManualWithdraw.swift
index 9cf3534..f63751e 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Banking/ManualWithdraw.swift
@@ -6,7 +6,8 @@ import SwiftUI
 import taler_swift
 import SymLog
 
-// Will be called by the user tapping "Withdraw Coins" in the exchange list
+// Will be called by either the user tapping "Withdraw Coins" in the exchange 
list
+// or from WithdrawExchangeV after a withdraw-exchange QR was scanned
 struct ManualWithdraw: View {
     private let symLog = SymLogV(0)
     let stack: CallStack
@@ -23,7 +24,7 @@ struct ManualWithdraw: View {
 //    @State var selectedAge = 0
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -47,23 +48,24 @@ struct ManualWithdraw: View {
 //                           restrictAge: restrictAge)
             }
             let disabled = amountToTransfer.isZero || someCoins.invalid || 
someCoins.tooMany
+            let tosAccepted = exchange.tosStatus == .accepted
             ScrollView { VStack(alignment: .trailing) {
                 Text("via \(exchange.exchangeBaseUrl.trimURL())")
                     .multilineTextAlignment(.center)
                     .accessibilityFont(.body)
-                CurrencyInputView(amount: $amountToTransfer,
-                               available: nil,
-                                   title: iconOnly ? String(localized: 
"Amount:")
-                                                   : String(localized: "Amount 
to withdraw:"),
-                          shortcutAction: nil)
-                    .padding(.top)
-                QuiteSomeCoins(someCoins: someCoins,
-                           shouldShowFee: true,           // TODO: set to 
false if we never charge withdrawal fees
-                                currency: currency,
-                            currencyInfo: currencyInfo,
-                         amountEffective: 
withdrawalAmountDetails?.amountEffective)
-//              agePicker
-                if exchange.tosStatus == .accepted {
+                if tosAccepted {
+                    CurrencyInputView(amount: $amountToTransfer,
+                                   available: nil,
+                                       title: iconOnly ? String(localized: 
"Amount:")
+                                                       : String(localized: 
"Amount to withdraw:"),
+                              shortcutAction: nil)
+                        .padding(.top)
+                    QuiteSomeCoins(someCoins: someCoins,
+                               shouldShowFee: true,           // TODO: set to 
false if we never charge withdrawal fees
+                                    currency: currency,
+                                currencyInfo: currencyInfo,
+                             amountEffective: 
withdrawalAmountDetails?.amountEffective)
+//                  agePicker
                     NavigationLink(destination: destination) {
                         Text("Confirm Withdrawal")      // VIEW_WITHDRAW_ACCEPT
                     }
@@ -90,24 +92,27 @@ struct ManualWithdraw: View {
                 symLog.log("❗️ \(navTitle) onDisappear")
             }
         } else {
-            WithdrawProgressView(message: exchangeBaseUrl.trimURL())
-                .navigationTitle("Contacting Exchange")
+            LoadingView(url: nil, message: exchangeBaseUrl.trimURL())
         }
       } .task(id: amountToTransfer.value) { // re-run this whenever 
amountToTransfer changes
-          symLog.log("getExchangeByUrl(\(exchangeBaseUrl))")
-            do {
+            symLog.log("getExchangeByUrl(\(exchangeBaseUrl))")
+          if exchange == nil || exchange?.tosStatus != .accepted {
                 if let exc = await model.getExchangeByUrl(url: 
exchangeBaseUrl) {
                     exchange = exc
-                    if !amountToTransfer.isZero {
-                        let details = try await 
model.loadWithdrawalDetailsForAmountM(exchangeBaseUrl,
-                                                                               
       amount: amountToTransfer)
-                        withdrawalAmountDetails = details
+                } else {
+                    // TODO: Error "Can't get Exchange Info"
+                }
+            }
+            if !amountToTransfer.isZero {
+                do {
+                    let details = try await 
model.loadWithdrawalDetailsForAmountM(exchangeBaseUrl,
+                                                                           
amount: amountToTransfer)
+                    withdrawalAmountDetails = details
 //                    agePicker.setAges(ages: 
withdrawalAmountDetails?.ageRestrictionOptions)
-                    }
+                } catch {    // TODO: error
+                    symLog.log(error.localizedDescription)
+                    withdrawalAmountDetails = nil
                 }
-            } catch {    // TODO: error
-                symLog.log(error.localizedDescription)
-                withdrawalAmountDetails = nil
             }
         }
     }
diff --git a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift 
b/TalerWallet1/Views/Banking/ManualWithdrawDone.swift
similarity index 77%
rename from TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
rename to TalerWallet1/Views/Banking/ManualWithdrawDone.swift
index 039f6e4..d4fa7ec 100644
--- a/TalerWallet1/Views/Exchange/ManualWithdrawDone.swift
+++ b/TalerWallet1/Views/Banking/ManualWithdrawDone.swift
@@ -23,9 +23,12 @@ struct ManualWithdrawDone: View {
     func reloadOneAction(_ transactionId: String) async throws -> Transaction {
         return try await model.getTransactionByIdT(transactionId)
     }
+    func dismissTopAnimated(_ stack: CallStack) {
+        dismissTop()
+    }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -35,7 +38,7 @@ struct ManualWithdrawDone: View {
                               transactionId: transactionId,
                                reloadAction: reloadOneAction,
                                    navTitle: navTitle,
-                                 doneAction: ViewState.shared.popToRootView,
+                                 doneAction: dismissTopAnimated,    // 
ViewState.shared.popToRootView,
                                 abortAction: nil,
                                deleteAction: nil,
                                  failAction: nil,
@@ -45,19 +48,20 @@ struct ManualWithdrawDone: View {
                 .interactiveDismissDisabled()           // can only use "Done" 
button to dismiss
 //                .navigationTitle(navTitle)
             } else {
-                WithdrawProgressView(message: 
exchange.exchangeBaseUrl.trimURL())
-                    .navigationTitle("Loading " + navTitle)
+                LoadingView(url: nil, message: 
exchange.exchangeBaseUrl.trimURL())
             }
         }.onAppear() {
             symLog.log("onAppear")
             DebugViewC.shared.setViewID(VIEW_WITHDRAW_ACCEPT, stack: 
stack.push())
         }.task {
-            do {
-                let result = try await 
model.sendAcceptManualWithdrawalM(exchange.exchangeBaseUrl,
-                                                                         
amount: amountToTransfer, restrictAge: 0)
-                transactionId = result!.transactionId
-            } catch {    // TODO: error
-                symLog.log(error.localizedDescription)
+            if transactionId == nil {
+                do {
+                    let result = try await 
model.sendAcceptManualWithdrawalM(exchange.exchangeBaseUrl,
+                                                                             
amount: amountToTransfer, restrictAge: 0)
+                    transactionId = result!.transactionId
+                } catch {    // TODO: error
+                    symLog.log(error.localizedDescription)
+                }
             }
         }
     }
@@ -69,8 +73,9 @@ struct ManualWithdrawDone_Container : View {
     @State private var amountToTransfer = Amount(currency: LONGCURRENCY, cent: 
510)
 
     var body: some View {
+        let scopeInfo = ScopeInfo(type: .exchange, currency: LONGCURRENCY)
         let exchange = Exchange(exchangeBaseUrl: DEMOEXCHANGE,
-                                       currency: LONGCURRENCY,
+                                      scopeInfo: scopeInfo,
                                       paytoUris: [],
                                       tosStatus: .pending,
                             exchangeEntryStatus: .preset,
diff --git a/TalerWallet1/Views/Exchange/QuiteSomeCoins.swift 
b/TalerWallet1/Views/Banking/QuiteSomeCoins.swift
similarity index 100%
rename from TalerWallet1/Views/Exchange/QuiteSomeCoins.swift
rename to TalerWallet1/Views/Banking/QuiteSomeCoins.swift
diff --git a/TalerWallet1/Views/HelperViews/AmountRowV.swift 
b/TalerWallet1/Views/HelperViews/AmountRowV.swift
index 19f99dc..ed5f6df 100644
--- a/TalerWallet1/Views/HelperViews/AmountRowV.swift
+++ b/TalerWallet1/Views/HelperViews/AmountRowV.swift
@@ -5,92 +5,76 @@
 import SwiftUI
 import taler_swift
 
-// calculate the width of the amountStr (with Font)
-// calculate the width of 'content' in compact form
-// if it fits side by side, then render HStack(content(compact), Spacer(), 
amountStr)
-// else render VStack(content(wide), HStack(Spacer(), amountStr))
-
-struct AmountRowV<Content: View>: View {
-    let amountStr: String
-    let amountColor: Color
-    let doneOrPending: Bool
-    let largeAmountFont: Bool
-    let fitsHorizontal: Bool
-    let vertAlignment: VerticalAlignment
-
-    var content: () -> Content
+// Title and Amount
+struct AmountRowV: View {
+    let title: String
+    let amount: Amount
+    let color: Color
+    let large: Bool      // set to false for QR or IBAN
 
     var body: some View {
-        let text = Text(amountStr)
-//            .strikethrough(!doneOrPending)
-            .foregroundColor(amountColor)
-            .accessibilityFont(largeAmountFont ? .title : .title2)
-            .monospacedDigit()
-        if fitsHorizontal {
-            HStack(alignment: vertAlignment, spacing: 0) {
-                content()
-                Spacer(minLength: 0)
-                text
+        let titleV = Text(title)
+                        .multilineTextAlignment(.leading)
+                        .accessibilityFont(.body)
+        let amountV = AmountV(amount: amount, large: large)
+                        .foregroundColor(color)
+        let verticalV = VStack(alignment: .leading) {
+            titleV
+            HStack(alignment: .lastTextBaseline) {
+                Spacer(minLength: 2)
+                amountV
             }
-        } else {
-            VStack(alignment: .leading, spacing: 0) {
-                content()
-                HStack {
-                    Spacer(minLength: 0)
-                    text
+        }
+        Group {
+            if #available(iOS 16.0, *) {
+                ViewThatFits(in: .horizontal) {
+                    HStack(alignment: .lastTextBaseline) {
+                        titleV//.border(.orange)
+                        Spacer(minLength: 2)
+                        amountV//.border(.gray)
+                    }
+                    HStack(alignment: .lastTextBaseline) {
+                        titleV//.border(.blue)
+                            .lineLimit(2, reservesSpace: true)
+                            .fixedSize(horizontal: false, vertical: true)
+                        Spacer(minLength: 2)
+                        amountV//.border(.gray)
+                    }
+                    verticalV
                 }
+            } else { // view for iOS 15
+                verticalV
             }
         }
+            .frame(maxWidth: .infinity, alignment: .leading)
+            .listRowSeparator(.hidden)
     }
 }
-// MARK: -
-#if  DEBUG
-
-struct PreviewSectionWithAmountRow: View {
-    @Environment(\.sizeCategory) var sizeCategory
-    @Environment(\.colorSchemeContrast) private var colorSchemeContrast
-
-    var body: some View {
-        let testInfo = PreviewCurrencyInfo(TESTCURRENCY, digits: 0)
-        let demoInfo = PreviewCurrencyInfo(DEMOCURRENCY, digits: 2)
-        let test = Amount(currency: TESTCURRENCY, cent: 123)
-        let demo = Amount(currency: DEMOCURRENCY, cent: 123456)
-        let testStr = test.string(testInfo)
-        let demoStr = demo.string(demoInfo)
-        List {
-            Section {
-                AmountRowV(amountStr: demoStr, amountColor: .primary, 
doneOrPending: true, largeAmountFont: true,
-                           fitsHorizontal: true, vertAlignment: 
.lastTextBaseline) {
-                    Text(verbatim: "Balance")
-                        .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
-                        .accessibilityFont(.title2)
-                }
-                AmountRowV(amountStr: demoStr, amountColor: .primary, 
doneOrPending: true, largeAmountFont: true,
-                           fitsHorizontal: false, vertAlignment: 
.lastTextBaseline) {
-                    Text(verbatim: "Balance")
-                        .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
-                        .accessibilityFont(.title2)
-                }
-            }
-            Section {
-                AmountRowV(amountStr: testStr, amountColor: .primary, 
doneOrPending: false, largeAmountFont: false,
-                           fitsHorizontal: true, vertAlignment: 
.lastTextBaseline) {
-                    Text(verbatim: "Balance")
-                        .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
-                        .accessibilityFont(.title2)
-                }
-                AmountRowV(amountStr: testStr, amountColor: .secondary, 
doneOrPending: false, largeAmountFont: false,
-                           fitsHorizontal: false, vertAlignment: 
.lastTextBaseline) {
-                    Text(verbatim: "Balance")
-                        .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
-                        .accessibilityFont(.title2)
-                }
-            }
-        }
+extension AmountRowV {
+    init(title: String, amount: Amount, color: Color) {
+        self.title = title
+        self.amount = amount
+        self.color = color
+        self.large = true
     }
 }
 
+// MARK: -
+fileprivate func talerFromStr(_ from: String) -> Amount {
+    do {
+        let amount = try Amount(fromString: from)
+        return amount
+    } catch {
+        return Amount(currency: "Taler", cent: 480)
+    }
+}
 #Preview {
-    PreviewSectionWithAmountRow()
+    List {
+        let fee = Amount(currency: "Taler", cent: 20)
+        AmountRowV(title: "Fee", amount: fee, color: Color("Outgoing"), large: 
false)
+        let cents = Amount(currency: "Taler", cent: 480)
+        AmountRowV(title: "Cents", amount: cents, color: Color("Incoming"))
+        let amount = talerFromStr("Taler:4.80")
+        AmountRowV(title: "Chosen amount to withdraw", amount: amount, color: 
Color("Incoming"))
+    }
 }
-#endif
diff --git a/TalerWallet1/Views/HelperViews/AmountV.swift 
b/TalerWallet1/Views/HelperViews/AmountV.swift
new file mode 100644
index 0000000..5a7760f
--- /dev/null
+++ b/TalerWallet1/Views/HelperViews/AmountV.swift
@@ -0,0 +1,38 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+import taler_swift
+
+struct AmountV: View {
+    let amount: Amount
+    let large: Bool      // set to false for QR or IBAN
+
+    @EnvironmentObject private var controller: Controller
+
+    var amountStr: String {
+        if let currencyInfo = controller.info(for: amount.currencyStr) {
+            return amount.string(currencyInfo)
+        }
+        return amount.readableDescription
+    }
+
+    var body: some View {
+        Text(amountStr)
+            .multilineTextAlignment(.center)
+            .accessibilityFont(large ? .title : .title2)
+//            .fontWeight(large ? .medium : .regular)       // @available(iOS 
16.0, *)
+            .monospacedDigit()
+    }
+}
+extension AmountV {
+    init(_ amount: Amount) {
+        self.amount = amount
+        self.large = false
+    }
+}
+// MARK: -
+//#Preview {
+//    AmountV()
+//}
diff --git a/TalerWallet1/Views/HelperViews/AmountView.swift 
b/TalerWallet1/Views/HelperViews/AmountView.swift
deleted file mode 100644
index 2015115..0000000
--- a/TalerWallet1/Views/HelperViews/AmountView.swift
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-
-struct AmountView: View {
-    let title: String
-    let value: String
-    let color: Color
-    let large: Bool      // set to false for QR or IBAN
-
-    var body: some View {
-        VStack(alignment: .leading) {
-            Text(title)
-                .fixedSize(horizontal: false, vertical: true)       // wrap in 
scrollview
-                .multilineTextAlignment(.leading)
-                .accessibilityFont(.body)
-            HStack {
-                Spacer()
-                Text(value)
-                    .fixedSize(horizontal: false, vertical: true)   // wrap in 
scrollview
-                    .multilineTextAlignment(.center)
-                    .foregroundColor(color)
-                    .accessibilityFont(large ? .title : .title2)
-//                    .fontWeight(large ? .medium : .regular)       // 
@available(iOS 16.0, *)
-                    .monospacedDigit()
-            }
-        }
-            .frame(maxWidth: .infinity, alignment: .leading)
-            .listRowSeparator(.hidden)
-    }
-}
-
-struct AmountView_Previews: PreviewProvider {
-    static var previews: some View {
-        List {
-            AmountView(title: "Fee", value: "- 0,2 Taler",
-                       color: Color("Outgoing"), large: true)
-            AmountView(title: "Coins", value: "4,8 Taler",
-                       color: Color("Incoming"), large: false)
-        }
-    }
-}
diff --git a/TalerWallet1/Views/HelperViews/BarGraph.swift 
b/TalerWallet1/Views/HelperViews/BarGraph.swift
index 92c6e69..e7b60ef 100644
--- a/TalerWallet1/Views/HelperViews/BarGraph.swift
+++ b/TalerWallet1/Views/HelperViews/BarGraph.swift
@@ -16,6 +16,7 @@ struct BarGraphHeader: View {
     @Environment(\.colorSchemeContrast) private var colorSchemeContrast
 
     @State private var completedTransactions: [Transaction] = []
+    @ScaledMetric var barHeight = 9       // relative to fontSize
 
     var body: some View {
         HStack (alignment: .center, spacing: 10) {
@@ -23,7 +24,7 @@ struct BarGraphHeader: View {
                 .accessibilityFont(.title2)
 //                .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
             BarGraph(transactions: $completedTransactions,
-                     maxBars: MAXBARS, barHeight: 10)       // TODO: barHeight 
relative to fontSize
+                     maxBars: MAXBARS, barHeight: barHeight)
         }
         .task {
             symLog.log(".task for BarGraphHeader(\(currency)) - reload 
Transactions")
@@ -38,7 +39,7 @@ struct BarGraphHeader: View {
 struct BarGraph: View {
     @Binding var transactions: [Transaction]
     let maxBars: Int
-    let barHeight : Double
+    let barHeight: Double
 
     func maxValue(_ someTransactions: [Transaction]) -> Double {
         var maxValue = 0.0
@@ -67,15 +68,16 @@ struct BarGraph: View {
                     let valueTransparent = barHeight - valueColored
 //                    let _ = print("max: \(maxValue), ", incoming ? "+" : 
"-", netto)
                     VStack(spacing: 0) {
+                        let width = barHeight / 3
                         Rectangle()
                             .opacity(0.001)
-                            .frame (width: 3, height: incoming ? 
valueTransparent : barHeight )
+                            .frame (width: width, height: incoming ? 
valueTransparent : barHeight )
                         Rectangle()
                             .foregroundColor(incoming ? .green : .red)
-                            .frame (width: 3, height: valueColored )
+                            .frame (width: width, height: valueColored )
                         Rectangle()
                             .opacity(0.001)
-                            .frame (width: 3, height: incoming ? barHeight : 
valueTransparent)
+                            .frame (width: width, height: incoming ? barHeight 
: valueTransparent)
                     }
                 }
             }
diff --git a/TalerWallet1/Views/HelperViews/Buttons.swift 
b/TalerWallet1/Views/HelperViews/Buttons.swift
index 9ab4a4b..7d9a788 100644
--- a/TalerWallet1/Views/HelperViews/Buttons.swift
+++ b/TalerWallet1/Views/HelperViews/Buttons.swift
@@ -110,8 +110,8 @@ struct TalerButtonStyle: ButtonStyle {
     public func makeBody(configuration: ButtonStyle.Configuration) -> some 
View {
 //        configuration.role = type == .prominent ? .primary : .normal         
 Only on macOS
             MyBigButton(//type: type,
-                   foreColor: foreColor(type: type, pressed: 
configuration.isPressed),
-                   backColor: backColor(type: type, pressed: 
configuration.isPressed),
+                   foreColor: foreColor(type: type, pressed: 
configuration.isPressed, disabled: disabled),
+                   backColor: backColor(type: type, pressed: 
configuration.isPressed, disabled: disabled),
                       dimmed: dimmed,
                configuration: configuration,
                     disabled: disabled,
@@ -120,14 +120,14 @@ struct TalerButtonStyle: ButtonStyle {
                        badge: badge)
     }
 
-    func foreColor(type: TalerButtonStyleType, pressed: Bool) -> Color {
+    func foreColor(type: TalerButtonStyleType, pressed: Bool, disabled: Bool) 
-> Color {
         return type == .plain ? WalletColors().fieldForeground :      // 
primary text color
             WalletColors().buttonForeColor(pressed: pressed,
                                           disabled: disabled,
                                          prominent: type == .prominent,
                                            balance: type == .balance)
     }
-    func backColor(type: TalerButtonStyleType, pressed: Bool) -> Color {
+    func backColor(type: TalerButtonStyleType, pressed: Bool, disabled: Bool) 
-> Color {
         return type == .plain && !pressed ? Color.clear :
             WalletColors().buttonBackColor(pressed: pressed,
                                           disabled: disabled,
diff --git a/TalerWallet1/Views/HelperViews/CopyShare.swift 
b/TalerWallet1/Views/HelperViews/CopyShare.swift
index 500759b..7d0ebfb 100644
--- a/TalerWallet1/Views/HelperViews/CopyShare.swift
+++ b/TalerWallet1/Views/HelperViews/CopyShare.swift
@@ -8,12 +8,15 @@ import SymLog
 
 struct CopyButton: View {
     private let symLog = SymLogV(0)
-    @Environment(\.isEnabled) private var isEnabled: Bool
     let textToCopy: String
     let vertical: Bool
 
+    @Environment(\.isEnabled) private var isEnabled: Bool
+    @EnvironmentObject private var controller: Controller
+
     func copyAction() -> Void {
         symLog.log(textToCopy)
+        controller.hapticFeedback(.medium)
         UIPasteboard.general.setValue(textToCopy,
                                       forPasteboardType: 
UTType.plainText.identifier)
     }
@@ -44,12 +47,14 @@ struct CopyButton: View {
 @MainActor
 struct ShareButton: View {
     private let symLog = SymLogV(0)
-    @Environment(\.isEnabled) private var isEnabled: Bool
-
     let textToShare: String
 
+    @Environment(\.isEnabled) private var isEnabled: Bool
+    @EnvironmentObject private var controller: Controller
+
     func shareAction() -> Void {
         symLog.log(textToShare)
+        controller.hapticFeedback(.soft)
         ShareSheet.shareSheet(url: textToShare)
     }
 
diff --git a/TalerWallet1/Views/HelperViews/CurrencyField.swift 
b/TalerWallet1/Views/HelperViews/CurrencyField.swift
index 7b74114..37d7342 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyField.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyField.swift
@@ -52,7 +52,7 @@ struct CurrencyField: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog(amount.description)       // just to get the # to 
compare it with .onAppear & onDisappear
 #endif
diff --git a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift 
b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
index 3c9cbae..4822954 100644
--- a/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
+++ b/TalerWallet1/Views/HelperViews/CurrencyInputView.swift
@@ -149,7 +149,7 @@ struct CurrencyInputView: View {
 //                print("❗️Yikes: CurrencyInputView hasBeenShown")
             } else {
                 print("❗️CurrencyInputView❗️")
-                DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
+                DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
                     hasBeenShown = true
                     if !currencyField.becomeFirstResponder() {
                         print("❗️Yikes❗️")
diff --git a/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift 
b/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
index 8e923ca..4bb91e6 100644
--- a/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
+++ b/TalerWallet1/Views/HelperViews/LaunchAnimationView.swift
@@ -18,7 +18,7 @@ struct RotatingTaler: View {
     let size: CGFloat
     @Binding var rotationEnabled: Bool
     @State private var rotationDirection = false
-#if TABBAR  // Taler Wallet
+#if TALER_WALLET
     let logo = "taler-logo-2023-blue"
 #else       // GNU Taler
     let logo = "taler-logo-2023-red"
diff --git a/TalerWallet1/Views/HelperViews/LoadingView.swift 
b/TalerWallet1/Views/HelperViews/LoadingView.swift
index 10450fc..b156cc0 100644
--- a/TalerWallet1/Views/HelperViews/LoadingView.swift
+++ b/TalerWallet1/Views/HelperViews/LoadingView.swift
@@ -7,22 +7,49 @@ import SymLog
 
 struct LoadingView: View {
     private let symLog = SymLogV(0)
-    let navTitle = String(localized: "Loading...")
-    let backButtonHidden: Bool
+    let url:URL?
+    let message: String?
+
+//    let backButtonHidden: Bool
+    let navTitle = String(localized: "Loading…")
+
+    @State private var rotationEnabled = true
 
     var body: some View {
-        LaunchAnimationView()
-            .navigationBarBackButtonHidden(backButtonHidden)
-            .navigationTitle(navTitle)
-            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: 
.center)
-//            
.background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
+        VStack(alignment: .center) {
+            Spacer()
+            RotatingTaler(size: 100, rotationEnabled: $rotationEnabled)
+                .onTapGesture(count: 2) {
+                    rotationEnabled.toggle()
+                }
+            Spacer()
+            if let url {
+                if let urlStr = url.host {
+                    Text(urlStr)
+                } else {
+                    Text("Error in URL: \(url)")
+                }
+                Spacer()
+            }
+            if let message {
+                Text(message)
+            } else {
+                Text(EMPTYSTRING)
+            }
+            Spacer()
+            Spacer()
+        }
+        .frame(maxWidth: .infinity)
+        .accessibilityFont(.title)
+        .navigationTitle("Loading…")
+        .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
     }
 }
 // MARK: -
 struct LoadingView_Previews: PreviewProvider {
     static var previews: some View {
         NavigationView {
-            LoadingView(backButtonHidden: true)
+            LoadingView(url: nil, message: "test message")  // , 
backButtonHidden: true)
                 .navigationBarTitleDisplayMode(.automatic)
         }.navigationViewStyle(.stack)
     }
diff --git a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift 
b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
index 2af6d8a..0a2dcfc 100644
--- a/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
+++ b/TalerWallet1/Views/HelperViews/QRCodeDetailView.swift
@@ -10,12 +10,12 @@ import AVFoundation
 struct QRCodeDetailView: View {
     let talerURI: String
     let incoming: Bool
-    let amount: Amount?
+    let amount: Amount
 
     var body: some View {
         if talerURI.count > 10 {
             Section {
-                Text("Either", comment: "Either (copy/share)")
+                Text("Either", comment: "Either (copy/share the payment link 
to the ...)")
                     .multilineTextAlignment(.leading)
                     .accessibilityFont(.title3)
 //                    .padding(.vertical)
@@ -28,7 +28,8 @@ struct QRCodeDetailView: View {
 
                 let otherParty = incoming ? String(localized: "payer", 
comment: "the payment link to the (otherParty), or")
                                           : String(localized: "payee", 
comment: "the payment link to the (otherParty), or")
-                Text("the payment link to the \(otherParty), or", comment:  
"...the payment link to the (payer/payee), or")
+                Text("the payment link to the \(otherParty), or", 
+                     comment: "(Either copy/share the payment link) to the 
(payer/payee), or")
                     .multilineTextAlignment(.leading)
                     .accessibilityFont(.title3)
                     .listRowSeparator(.hidden)
@@ -41,14 +42,11 @@ struct QRCodeDetailView: View {
                 }
                 .listRowSeparator(.hidden)
 
-                // TODO: use currency formatter instead of .readableDescription
-                let hintStr = (amount == nil) ?
-                    (incoming ? String(localized: "let the payer scan this QR 
code to pay.")
-                              : String(localized: "let the payee scan this QR 
code to receive."))
-                :   (incoming ? String(localized: "let the payer scan this QR 
code to pay \(amount!.readableDescription).",
-                                       comment: "e.g. '5,3 €'")
-                              : String(localized: "let the payee scan this QR 
code to receive \(amount!.readableDescription).",
-                                       comment: "e.g. '$ 7.41'"))
+                let amountStr = amount.readableDescription       // TODO: 
currency formatter?
+                let hintStr = incoming ? String(localized: "let the payer scan 
this QR code to pay \(amountStr).",
+                                                  comment: "e.g. '5,3 €'")
+                                       : String(localized: "let the payee scan 
this QR code to receive \(amountStr).",
+                                                  comment: "e.g. '$ 7.41'")
                 Text(hintStr)
                     .fixedSize(horizontal: false, vertical: true)       // 
wrap in scrollview
                     .accessibilityFont(.title3)
@@ -62,8 +60,9 @@ fileprivate struct ContentView: View {
     @State var talerURI: String = 
"taler://pay-push/exchange.demo.taler.net/95ZG4D1AGFGZQ7CNQ1V49D3FT18HXKA6HQT4X3XME9YSJQVFQ520"
 
     var body: some View {
+        let amount = Amount(currency: LONGCURRENCY, cent: 123)
         List {
-            QRCodeDetailView(talerURI: talerURI, incoming: false, amount: nil)
+            QRCodeDetailView(talerURI: talerURI, incoming: false, amount: 
amount)
         }
     }
 }
diff --git a/TalerWallet1/Views/HelperViews/TransactionButton.swift 
b/TalerWallet1/Views/HelperViews/TransactionButton.swift
index 40b0c5e..2f1674a 100644
--- a/TalerWallet1/Views/HelperViews/TransactionButton.swift
+++ b/TalerWallet1/Views/HelperViews/TransactionButton.swift
@@ -7,12 +7,30 @@ import taler_swift
 import AVFoundation
 
 struct TransactionButton: View {
-    let transactionId : String
-    let command : TxAction
+    let transactionId: String
+    let command: TxAction
+    let warning: String?
     let action: (_ transactionId: String) async throws -> Void
 
-    @State var disabled: Bool = false
-    @State var executed: Bool = false
+    @AppStorage("shouldShowWarning") var shouldShowWarning: Bool = true
+
+    @State private var disabled: Bool = false
+    @State private var executed: Bool = false
+    @State private var showAlert: Bool = false
+
+    private func doAction() {
+        disabled = true     // don't try this more than once
+        Task { // runs on MainActor
+            do {
+                try await action(transactionId)
+//                symLog.log("\(executed) \(transactionId)")
+                executed = true
+            } catch {    // TODO: error
+//                symLog.log(error.localizedDescription)
+            }
+        }
+    }
+
     var body: some View {
         let isDestructive = (command == .delete) || (command == .fail)
         let isCancel = (command == .abort)
@@ -21,15 +39,10 @@ struct TransactionButton: View {
                                               : nil
         Button(role: role, action: {
             if !disabled {
-                disabled = true     // don't try this more than once
-                Task { // runs on MainActor
-                    do {
-                        try await action(transactionId)
-//                    symLog.log("\(executed) \(transactionId)")
-                        executed = true
-                    } catch {    // TODO: error
-//                    symLog.log(error.localizedDescription)
-                    }
+                if shouldShowWarning && (isDestructive || isCancel) {
+                    showAlert = true
+                } else {
+                    doAction()
                 }
             }
         }, label: {
@@ -46,6 +59,16 @@ struct TransactionButton: View {
         .buttonStyle(.bordered)
         .controlSize(.large)
         .disabled(disabled)
+        .alert(warning ?? EMPTYSTRING, isPresented: $showAlert, actions: {
+                Button("Cancel", role: .cancel) {
+                    showAlert = false
+                }
+                Button(command.localizedActionTitle) {
+                    showAlert = false
+                    doAction()
+                }
+            }, message: { Text("This operation cannot be undone") }
+        )
     }
 }
 // MARK: -
@@ -58,7 +81,9 @@ struct TransactionButton_Previews: PreviewProvider {
 
     static var previews: some View {
         List {
-            TransactionButton(transactionId: "Button pressed", command: 
.abort, action: action)
+            TransactionButton(transactionId: "Button pressed", command: .abort,
+                              warning: "Are you sure you want to abort this 
transaction?",
+                              action: action)
         }
     }
 }
diff --git a/TalerWallet1/Views/Main/MainView.swift 
b/TalerWallet1/Views/Main/MainView.swift
index 43c6906..ce54497 100644
--- a/TalerWallet1/Views/Main/MainView.swift
+++ b/TalerWallet1/Views/Main/MainView.swift
@@ -20,10 +20,10 @@ struct MainView: View {
     let stack: CallStack
     @Binding var soundPlayed: Bool
 
-    @EnvironmentObject private var viewState: ViewState         // 
popToRootView()
     @EnvironmentObject private var controller: Controller
     @AppStorage("talerFont") var talerFont: Int = 0         // extension 
mustn't define this, so it must be here
-    @AppStorage("playSounds") var playSounds: Int = 1       // extension 
mustn't define this, so it must be here
+    @AppStorage("playSoundsI") var playSoundsI: Int = 1     // extension 
mustn't define this, so it must be here
+    @AppStorage("playSoundsB") var playSoundsB: Bool = true
 
     @State private var sheetPresented = false
     @State private var urlToOpen: URL? = nil
@@ -32,19 +32,19 @@ struct MainView: View {
         logger.info("sheet dismiss")
     }
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
         Group {
             if controller.backendState == .ready {
                 Content(logger: logger, stack: stack.push("Content"), 
talerFont: $talerFont)
-                // any change to rootViewId triggers popToRootView behaviour
-                    .id(viewState.rootViewId)
                     .onAppear() {
-                        if playSounds != 0  && !soundPlayed {
+#if DEBUG
+                        if playSoundsI != 0  && playSoundsB && !soundPlayed {
                             controller.playSound(1008)     // Startup chime
                         }
+#endif
                         soundPlayed = true
                     }
             } else if controller.backendState == .error {
@@ -88,10 +88,10 @@ extension MainView {
         @AppStorage("iconOnly") var iconOnly: Bool = false
         @EnvironmentObject private var controller: Controller
         @EnvironmentObject private var model: WalletModel
+        @EnvironmentObject private var viewState: ViewState     // 
popToRootView()
         let balancesTitle = String(localized: "TitleBalances", defaultValue: 
"Balances")
-        let exchangesTitle = String(localized: "TitleExchanges", defaultValue: 
"Exchanges")
+        let exchangesTitle = String(localized: "TitleExchanges", defaultValue: 
"Banking")
         let settingsTitle = String(localized: "TitleSettings", defaultValue: 
"Settings")
-#if TABBAR  // Taler Wallet
         @State private var selectedTab: Tab = .balances
         @State private var showKycAlert: Bool = false
         @State private var kycURI: URL?
@@ -128,57 +128,23 @@ extension MainView {
                 self.selectedTab = tappedTab
             }
         }
-#else       // GNU Taler
-        @State var sidebarVisible: Bool = false
-        func hamburgerAction() {
-            withAnimation(.easeInOut(duration: 0.25)) {
-                sidebarVisible = !sidebarVisible
-            }
-        }
-
-        var views: [SidebarItem] {[
-            SidebarItem(name: balancesTitle,
-                    sysImage: "chart.bar.xaxis",  // creditcard.fill     // 
TODO: Wallet Icon
-                        view: AnyView(BalancesListView(stack: 
stack.push(balancesTitle),
-                                                    navTitle: balancesTitle,
-                                                    balances: $balances,
-                                        shouldReloadBalances: 
$shouldReloadBalances,
-                                             hamburgerAction: hamburgerAction)
-                                     )),
-            SidebarItem(name: exchangesTitle,
-                    sysImage: "arrow.triangle.2.circlepath",
-                        view: AnyView(ExchangeListView(stack: 
stack.push(exchangesTitle),
-//                                                    balances: $balances,
-                                                    navTitle: exchangesTitle,
-                                             hamburgerAction: hamburgerAction)
-                                     )),
-            SidebarItem(name: settingsTitle,    // TODO: "About"?
-                    sysImage: "gearshape.fill",
-                        view: AnyView(SettingsView(stack: 
stack.push(settingsTitle),
-                                                navTitle: settingsTitle,
-                                         hamburgerAction: hamburgerAction)
-                                     ))
-        ]}
-        @State var currentView: Int = 0
-#endif
 
         var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
             // "@self" marks that the view value itself has changed, and 
"@identity" marks that the
             // identity of the view has changed (that is, that the persistent 
data associated with
             // the view has been recycled for a new instance of the same type)
-//            if #available(iOS 17.1, *) {
-//                // logs at INFO level, “com.apple.SwiftUI” subsystem, 
category “Changed Body Properties”
-//                let _ = Self._logChanges()
-//            } else {
+            if #available(iOS 17.1, *) {
+                // logs at INFO level, “com.apple.SwiftUI” subsystem, category 
“Changed Body Properties”
+                let _ = Self._logChanges()
+            } else {
                 let _ = Self._printChanges()
-//            }
+            }
             let delay: UInt = 0     // set to 5 to test delayed currency 
information
 #else
-            let delay: UInt = 0
+            let delay: UInt = 0     // no delay for release builds
 #endif
           Group {
-#if TABBAR  // Taler Wallet
 //            let labelStyle = iconOnly ? IconOnlyLabelStyle() : 
TitleAndIconLabelStyle()    // labelStyle doesn't work
             TabView(selection: tabSelection()) {
                 NavigationView {
@@ -186,9 +152,10 @@ extension MainView {
                                   navTitle: balancesTitle,
                                   balances: $balances,
                       shouldReloadBalances: $shouldReloadBalances)
-                }.navigationViewStyle(.stack)
+                }.id(viewState.rootViewId)                // any change to 
rootViewId triggers popToRootView behaviour
+                .navigationViewStyle(.stack)
                 .tabItem {
-                    Image(systemName: "chart.bar.xaxis")             // 
creditcard     system will automatically use filled variant
+                    Image(systemName: "chart.bar.xaxis")             // iOS 
will automatically use filled variant
                         .accessibilityLabel(balancesTitle)
                     if !iconOnly { Text(balancesTitle) }
                 }
@@ -201,7 +168,7 @@ extension MainView {
                                   navTitle: exchangesTitle)
                 }.navigationViewStyle(.stack)
                 .tabItem {
-                    Image(systemName: "arrow.triangle.2.circlepath")
+                    Image(systemName: "building.columns")    //  
"arrow.triangle.2.circlepath")
                         .accessibilityLabel(exchangesTitle)
                     if !iconOnly { Text(exchangesTitle) }
                 }
@@ -218,35 +185,6 @@ extension MainView {
                 .tag(Tab.settings)
             }
 //            .animation(.linear(duration: LAUNCHDURATION), value: 
selectedTab)     doesn't work. Needs CustomTabView
-#else       // GNU Taler
-            ZStack(alignment: .leading) {
-                NavigationView {   // the one and only for all non-sheet views
-                    VStack(alignment: .leading) {   // only needed for 
backslide transition
-                        views[currentView].view
-                            .id(views[currentView].name)
-                            .frame(maxWidth: .infinity, maxHeight: .infinity, 
alignment: .center)
-                            .transition(.backslide)
-                    }   .id(talerFont)
-                        .navigationBarTitleDisplayMode(.automatic)
-                        .background(NavigationBarBuilder { 
navigationController in
-                            //                            
navigationController.navigationBar.barTintColor = .red
-                            
navigationController.navigationBar.titleTextAttributes =
-                                [.font: TalerFont.uiFont(talerFont, size: 24, 
relativeTo: .title2)]
-                            
navigationController.navigationBar.largeTitleTextAttributes =
-                                [.font: TalerFont.uiFont(talerFont, size: 38, 
relativeTo: .largeTitle)]
-                        })
-                }
-                .navigationViewStyle(.stack)
-                .talerNavBar(talerFont: talerFont)
-
-                // The side view is above (Z-Axis) the current view
-                SideBarView(stack: stack.push(),
-                            views: views,
-                      currentView: $currentView,
-                   sidebarVisible: sidebarVisible,
-                  hamburgerAction: hamburgerAction)
-            }
-#endif
           } .onNotification(.KYCrequired) { notification in
               // show an alert with the KYC link (button) which opens in Safari
                 if let transition = 
notification.userInfo?[TRANSACTIONTRANSITION] as? TransactionTransition {
@@ -276,16 +214,17 @@ extension MainView {
                 logger.info(".onNotification(.TransactionExpired) ==> reload")
                 shouldReloadBalances += 1
             }
+            .onNotification(.TransactionDone) {
+                shouldReloadBalances += 1
+                selectedTab = .balances
+            }
             .onChange(of: balances) { newArray in
                 for balance in newArray {
                     let scope = balance.scopeInfo
-                    logger.info("balance changed: \(scope.currency, privacy: 
.public)")
-                    if !controller.hasInfo(for: scope.currency) {
+                    if controller.hasInfo(for: scope.currency) == nil {
                         Task { // runs on MainActor
-                            logger.info("Task to get info for: 
\(scope.currency, privacy: .public)")
                             do {
-                                let info = try await 
model.getCurrencyInfo(scope: scope, delay: delay)
-                                logger.info("got info: \(scope.currency, 
privacy: .public)")
+                                let info = try await 
model.getCurrencyInfoM(scope: scope, delay: delay)
                                 await controller.setInfo(info)
                             } catch {    // TODO: error handling - couldn't 
get CurrencyInfo
                                 logger.error("Couldn't get info for: 
\(scope.currency, privacy: .public)\n\(error)")
diff --git a/TalerWallet1/Views/Main/SideBarView.swift 
b/TalerWallet1/Views/Main/SideBarView.swift
deleted file mode 100644
index 0ae3b1d..0000000
--- a/TalerWallet1/Views/Main/SideBarView.swift
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-import SymLog
-
-fileprivate let sidebarWidth = 200.0
-
-struct SidebarItem {
-    var name: String
-    var sysImage: String?
-    var view: AnyView
-}
-
-struct SideBarView: View {
-    private let symLog = SymLogV(0)
-    let stack: CallStack
-    let views: [SidebarItem]
-    @Binding var currentView: Int
-    let sidebarVisible: Bool
-    let hamburgerAction: () -> Void
-    @State private var rotationEnabled = false
-
-    var body: some View {
-        HStack {    // sideView left, clear dismiss target right
-            EqualIconWidthDomain {
-                VStack(spacing: 10) {
-                    let gnuTaler = String("GNU Taler")  // this should NOT be 
translated
-                    Link(gnuTaler, destination: 
URL(string:"https://taler.net";)!)
-                        .accessibilityFont(.largeTitle)
-                        .padding(.top, 30)
-                    RotatingTaler(size: 100, rotationEnabled: $rotationEnabled)
-                        .onTapGesture {
-                            rotationEnabled.toggle()
-                        }
-                    ForEach(0..<views.count, id: \.self) { i in
-                        Button {
-                            symLog.log("sidebar item \"\(views[i].name)\" 
selected")
-                            hamburgerAction()      // slide sidebar to the left
-                            withAnimation(.easeInOut(duration: 0.3)) 
{currentView = i}        // animate to the view the user selected
-                        } label: {
-                            if let sysImage = views[i].sysImage {
-                                Label(views[i].name, systemImage: sysImage)
-                                    .frame(maxWidth: sidebarWidth, alignment: 
.leading)
-                            } else {
-                                Text(views[i].name)
-                                    .frame(maxWidth: sidebarWidth)
-                            }
-                        }
-                        .padding()
-                        .buttonStyle(.borderless)
-                        .accessibilityFont(.title2)
-                        .disabled(i == currentView)
-                        .accessibilityHidden(i == currentView)      // don't 
suggest the current item
-                    }
-                    Spacer()
-                }
-                .background(WalletColors().sideBackground)
-                .frame(width: sidebarWidth, alignment: .center)
-                // TODO: use leading instead of sidebarWidth for right-to-left
-                .offset(x: sidebarVisible ? 0 : -sidebarWidth)
-                //  .onAppear can NOT be used here, because we don't show or 
dismiss this view,
-                //   but only slide it left or right - so it is always there.
-            }
-            // this is just a target for a tap gesture outside the sidebar to 
dismiss it
-            Color.clear
-                .frame(maxWidth: sidebarVisible ? .infinity : 0, maxHeight: 
.infinity, alignment: .leading)
-                // TODO: right-to-left ?
-                .offset(x: sidebarVisible ? sidebarWidth : 0)
-                .contentShape(Rectangle())
-                .onTapGesture {
-                    hamburgerAction()      // slide sidebar to the left
-                }
-        }
-    }
-}
-// MARK: -
-#if DEBUG
-fileprivate struct BindingViewContainer : View {
-    @State var currentView: Int = 0
-    @State var sidebarVisible: Bool = true
-    var views: [SidebarItem]
-
-    var body: some View {
-        ZStack(alignment: .leading) {
-            views[currentView].view
-                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: 
.center)
-            SideBarView(stack: CallStack("Preview"), views: views, 
currentView: $currentView,
-                        sidebarVisible: sidebarVisible,
-                        hamburgerAction: { sidebarVisible = !sidebarVisible })
-        }
-    }
-}
-
-struct SideBarView_Previews: PreviewProvider {
-    static var views: [SidebarItem] {[
-        SidebarItem(name: "Balances",
-                sysImage: "creditcard.fill",        // TODO: Wallet Icon
-                    view: AnyView(WalletEmptyView(stack: 
CallStack("Preview")))),
-        SidebarItem(name: "Settings",
-                sysImage: "gearshape.fill",
-                    view: AnyView(WalletEmptyView(stack: 
CallStack("Preview"))))
-    ]}
-    static var previews: some View {
-        BindingViewContainer(views: views)
-    }
-}
-#endif
diff --git a/TalerWallet1/Views/Main/WalletEmptyView.swift 
b/TalerWallet1/Views/Main/WalletEmptyView.swift
index f38b657..6caaf15 100644
--- a/TalerWallet1/Views/Main/WalletEmptyView.swift
+++ b/TalerWallet1/Views/Main/WalletEmptyView.swift
@@ -19,7 +19,7 @@ struct WalletEmptyView: View {
                 Text("There is no digital cash in your wallet.")
                     .accessibilityFont(.title3)
                     .listRowSeparator(.hidden)
-                let title = String(localized: "LinkTitle_DEMOBANK", 
defaultValue: "Get some test money")
+                let title = String(localized: "LinkTitle_Test_Money", 
defaultValue: "Get some test money")
                 Link(title, destination: URL(string: DEMOBANK)!)
                     .buttonStyle(TalerButtonStyle(type: .prominent, narrow: 
false, aligned: .center))
                     .padding(.vertical)
diff --git a/TalerWallet1/Views/Peer2peer/P2PReadyV.swift 
b/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
index b448eaa..e155bd8 100644
--- a/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
+++ b/TalerWallet1/Views/Peer2peer/P2PReadyV.swift
@@ -32,7 +32,7 @@ struct P2PReadyV: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -51,7 +51,7 @@ struct P2PReadyV: View {
                 .navigationBarBackButtonHidden(true)
                 .interactiveDismissDisabled()           // can only use "Done" 
button to dismiss
             } else {
-                WithdrawProgressView(message: "Loading...")
+                LoadingView(url: nil, message: "for 
\(amountToTransfer.currencyStr)")
             }
         }
         .navigationTitle(navTitle)
diff --git a/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift 
b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
index d6a788c..4ee9f03 100644
--- a/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
+++ b/TalerWallet1/Views/Peer2peer/P2PSubjectV.swift
@@ -51,7 +51,7 @@ struct P2PSubjectV: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog(amountToTransfer.readableDescription)       // 
just to get the # to compare it with .onAppear & onDisappear
 #endif
@@ -87,7 +87,7 @@ struct P2PSubjectV: View {
                     .textFieldStyle(.roundedBorder)
                     .onAppear {
                         symLog.log("dispatching kbd...")
-                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
+                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
                             isFocused = true        // make first responder - 
raise keybord
                             symLog.log("...kbd isFocused")
                         }
@@ -129,7 +129,7 @@ struct P2PSubjectV: View {
 //            print("❗️ P2PSubjectV onDisappear")
         }
         .task(id: amountToTransfer.value) {
-            if feeLabel == nil {
+            if amountToSend && feeLabel == nil {
                 do {
                     let ppCheck = try await 
model.checkPeerPushDebitM(amountToTransfer)
                     if let feeAmount = p2pFee(ppCheck: ppCheck) {
diff --git a/TalerWallet1/Views/Peer2peer/RequestPayment.swift 
b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
index f30bb0f..5de2e1d 100644
--- a/TalerWallet1/Views/Peer2peer/RequestPayment.swift
+++ b/TalerWallet1/Views/Peer2peer/RequestPayment.swift
@@ -29,7 +29,7 @@ struct RequestPayment: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
diff --git a/TalerWallet1/Views/Peer2peer/SendAmount.swift 
b/TalerWallet1/Views/Peer2peer/SendAmount.swift
index d74d264..b5c4f5c 100644
--- a/TalerWallet1/Views/Peer2peer/SendAmount.swift
+++ b/TalerWallet1/Views/Peer2peer/SendAmount.swift
@@ -45,7 +45,7 @@ struct SendAmount: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -150,7 +150,7 @@ fileprivate struct Preview_Content: View {
     @State private var summary: String = ""
 
     var body: some View {
-        let amount = Amount(currency: TESTCURRENCY, integer: 10, fraction: 0)
+        let amount = Amount(currency: TESTCURRENCY, cent: 1000)
         SendAmount(stack: CallStack("Preview"),
          amountAvailable: amount,
         amountToTransfer: $amountToPreview,
diff --git a/TalerWallet1/Views/Settings/AboutView.swift 
b/TalerWallet1/Views/Settings/AboutView.swift
index ae5fe33..a00230f 100644
--- a/TalerWallet1/Views/Settings/AboutView.swift
+++ b/TalerWallet1/Views/Settings/AboutView.swift
@@ -24,7 +24,7 @@ struct AboutView: View {
     @State private var listID = UUID()
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -44,7 +44,7 @@ struct AboutView: View {
                     .accessibilityAddTraits(.isLink)
                     .accessibilityRemoveTraits(.isStaticText)
                     .onTapGesture() {
-                        
UIApplication.shared.open(URL(string:"https://taler.net";)!, options: [:])
+                        UIApplication.shared.open(URL(string:TALER_NET)!, 
options: [:])
                     }
 
                 SettingsItem(name: "App Version", id1: "app") {
@@ -53,15 +53,15 @@ struct AboutView: View {
                 SettingsItem(name: "Wallet Core Version", id1: "wallet-core") {
                     Text(verbatim: "\(walletCore.versionInfo?.version ?? 
"unknown")")
                 }
-                SettingsItem(name: "Supported Exchange Versions", id1: 
"exchange") {
-                    Text(verbatim: "\(walletCore.versionInfo?.exchange ?? 
"unknown")")
-                }
-                SettingsItem(name: "Supported Merchant Versions", id1: 
"merchant") {
-                    Text(verbatim: "\(walletCore.versionInfo?.merchant ?? 
"unknown")")
-                }
-                SettingsItem(name: "Used Bank", id1: "bank") {
-                    Text(verbatim: "\(walletCore.versionInfo?.bank ?? 
"unknown")")
-                }
+//                SettingsItem(name: "Supported Exchange Versions", id1: 
"exchange") {
+//                    Text(verbatim: "\(walletCore.versionInfo?.exchange ?? 
"unknown")")
+//                }
+//                SettingsItem(name: "Supported Merchant Versions", id1: 
"merchant") {
+//                    Text(verbatim: "\(walletCore.versionInfo?.merchant ?? 
"unknown")")
+//                }
+//                SettingsItem(name: "Used Bank", id1: "bank") {
+//                    Text(verbatim: "\(walletCore.versionInfo?.bank ?? 
"unknown")")
+//                }
             }
             .id(listID)
             .listStyle(myListStyle.style).anyView
diff --git a/TalerWallet1/Views/Settings/SettingsItem.swift 
b/TalerWallet1/Views/Settings/SettingsItem.swift
index 4c4bc72..595425e 100644
--- a/TalerWallet1/Views/Settings/SettingsItem.swift
+++ b/TalerWallet1/Views/Settings/SettingsItem.swift
@@ -20,9 +20,13 @@ struct SettingsItem<Content: View>: View {
     var body: some View {
         HStack {
             VStack {
+                let isWeb = id1?.hasPrefix("web") ?? false
+                let foreColor = isWeb ? WalletColors().tint
+                                      : .primary
                 Text(name)
                     .id(id1)
                     .frame(maxWidth: .infinity, alignment: .leading)
+                    .foregroundColor(foreColor)
                     .accessibilityFont(.title2)
                     .padding([.bottom], 0.01)
                 if let desc = description {
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift 
b/TalerWallet1/Views/Settings/SettingsView.swift
index a81a9ab..06d552d 100644
--- a/TalerWallet1/Views/Settings/SettingsView.swift
+++ b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -29,17 +29,15 @@ struct SettingsView: View {
     @AppStorage("developerMode") var developerMode: Bool = false
 #endif
     @AppStorage("useHaptics") var useHaptics: Bool = true
-    @AppStorage("playSounds") var playSounds: Int = 1
+    @AppStorage("playSoundsI") var playSoundsI: Int = 1
+    @AppStorage("playSoundsB") var playSoundsB: Bool = true
+    @AppStorage("shouldShowWarning") var shouldShowWarning: Bool = true
+//    @AppStorage("increaseContrast") var increaseContrast: Bool = false
     @AppStorage("talerFont") var talerFont: Int = 0
     @AppStorage("developDelay") var developDelay: Bool = false
     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
     @AppStorage("iconOnly") var iconOnly: Bool = false
 
-#if TABBAR  // Taler Wallet
-#else       // GNU Taler
-    var hamburgerAction: () -> Void
-#endif
-
     @State private var checkDisabled = false
     @State private var withDrawDisabled = false
 #if DEBUG
@@ -80,14 +78,9 @@ struct SettingsView: View {
         }
     }
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
-#endif
-#if TABBAR  // Taler Wallet
-        let hamburger: HamburgerButton? = nil
-#else       // GNU Taler
-        let hamburger: HamburgerButton = HamburgerButton(action: 
hamburgerAction)
 #endif
         let walletCore = WalletCore.shared
         Group {
@@ -100,12 +93,13 @@ struct SettingsView: View {
                           description: hideDescriptions ? nil : 
String(localized: "More info about this app...")) {}
                 }
 #if DEBUG
-                Group {
+                if showDevelopItems {
                     Text("https://bank.taler.grothoff.org/";)
                     Text("https://bank.taler.fdold.eu/";)
                     Text("https://bank.head.taler.net/";)
                     Text("https://bank.test.taler.net/";)
                     Text("https://bank.demo.taler.net/";)
+                    Text("https://bank.taler.ar/";)
                 }
 #endif
                 if controller.hapticCapability.supportsHaptics {
@@ -113,11 +107,26 @@ struct SettingsView: View {
                             description: hideDescriptions ? nil : 
String(localized: "Vibration Feedback"))
                     .id("playHaptics")
                 }
-                SettingsSpeaker(name: String(localized: "Play Payment 
Sounds"), value: $playSounds,
-                         description: hideDescriptions ? nil : 
String(localized: "When a transaction finished"))
-                .id("playSounds")
-                SettingsFont(title: String(localized: "Font:"), value: 
talerFont, action: redraw)
-                    .id("font")
+                let playToggle = SettingsToggle(name: String(localized: "Play 
Payment Sounds"), value: $playSoundsB,
+                                         description: hideDescriptions ? nil : 
String(localized: "When a transaction finished"))
+                                    .id("playSounds")
+#if DEBUG
+                if Double.random(in: -100.0...100.0) > 0 {
+                    SettingsSpeaker(name: String(localized: "Play Payment 
Sounds"), value: $playSoundsI,
+                                    description: hideDescriptions ? nil : 
String(localized: "When a transaction finished"))
+                    .id("playSounds")
+                } else { playToggle }
+#else
+                playToggle
+#endif
+//                SettingsToggle(name: String(localized: "Increase Contrast"), 
value: $increaseContrast, id1: "contrast",
+//                               description: hideDescriptions ? nil : 
String(localized: "If you don't want to set it globally in Settings.app"))
+//                .id("increaseContrast")
+                SettingsToggle(name: String(localized: "Show Warnings"), 
value: $shouldShowWarning, id1: "warnings",
+                               description: hideDescriptions ? nil : 
String(localized: "For Delete, Fail & Abort buttons"))
+                .id("showWarnings")
+//                SettingsFont(title: String(localized: "Font:"), value: 
talerFont, action: redraw)
+//                    .id("font")
                 SettingsStyle(title: String(localized: "Liststyle:"), 
myListStyle: $myListStyle)
                     .id("liststyle")
                 SettingsToggle(name: String(localized: "Minimalistic"), value: 
$iconOnly, id1: "minimal",
@@ -237,6 +246,23 @@ struct SettingsView: View {
                             .buttonStyle(.bordered)
                             .disabled(checkDisabled)
                         }.id("test2runTest")
+                        SettingsItem(name: String("Run Infinite Transaction 
Loop"), id1: "runInfinite",
+                              description: hideDescriptions ? nil : 
String("Check DB in background")) {
+                            let title = "Loop"
+                            Button(title) {
+                                checkDisabled = true    // don't run twice
+                                Task { // runs on MainActor
+                                    symLog.log("Running Infinite Transaction 
Loop")
+                                    do {
+                                        try await 
model.testingInfiniteTransaction(delayMs: 10_000, shouldFetch: true)
+                                    } catch {    // TODO: show error
+                                        symLog.log(error.localizedDescription)
+                                    }
+                                }
+                            }
+                            .buttonStyle(.bordered)
+                            .disabled(checkDisabled)
+                        }.id("runInfiniteLoop")
                         SettingsItem(name: String("Save Logfile"), id1: "save",
                               description: hideDescriptions ? nil : 
String("Help debugging wallet-core")) {
                             Button("Save") {
@@ -261,7 +287,6 @@ struct SettingsView: View {
             .listStyle(myListStyle.style).anyView
         }
         .navigationTitle(navTitle)
-        .navigationBarItems(leading: hamburger)
         .onAppear() {
             showDevelopItems = developerMode
             hideDescriptions = iconOnly
@@ -292,11 +317,7 @@ struct SettingsView: View {
 #if DEBUG
 struct SettingsView_Previews: PreviewProvider {
     static var previews: some View {
-#if TABBAR  // Taler Wallet
         SettingsView(stack: CallStack("Preview"), navTitle: "Settings")
-#else       // GNU Taler
-        SettingsView(stack: CallStack("Preview"), navTitle: "Settings") { }
-#endif
     }
 }
 #endif
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift 
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
index 0088fdc..51cd0d4 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pAcceptDone.swift
@@ -22,9 +22,12 @@ struct P2pAcceptDone: View {
     func reloadOneAction(_ transactionId: String) async throws -> Transaction {
         return try await model.getTransactionByIdT(transactionId)
     }
-
+    func dismissTopAnimated(_ stack: CallStack) {
+        dismissTop()
+    }
+    
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -34,7 +37,7 @@ struct P2pAcceptDone: View {
                       transactionId: transactionId,
                        reloadAction: reloadOneAction,
                            navTitle: navTitle,
-                         doneAction: { dismissTop() },
+                         doneAction: dismissTopAnimated,
                         abortAction: nil,
                        deleteAction: nil,
                          failAction: nil,
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift 
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
index 7d4b384..3b64c21 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pPayURIView.swift
@@ -36,7 +36,17 @@ struct P2pPayURIView: View {
                               bottomAbbrev: String(localized: "Effective:"),
                               bottomAmount: effective,
                                      large: false, pending: false, incoming: 
false,
-                                   baseURL: nil)
+                                   baseURL: nil,
+                                    status: nil,
+                                   summary: 
peerPullDebitResponse.contractTerms.summary)
+                    let expiration = 
peerPullDebitResponse.contractTerms.purse_expiration
+                    let (dateString, date) = TalerDater.dateString(from: 
expiration)
+                    let accessibilityDate = TalerDater.accessibilityDate(date) 
?? dateString
+                    let accessibilityLabel = String(localized: "Expires: 
\(accessibilityDate)")
+                    Text("Expires: \(dateString)")
+                        .accessibilityFont(.body)
+                        .accessibilityLabel(accessibilityLabel)
+//                        .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
                 }
                 .listStyle(myListStyle.style).anyView
                 .navigationTitle(navTitle)
@@ -51,25 +61,21 @@ struct P2pPayURIView: View {
                     .buttonStyle(TalerButtonStyle(type: .prominent))
                     .padding(.horizontal)
             } else {
-                WithdrawProgressView(message: url.host ?? "Yikes❗️ no valid 
URL")
-                    .navigationTitle("Contacting Exchange")
+                LoadingView(url: url, message: nil)
+                    .task { do {
+                        symLog.log(".task")
+                        let ppDebitResponse = try await 
model.preparePeerPullDebitM(url.absoluteString)
+                        peerPullDebitResponse = ppDebitResponse
+                    } catch {    // TODO: error
+                        symLog.log(error.localizedDescription)
+                        peerPullDebitResponse = nil
+                    } }
             }
         }
         .onAppear() {
             symLog.log("onAppear")
             DebugViewC.shared.setSheetID(SHEET_PAY_P2P)
         }
-        .task {
-            do { // TODO: cancelled
-                symLog.log(".task")
-                let ppDebitResponse = try await 
model.preparePeerPullDebitM(url.absoluteString)
-                peerPullDebitResponse = ppDebitResponse
-            } catch {    // TODO: error
-                symLog.log(error.localizedDescription)
-                peerPullDebitResponse = nil
-            }
-        }
-
     }
 }
 // MARK: -
diff --git a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift 
b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
index 05da8fb..f810da7 100644
--- a/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
+++ b/TalerWallet1/Views/Sheets/P2P_Sheets/P2pReceiveURIView.swift
@@ -23,9 +23,15 @@ struct P2pReceiveURIView: View {
     @State private var exchange: Exchange? = nil
 
     var body: some View {
-        let badURL = "Error in URL: \(url)"
         VStack {
             if let peerPushCreditResponse {
+                let tosAccepted = exchange?.tosStatus == .accepted
+                if !tosAccepted {
+                    ToSButtonView(stack: stack.push(),
+                        exchangeBaseUrl: 
peerPushCreditResponse.exchangeBaseUrl,
+                                 viewID: SHEET_RCV_P2P_TOS,
+                                    p2p: true)
+                }
                 List {
                     let raw = peerPushCreditResponse.amountRaw
                     let effective = peerPushCreditResponse.amountEffective
@@ -38,11 +44,20 @@ struct P2pReceiveURIView: View {
                               bottomAbbrev: String(localized: "Effective:"),
                               bottomAmount: effective,
                                      large: false, pending: false, incoming: 
true,
-                                   baseURL: nil)
+                                   baseURL: nil,
+                                    status: nil,
+                                   summary: 
peerPushCreditResponse.contractTerms.summary)
+                    let expiration = 
peerPushCreditResponse.contractTerms.purse_expiration
+                    let (dateString, date) = TalerDater.dateString(from: 
expiration)
+                    let accessibilityDate = TalerDater.accessibilityDate(date) 
?? dateString
+                    let accessibilityLabel = String(localized: "Expires: 
\(accessibilityDate)")
+                    Text("Expires: \(dateString)")
+                        .accessibilityFont(.body)
+                        .accessibilityLabel(accessibilityLabel)
+//                        .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
                 }
                 .listStyle(myListStyle.style).anyView
                 .navigationTitle(navTitle)
-                let tosAccepted = exchange?.tosStatus == .accepted
                 if tosAccepted {
                     NavigationLink(destination: LazyView {
                         P2pAcceptDone(stack: stack.push(),
@@ -53,16 +68,9 @@ struct P2pReceiveURIView: View {
                     }
                     .buttonStyle(TalerButtonStyle(type: .prominent))
                     .padding(.horizontal)
-                } else {
-                    ToSButtonView(stack: stack.push(),
-                        exchangeBaseUrl: 
peerPushCreditResponse.exchangeBaseUrl,
-                                 viewID: SHEET_RCV_P2P_TOS,
-                                    p2p: true)
                 }
             } else {
-                // Yikes no details or no baseURL
-                WithdrawProgressView(message: url.host ?? badURL)
-                    .navigationTitle("Contacting Exchange")
+                LoadingView(url: url, message: nil)
             }
         }
         .onAppear() {
diff --git a/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift 
b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
index 0a7028e..019152b 100644
--- a/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PayTemplateView.swift
@@ -66,7 +66,9 @@ struct PayTemplateView: View {
                               bottomAbbrev: String(localized: "Effective:"),
                               bottomAmount: effective,
                                      large: false, pending: false, incoming: 
false,
-                                   baseURL: baseURL)
+                                   baseURL: baseURL,
+                                    status: nil,
+                                   summary: nil)
                     // TODO: payment: popup with all possible exchanges, check 
fees
                 } else if let balanceDetails = preparePayResult.balanceDetails 
{    // Insufficient
                     Text("You don't have enough \(currency)")
@@ -78,7 +80,9 @@ struct PayTemplateView: View {
                               bottomAbbrev: String(localized: "Available:"),
                               bottomAmount: balanceDetails.balanceAvailable,
                                      large: false, pending: false, incoming: 
false,
-                                   baseURL: baseURL)
+                                   baseURL: baseURL,
+                                    status: nil,
+                                   summary: nil)
                 } else {
                     // TODO: Error - neither effective nor balanceDetails
                     Text("Error")
@@ -103,9 +107,7 @@ struct PayTemplateView: View {
                 DebugViewC.shared.setSheetID(SHEET_PAY_TEMPLATE)
             }
         } else {
-            let badURL = "Error in Link: \(url)"
-            WithdrawProgressView(message: url.host ?? badURL)
-                .navigationTitle("Find Exchange")
+            LoadingView(url: url, message: nil)
                 .task {
                     do {
                         symLog.log(".task")
diff --git a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift 
b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
index 4ce2970..1124006 100644
--- a/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
+++ b/TalerWallet1/Views/Sheets/Payment/PaymentView.swift
@@ -45,8 +45,7 @@ struct PaymentView: View {
             let effective = preparePayResult.amountEffective
             let terms = preparePayResult.contractTerms
             List {
-                Text(terms.summary)
-                    .accessibilityFont(.title3)
+                // TODO: show balanceDetails.balanceAvailable
                 let baseURL = 
preparePayResult.contractTerms.exchanges.first?.url
                 let raw = preparePayResult.amountRaw
                 let currency = raw.currencyStr
@@ -62,11 +61,13 @@ struct PaymentView: View {
                               bottomAbbrev: String(localized: "Effective:"),
                               bottomAmount: effective,
                                      large: false, pending: false, incoming: 
false,
-                                   baseURL: baseURL)
+                                   baseURL: baseURL,
+                                    status: nil,
+                                   summary: terms.summary)
                     // TODO: payment: popup with all possible exchanges, check 
fees
                 } else if let balanceDetails = preparePayResult.balanceDetails 
{    // Insufficient
                     Text("You don't have enough \(currency)")
-                        .accessibilityFont(.body)
+                        .accessibilityFont(.headline)
                     ThreeAmountsV(topTitle: topTitle,
                                  topAbbrev: topAbbrev,
                                  topAmount: raw, fee: nil,
@@ -74,7 +75,9 @@ struct PaymentView: View {
                               bottomAbbrev: String(localized: "Available:"),
                               bottomAmount: balanceDetails.balanceAvailable,
                                      large: false, pending: false, incoming: 
false,
-                                   baseURL: baseURL)
+                                   baseURL: baseURL,
+                                    status: nil,
+                                   summary: terms.summary)
                 } else {
                     // TODO: Error - neither effective nor balanceDetails
                     Text("Error")
@@ -99,9 +102,7 @@ struct PaymentView: View {
                 DebugViewC.shared.setSheetID(SHEET_PAYMENT)
             }
         } else {
-            let badURL = "Error in Link: \(url)"
-            WithdrawProgressView(message: url.host ?? badURL)
-                .navigationTitle("Find Exchange")
+            LoadingView(url: url, message: nil)
                 .task {
                     do {
                         symLog.log(".task")
diff --git a/TalerWallet1/Views/Sheets/QRSheet.swift 
b/TalerWallet1/Views/Sheets/QRSheet.swift
index 17cf9f2..33aab47 100644
--- a/TalerWallet1/Views/Sheets/QRSheet.swift
+++ b/TalerWallet1/Views/Sheets/QRSheet.swift
@@ -19,7 +19,7 @@ struct QRSheet: View {
 
                 if let scannedURL = URL(string: scannedCode!) {
                     let scheme = scannedURL.scheme
-                    if scheme == "taler" {
+                    if scheme?.lowercased() == "taler" {
                         URLSheet(stack: stack.push(), urlToOpen: scannedURL)
                     } else {
 //                        let _ = print(scannedURL)       // TODO: logging
diff --git a/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift 
b/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
index edebcff..66d1870 100644
--- a/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
+++ b/TalerWallet1/Views/Sheets/Refund/RefundURIView.swift
@@ -34,9 +34,7 @@ struct RefundURIView: View {
                           suspendAction: model.suspendTransaction,
                            resumeAction: model.resumeTransaction)
         } else {
-            let badURL = "Error in Link: \(url)"
-            WithdrawProgressView(message: url.host ?? badURL)
-                .navigationTitle("Find Exchange")
+            LoadingView(url: url, message: nil)
                 .task {
                     do {
                         symLog.log(".task")
diff --git a/TalerWallet1/Views/Sheets/URLSheet.swift 
b/TalerWallet1/Views/Sheets/URLSheet.swift
index 8e38872..09a0fc2 100644
--- a/TalerWallet1/Views/Sheets/URLSheet.swift
+++ b/TalerWallet1/Views/Sheets/URLSheet.swift
@@ -16,7 +16,7 @@ struct URLSheet: View {
     @State private var amountToTransfer = Amount.zero(currency: "")
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -26,8 +26,8 @@ struct URLSheet: View {
             switch urlCommand {
                 case .withdraw:
                     WithdrawURIView(stack: stack.push(), url: urlToOpen)
-//                case .withdrawExchange:
-//                    ManualWithdraw(stack: stack.push(), url: urlToOpen, 
exchange: nil, amountToTransfer: $amountToTransfer)
+                case .withdrawExchange:
+                    WithdrawExchangeV(stack: stack.push(), url: urlToOpen)
                 case .pay:
                     PaymentView(stack: stack.push(), url: urlToOpen)
                 case .payPull:
diff --git 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift 
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
index 048351d..a15ebd3 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawAcceptDone.swift
@@ -22,9 +22,12 @@ struct WithdrawAcceptDone: View {
     func reloadOneAction(_ transactionId: String) async throws -> Transaction {
             return try await model.getTransactionByIdT(transactionId)
     }
+    func dismissTopAnimated(_ stack: CallStack) {
+        dismissTop()
+    }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -34,7 +37,7 @@ struct WithdrawAcceptDone: View {
                               transactionId: transactionId,
                                reloadAction: reloadOneAction,
                                    navTitle: navTitle,
-                                 doneAction: { dismissTop() },
+                                 doneAction: dismissTopAnimated,
                                 abortAction: nil,
                                deleteAction: nil,
                                  failAction: nil,
@@ -44,8 +47,8 @@ struct WithdrawAcceptDone: View {
                 .interactiveDismissDisabled()           // can only use "Done" 
button to dismiss
                 .navigationTitle(navTitle)
             } else {
-                WithdrawProgressView(message: "Bank Confirmation")
-                    .navigationTitle("Loading...")
+                LoadingView(url: nil, message: exchangeBaseUrl?.trimURL()
+                                                    ?? "Bank Confirmation")
             }
         }.onAppear() {
             symLog.log("onAppear")
diff --git 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift 
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift
deleted file mode 100644
index 0c7e906..0000000
--- 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawProgressView.swift
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
- * See LICENSE.md
- */
-import SwiftUI
-
-struct WithdrawProgressView: View {
-    let message: String
-
-    var body: some View {
-        VStack {
-            Spacer()
-            ProgressView()
-            Spacer()
-            HStack {
-                Spacer()
-                Text(message)
-                    .accessibilityFont(.title)
-                Spacer()
-            }
-            Spacer()
-            Spacer()
-        }
-        .background(WalletColors().backgroundColor.edgesIgnoringSafeArea(.all))
-    }
-}
-
-struct WithdrawProgressView_Previews: PreviewProvider {
-    static var previews: some View {
-        WithdrawProgressView(message: "message")
-    }
-}
diff --git 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift 
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
index 0eba89c..7cba37b 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
@@ -4,6 +4,7 @@
  */
 import SwiftUI
 import SymLog
+import MarkdownUI
 
 struct WithdrawTOSView: View {
     private let symLog = SymLogV(0)
@@ -21,12 +22,34 @@ struct WithdrawTOSView: View {
 
     @State var exchangeTOS: ExchangeTermsOfService?
 
+    func loadToS(_ language: String) async {
+        do {
+            if let exchangeBaseUrl {
+                let acceptedFormat: [String] = [MARKDOWN, PLAINTEXT]
+                let someTOS = try await 
model.loadExchangeTermsOfServiceM(exchangeBaseUrl,
+                                                           acceptedFormat: 
acceptedFormat,
+                                                           acceptLanguage: 
language)
+                exchangeTOS = someTOS
+            } else {
+                // TODO: Yikes! No baseURL
+
+
+
+            }
+        } catch {    // TODO: error
+            symLog.log(error.localizedDescription)
+        }
+    }
+
     var body: some View {
-        VStack {
-            Content(symLog: symLog, exchangeTOS: exchangeTOS, myListStyle: 
$myListStyle) {
+        let languageCode = Locale.preferredLanguageCode
+//        let languageName = Locale.current.localizedString(forLanguageCode: 
languageCode)
+        if let exchangeTOS {
+            Content(symLog: symLog, tos: exchangeTOS, myListStyle: 
$myListStyle,
+                  language: languageCode, languageAction: loadToS) {
                 Task { // runs on MainActor
                     do {
-                        if let exchangeBaseUrl, let exchangeTOS {
+                        if let exchangeBaseUrl {
                             _ = try await 
model.setExchangeTOSAcceptedM(exchangeBaseUrl, etag: exchangeTOS.currentEtag)
                             if acceptAction != nil {
                                 acceptAction!()
@@ -42,76 +65,104 @@ struct WithdrawTOSView: View {
                 }
             }
             .navigationTitle(navTitle)
-            .overlay {
-                if exchangeTOS == nil {
-                    if let exchangeBaseUrl {
-                        WithdrawProgressView(message: 
exchangeBaseUrl.trimURL())
-                            .navigationTitle("Loading " + navTitle)
-                    } else {
-                        // Yikes!
-                        WithdrawProgressView(message: "No exchangeBaseUrl!")
-                            .navigationTitle("Loading " + navTitle)
-                    }
+            .onAppear() {
+                if viewID > SHEET_WITHDRAWAL {
+                    DebugViewC.shared.setSheetID(SHEET_WITHDRAW_TOS)
+                } else {
+                    DebugViewC.shared.setViewID(VIEW_WITHDRAW_TOS, stack: 
stack.push())
                 }
             }
-        }.onAppear() {
-            if viewID > SHEET_WITHDRAWAL {
-                DebugViewC.shared.setSheetID(SHEET_WITHDRAW_TOS)
-            } else {
-                DebugViewC.shared.setViewID(VIEW_WITHDRAW_TOS, stack: 
stack.push())
-            }
-        }.task {
-            do {
-                if let exchangeBaseUrl {
-                    let acceptedFormat: [String] = [MARKDOWN, PLAINTEXT]
-                    let someTOS = try await 
model.loadExchangeTermsOfServiceM(exchangeBaseUrl,
-                                                                
acceptedFormat: acceptedFormat)
-                    exchangeTOS = someTOS
+        } else {
+            LoadingView(url: nil, message: exchangeBaseUrl?.trimURL() ?? "No 
exchangeBaseUrl!")
+                .task {
+                    await loadToS(languageCode)
                 }
-            } catch {    // TODO: error
-                symLog.log(error.localizedDescription)
-            }
         }
     }
 }
 // MARK: -
 extension WithdrawTOSView {
+    struct plaintextToSV: View {
+        let plaintext: String
+
+        var body: some View {
+            let components = plaintext.components(separatedBy: "\n\n")
+
+            ForEach (components, id: \.self) { term0 in
+                let term1 = term0.replacingOccurrences(of: "\n     ", with: " 
")    // newline + 5 blanks
+                let term2 = term1.replacingOccurrences(of: "\n    ", with: " 
")     // newline + 4 blanks
+                let term3 = term2.replacingOccurrences(of: "\n   ", with: " ") 
     // newline + 3 blanks
+                let term4 = term3.replacingOccurrences(of: "\n  ", with: " ")  
     // newline + 2 blanks
+                let term5 = term4.replacingOccurrences(of: "\n ", with: " ")   
     // newline + 1 blank
+                let term6 = term5.replacingOccurrences(of: "\n", with: " ")    
     // remove all other linebreaks
+                let term7 = term6.replacingOccurrences(of: " ====", with: 
"\n====") // add them back for underscoring
+                let term8 = term7.replacingOccurrences(of: " ----", with: 
"\n----") // special for "Highlights:"
+                let term9 = term8.replacingOccurrences(of: " ****", with: 
"\n****") // special for "Terms of Service:"
+                Section {
+                    Text(term9)
+                        .accessibilityFont(.footnote)
+                        .foregroundColor(Color(UIColor.label))
+                }
+            } // for
+        }
+    }
+
     struct Content: View {
         let symLog: SymLogV
-        var exchangeTOS: ExchangeTermsOfService?
+        var tos: ExchangeTermsOfService
         @Binding var myListStyle: MyListStyle
+        let language: String
+        var languageAction: (String) async -> ()
         var acceptAction: () -> ()
 
+        @State private var selectedLanguage = Locale.preferredLanguageCode
+
         var body: some View {
-            if let tos = exchangeTOS {
+            let title = String(localized: "Language:", comment: "title of ToS 
language selection")
+            List {
+            if tos.tosAvailableLanguages.count > 1 {
+                Picker(title, selection: $selectedLanguage) {
+                        ForEach(tos.tosAvailableLanguages, id: \.self) { code 
in
+                            let languageName = 
Locale.current.localizedString(forLanguageCode: code)
+                            Text(languageName ?? code)
+                    }
+                }
+                .accessibilityFont(.title3)
+                .pickerStyle(.menu)
+                .onAppear() {
+                    withAnimation { selectedLanguage = language }
+                }
+                .onChange(of: selectedLanguage) { selected in
+                    Task {
+                        await languageAction(selected)
+                    }
+                }
+            }
+                if tos.contentType == MARKDOWN {
+                    Section {
+                        let content = MarkdownContent(tos.content)
+                        Markdown(content)
+                    }
+                } else {
                 let components = tos.content.components(separatedBy: "\n\n")
-
-                List (components, id: \.self) { term0 in
-                    if tos.contentType == PLAINTEXT {
+                ForEach (components, id: \.self) { term0 in
                         let term1 = term0.replacingOccurrences(of: "\n     ", 
with: " ")    // newline + 5 blanks
-                        let term2 = term1.replacingOccurrences(of: "\n"     , 
with: " ")    // remove all other linebreaks
-                        let term3 = term2.replacingOccurrences(of: " ====", 
with: "\n====") // add them back for underscoring
-                        let term4 = term3.replacingOccurrences(of: " ----", 
with: "\n----") // special for "Highlights:"
-                        let term5 = term4.replacingOccurrences(of: " ****", 
with: "\n****") // special for "Terms of Service:"
-//                        Text("term0")
-                        if #available(iOS 16.0, *) {
+                        let term2 = term1.replacingOccurrences(of: "\n    ", 
with: " ")     // newline + 4 blanks
+                        let term3 = term2.replacingOccurrences(of: "\n   ", 
with: " ")      // newline + 3 blanks
+                        let term4 = term3.replacingOccurrences(of: "\n  ", 
with: " ")       // newline + 2 blanks
+                        let term5 = term4.replacingOccurrences(of: "\n ", 
with: " ")        // newline + 1 blank
+                        let term6 = term5.replacingOccurrences(of: "\n", with: 
" ")         // remove all other linebreaks
+                        let term7 = term6.replacingOccurrences(of: " ====", 
with: "\n====") // add them back for underscoring
+                        let term8 = term7.replacingOccurrences(of: " ----", 
with: "\n----") // special for "Highlights:"
+                        let term9 = term8.replacingOccurrences(of: " ****", 
with: "\n****") // special for "Terms of Service:"
                             Section {
-                                Text(term5)
-                                    .accessibilityFont(.footnote)
-                                    .foregroundColor(Color(UIColor.label))
-                            }
-                        } else {
-                            Text(term5)
+                            Text(term9)
                                 .accessibilityFont(.footnote)
                                 .foregroundColor(Color(UIColor.label))
                         }
-                    } else {        // MarkDown
-                        Section {
-                            Text(term0)
-                                .accessibilityFont(.body)
-                        }
-                    }
-                }.safeAreaInset(edge: .bottom) {
+                    } // for
+                } // plain text
+            }.safeAreaInset(edge: .bottom) {
                     let currentEtag = tos.currentEtag
                     let showButton = tos.acceptedEtag == nil ? true
                                    : tos.acceptedEtag! == tos.currentEtag ? 
false
@@ -121,11 +172,8 @@ extension WithdrawTOSView {
                             .buttonStyle(TalerButtonStyle(type: .prominent))
                             .padding(.horizontal)
                     }
-                }
-                .listStyle(myListStyle.style).anyView
-            } else {
-                ErrorView(errortext: String(localized: "unknown ToS"))     // 
TODO: ???
             }
+            .listStyle(myListStyle.style).anyView
         }
     }
 }
diff --git 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift 
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
index 9ae8592..284d94d 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawURIView.swift
@@ -25,9 +25,15 @@ struct WithdrawURIView: View {
     @State private var exchange: Exchange? = nil
 
     var body: some View {
-        let badURL = "Error in URL: \(url)"
         VStack {
             if let withdrawalAmountDetails, let exchange {
+                let tosAccepted = exchange.tosStatus == .accepted
+                if !tosAccepted {
+                    ToSButtonView(stack: stack.push(),
+                        exchangeBaseUrl: exchange.exchangeBaseUrl,
+                                 viewID: SHEET_WITHDRAW_TOS,
+                                    p2p: false)
+                }
                 List {
                     let raw = withdrawalAmountDetails.amountRaw
                     let effective = withdrawalAmountDetails.amountEffective
@@ -44,7 +50,9 @@ struct WithdrawURIView: View {
                               bottomAbbrev: String(localized: "Effective:"),
                               bottomAmount: effective,
                                      large: false, pending: false, incoming: 
true,
-                                   baseURL: exchange.exchangeBaseUrl)
+                                   baseURL: exchange.exchangeBaseUrl,
+                                    status: nil,        // 
common.txState.major.localizedState
+                                   summary: nil)
                     let someCoins = SomeCoins(details: withdrawalAmountDetails)
                     QuiteSomeCoins(someCoins: someCoins,
                                shouldShowFee: true,       // TODO: set to 
false if we never charge withdrawal fees
@@ -54,7 +62,7 @@ struct WithdrawURIView: View {
                 }
                 .listStyle(myListStyle.style).anyView
                 .navigationTitle(navTitle)
-                if exchange.tosStatus == .accepted {
+                if tosAccepted {
                     NavigationLink(destination: LazyView {
                         WithdrawAcceptDone(stack: stack.push(),
                                  exchangeBaseUrl: exchange.exchangeBaseUrl,
@@ -64,16 +72,9 @@ struct WithdrawURIView: View {
                     }
                     .buttonStyle(TalerButtonStyle(type: .prominent))
                     .padding(.horizontal)
-                } else {
-                    ToSButtonView(stack: stack.push(),
-                        exchangeBaseUrl: exchange.exchangeBaseUrl,
-                                 viewID: SHEET_WITHDRAW_TOS,
-                                    p2p: false)
                 }
-            } else {
-                // Yikes no details or no exchange
-                WithdrawProgressView(message: url.host ?? badURL)
-                    .navigationTitle("Contacting Exchange")
+            } else {        // no details or no exchange
+                LoadingView(url: url, message: nil)
             }
         }
         .onAppear() {
diff --git a/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift 
b/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
new file mode 100644
index 0000000..dc32e73
--- /dev/null
+++ b/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
@@ -0,0 +1,53 @@
+/*
+ * This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
+ * See LICENSE.md
+ */
+import SwiftUI
+import taler_swift
+import SymLog
+
+struct WithdrawExchangeV: View {
+    private let symLog = SymLogV(0)
+    let stack: CallStack
+    let navTitle = String(localized: "Checking Link")
+    var url: URL
+
+    @EnvironmentObject private var controller: Controller
+    @EnvironmentObject private var model: WalletModel
+    @State private var exchangeBaseUrl: String?
+    @State private var amountToTransfer = Amount.zero(currency: "")
+
+    var body: some View {
+#if PRINT_CHANGES
+        let _ = Self._printChanges()
+        let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
+#endif
+        Group {
+            if let exchangeBaseUrl {
+                ManualWithdraw(stack: stack.push(),
+                     exchangeBaseUrl: exchangeBaseUrl,
+                    amountToTransfer: $amountToTransfer)
+            } else {
+                LoadingView(url: url, message: "No exchangeBaseUrl!")
+            }
+        }
+        .task {
+            do { // TODO: cancelled
+                symLog.log(".task")
+                let withdrawExchange = try await 
model.loadWithdrawalExchangeForUriM(url.absoluteString)
+                let baseUrl = withdrawExchange.exchangeBaseUrl
+                exchangeBaseUrl = baseUrl
+                if let amount = withdrawExchange.amount {
+                    amountToTransfer = amount
+                } else {
+                    // is already Amount.zero(currency: "")
+                }
+                // let the controller collect CurrencyInfo from this formerly 
unknown exchange
+                let _ = await controller.getInfo(from: baseUrl, model: model)
+            } catch {    // TODO: error
+                symLog.log(error.localizedDescription)
+                exchangeBaseUrl = nil
+            }
+        }
+    }
+}
diff --git a/TalerWallet1/Views/Transactions/ManualDetailsV.swift 
b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
index 42f827f..9099f45 100644
--- a/TalerWallet1/Views/Transactions/ManualDetailsV.swift
+++ b/TalerWallet1/Views/Transactions/ManualDetailsV.swift
@@ -3,64 +3,151 @@
  * See LICENSE.md
  */
 import SwiftUI
+import OrderedCollections
 import taler_swift
 
+struct AccountPicker: View {
+    let title: String
+    let value: Int
+    let accountDetails: [WithdrawalExchangeAccountDetails]
+    let action: (Int) -> Void
+
+    @State private var selectedAccount = 0
+
+    var body: some View {
+        Picker(title, selection: $selectedAccount, content: {
+            ForEach(0..<accountDetails.count, id: \.self, content: { index in
+                let detail = accountDetails[index]
+                let bankName = detail.bankName ?? "BankName " + String(index)
+                let amountStr = detail.transferAmount.readableDescription
+                Text(bankName + ":   " + amountStr)
+                    .tag(index)
+            })
+        })
+        .accessibilityFont(.title3)
+        .pickerStyle(.menu)
+        .onAppear() {
+            withAnimation { selectedAccount = value }
+        }
+        .onChange(of: selectedAccount) { selected in
+            action(selected)
+        }
+    }
+}
+
+struct TransferRestrictionsV: View {
+    let amountStr: String
+    let obtainStr: String
+    let restrictions: [AccountRestriction]?
+
+    @AppStorage("iconOnly") var iconOnly: Bool = false
+
+    @State private var selectedLanguage = Locale.preferredLanguageCode
+
+    var body: some View {
+        VStack(alignment: .leading) {
+            Text(iconOnly ? "Transfer \(amountStr) to the Exchange."
+                          : "You need to transfer \(amountStr) from your 
regular bank account to the Exchange to receive \(obtainStr) as electronic cash 
in this wallet.")
+            .multilineTextAlignment(.leading)
+            if let restrictions {
+                ForEach(restrictions) { restriction in
+                    if let hintsI18 = restriction.human_hint_i18n {
+//                        let sortedDict = OrderedDictionary(uniqueKeys: 
hintsI18.keys, values: hintsI18.values)
+//                        var sorted: OrderedDictionary<String:String>
+                        let sortedDict = 
OrderedDictionary(uncheckedUniqueKeysWithValues: hintsI18.sorted { $0.key < 
$1.key })
+                        Picker("Restriction:", selection: $selectedLanguage) {
+                            ForEach(sortedDict.keys, id: \.self) {
+                                Text(sortedDict[$0] ?? "missing hint")
+                            }
+                        }
+                    } else if let hint = restriction.human_hint {
+                        Text(hint)
+                    }
+                }
+            }
+        }
+    }
+}
+
 struct ManualDetailsV: View {
     var common : TransactionCommon
     var details : WithdrawalDetails
 
     @AppStorage("iconOnly") var iconOnly: Bool = false
+    @State private var accountID = 0
+    @State private var listID = UUID()
+
+    func redraw(_ newAccount: Int) -> Void {
+        if newAccount != accountID {
+            accountID = newAccount
+            withAnimation { listID = UUID() }
+        }
+    }
 
     var body: some View {
-        if let paytoUris = details.exchangePaytoUris {
-            let payto = paytoUris[0]
+        if let accountDetails = details.exchangeCreditAccountDetails {
+            if !iconOnly {
+                Text("The Exchange is waiting for your wire-transfer.")
+                    .multilineTextAlignment(.leading)
+                    .listRowSeparator(.hidden)
+            }
+            AccountPicker(title: String(localized: "Bank"), value: accountID,
+                          accountDetails: accountDetails, action: redraw)
+            let account = accountDetails[accountID]
+            let payto = account.paytoUri
             let payURL = URL(string: payto)
             let iban = payURL?.iban ?? "unknown IBAN"
-            let amountStr = common.amountRaw.readableDescription        // 
TODO: formatter
+            let amountStr = account.transferAmount.readableDescription        
// TODO: formatter?
+            let obtainStr = common.amountRaw.readableDescription
+
+            let cryptocode = HStack {
+                Text(details.reservePub)
+                    .monospacedDigit()
+                    .accessibilityLabel("Cryptocode")
+                Spacer()
+                CopyButton(textToCopy: details.reservePub, vertical: true)
+                    .accessibilityLabel("Copy the cryptocode")
+                    .disabled(false)
+            }   .padding(.leading)
+            let ibanCode = HStack {
+                Text(iban)
+                    .monospacedDigit()
+                    .accessibilityLabel("IBAN of the exchange")
+                Spacer()
+                CopyButton(textToCopy: iban, vertical: true)
+                    .accessibilityLabel("Copy the IBAN")
+                    .disabled(false)
+            }   .padding(.leading)
+                .padding(.top, -8)
+
+            let step1 = Text(iconOnly ? "**Step 1:** Copy+Paste this subject:"
+                                      : "**Step 1:** Copy this code and paste 
it into the subject/purpose field in your banking app or bank website:")
+                            .multilineTextAlignment(.leading)
+            let mandatory = Text("This is mandatory, otherwise your money will 
not arrive in this wallet.")
+                                .bold()
+                                .multilineTextAlignment(.leading)
+                                .listRowSeparator(.hidden)
+            let step2 = Text(iconOnly ? "**Step 2:** Copy+Paste this IBAN:"
+                                      : "**Step 2:** If you don't already have 
it in your banking favourites list, then copy and paste this IBAN into the 
receiver IBAN field in your banking app or website (and save it as favourite 
for the next time):")
+                            .multilineTextAlignment(.leading)
+                            .padding(.top)
+            let step3 = Text(iconOnly ? "**Step 3:** Transfer \(amountStr)."
+                                      : "**Step 3:** Finish the wire transfer 
of \(amountStr) in your banking app or website, then this withdrawal will 
proceed automatically.")
+                            .multilineTextAlignment(.leading)
+                            .padding(.top)
             Group {
-                Text(iconOnly ? "Transfer \(amountStr) to the Exchange."
-                              : "You need to transfer \(amountStr) from your 
regular bank account to the Exchange.")
-                Text(iconOnly ? "**Step 1:** Copy+Paste this subject:"
-                              : "**Step 1:** Copy this code and paste it into 
the subject/purpose field in your banking app or bank website:")
-                        .multilineTextAlignment(.leading)
-                        .listRowSeparator(.hidden)
+                TransferRestrictionsV(amountStr: amountStr,
+                                      obtainStr: obtainStr,
+                                   restrictions: account.creditRestrictions)
+                    .listRowSeparator(.visible)
+                step1.listRowSeparator(.hidden)
                 if !iconOnly {
-                    Text("This is mandatory, otherwise your money will not 
arrive in this wallet.")
-                        .bold()
-                        .multilineTextAlignment(.leading)
-                        .listRowSeparator(.hidden)
+                    mandatory
                 }
-                HStack {
-                    Text(details.reservePub)
-                        .monospacedDigit()
-                        .accessibilityLabel("Cryptocode")
-                    Spacer()
-                    CopyButton(textToCopy: details.reservePub, vertical: true)
-                        .accessibilityLabel("Copy the cryptocode")
-                        .disabled(false)
-                }   .padding(.leading)
-                    .listRowSeparator(.hidden)
-                Text(iconOnly ? "**Step 2:** Copy+Paste this IBAN:"
-                              : "**Step 2:** If you don't already have it in 
your banking favourites list, then copy and paste this IBAN into the receiver 
IBAN field in your banking app or website (and save it as favourite for the 
next time):")
-                    .multilineTextAlignment(.leading)
-                    .padding(.top)
-                    .listRowSeparator(.hidden)
-                HStack {
-                    Text(iban)
-                        .monospacedDigit()
-                        .accessibilityLabel("IBAN of the exchange")
-                    Spacer()
-                    CopyButton(textToCopy: iban, vertical: true)
-                        .accessibilityLabel("Copy the IBAN")
-                        .disabled(false)
-                }   .padding(.leading)
-                    .padding(.top, -8)
-                    .listRowSeparator(.hidden)
-                Text(iconOnly ? "**Step 3:** Transfer \(amountStr)."
-                              : "**Step 3:** Finish the wire transfer of 
\(amountStr) in your banking app or website, then this withdrawal will proceed 
automatically.")
-                    .multilineTextAlignment(.leading)
-                    .padding(.top)
-                    .listRowSeparator(.visible)
+                cryptocode.listRowSeparator(.hidden)
+                step2.listRowSeparator(.hidden)
+                ibanCode.listRowSeparator(.hidden)
+                step3.listRowSeparator(.visible)
                 Text(iconOnly ? "**Alternative:** Use this PayTo-Link:"
                               : "**Alternative:** If your bank already 
supports PayTo, you can use this PayTo-Link instead:")
                     .multilineTextAlignment(.leading)
@@ -76,8 +163,10 @@ struct ManualDetailsV: View {
                         .disabled(false)
                     Spacer()
                 }   .listRowSeparator(.automatic)
-            }
+            }.id(listID)
                 .accessibilityFont(.body)
+        } else {
+            // YIKES No exchangeCreditAccountDetails
         }
     }
 }
@@ -92,8 +181,11 @@ struct ManualDetails_Previews: PreviewProvider {
                               transactionId: "someTxID",
                                   timestamp: Timestamp(from: 
1_666_666_000_000),
                                   txActions: [])
-        let details = WithdrawalDetails(type: .manual, reservePub: 
"ReSeRvEpUbLiC_KeY_FoR_WiThDrAwAl", reserveIsReady: false,
-                                        
exchangePaytoUris:["payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company"])
+        let payto = 
"payto://iban/SANDBOXX/DE159593?receiver-name=Exchange+Company"
+        let details = WithdrawalDetails(type: .manual, 
+                                  reservePub: 
"ReSeRvEpUbLiC_KeY_FoR_WiThDrAwAl",
+                              reserveIsReady: false,
+                                   confirmed: false)
         List {
             ManualDetailsV(common: common, details: details)
         }
diff --git a/TalerWallet1/Views/Transactions/ThreeAmountsV.swift 
b/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
index 0ff8f06..7442714 100644
--- a/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
+++ b/TalerWallet1/Views/Transactions/ThreeAmountsV.swift
@@ -13,6 +13,7 @@ struct ThreeAmountsSheet: View {
     var bottomAbbrev: String?
     let baseURL: String?
     let large: Bool               // set to false for QR or IBAN
+    let summary: String?
 
     var body: some View {
         let raw = common.amountRaw
@@ -35,7 +36,8 @@ struct ThreeAmountsSheet: View {
                   bottomAmount: incomplete ? nil : effective,
                          large: large, pending: pending, incoming: incoming,
                        baseURL: baseURL,
-                        status: common.txState.major.localizedState)
+                        status: common.txState.major.localizedState,
+                       summary: summary)
     }
 }
 // MARK: -
@@ -51,7 +53,8 @@ struct ThreeAmountsV: View {
     let pending: Bool
     let incoming: Bool
     let baseURL: String?
-    var status: String?
+    let status: String?
+    let summary: String?
 
     @AppStorage("iconOnly") var iconOnly: Bool = false
     
@@ -60,24 +63,30 @@ struct ThreeAmountsV: View {
         let foreColor = pending ? WalletColors().pendingColor(incoming)
                                 : WalletColors().transactionColor(incoming)
         Section {
-            AmountView(title: iconOnly ? topAbbrev : topTitle,
-                       value: topAmount.readableDescription,
+            if let summary {
+                Text(summary)
+                    .accessibilityFont(.title2)
+                    .lineLimit(4)
+                    .padding(.bottom)
+            }
+            AmountRowV(title: iconOnly ? topAbbrev : topTitle,
+                      amount: topAmount,
                        color: labelColor,
                        large: large)
                 .padding(.bottom, 4)
                 .accessibilityElement(children: .combine)
             if let fee {
-                AmountView(title: iconOnly ? String(localized: "Fee:")
+                AmountRowV(title: iconOnly ? String(localized: "Fee:")
                                            : String(localized: "Exchange 
fee:"),
-                           value: fee.readableDescription,
+                          amount: fee,
                            color: labelColor,
                            large: false)
                 .padding(.bottom, 4)
                 .accessibilityElement(children: .combine)
             }
             if let bottomAmount {
-                AmountView(title: iconOnly ? bottomAbbrev : bottomTitle,
-                           value: bottomAmount.readableDescription,
+                AmountRowV(title: iconOnly ? bottomAbbrev : bottomTitle,
+                          amount: bottomAmount,
                            color: foreColor,
                            large: large)
                 .accessibilityElement(children: .combine)
@@ -122,7 +131,8 @@ struct ThreeAmounts_Previews: PreviewProvider {
         Group {
             List {
                 ThreeAmountsSheet(common: common, topAbbrev: "Withdrawal",
-                                topTitle: "Withdrawal", baseURL: DEMOEXCHANGE, 
large: 1==0)
+                                topTitle: "Withdrawal", baseURL: DEMOEXCHANGE,
+                                   large: 1==0, summary: nil)
                     .safeAreaInset(edge: .bottom) {
                         Button(String(localized: "Accept"), action: {})
                             .buttonStyle(TalerButtonStyle(type: .prominent))
diff --git a/TalerWallet1/Views/Transactions/TransactionDetailView.swift 
b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
index 4e68137..162cfb7 100644
--- a/TalerWallet1/Views/Transactions/TransactionDetailView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionDetailView.swift
@@ -27,7 +27,7 @@ struct TransactionDetailView: View {
     let transactionId: String
     let reloadAction: ((_ transactionId: String) async throws -> Transaction)
     let navTitle: String?
-    let doneAction: (() -> Void)?
+    let doneAction: ((_ stack: CallStack) -> Void)?
     let abortAction: ((_ transactionId: String) async throws -> Void)?
     let deleteAction: ((_ transactionId: String) async throws -> Void)?
     let failAction: ((_ transactionId: String) async throws -> Void)?
@@ -45,15 +45,6 @@ struct TransactionDetailView: View {
     @State var transaction: Transaction = Transaction(dummyCurrency: 
DEMOCURRENCY)
     @State var viewId = UUID()
 
-    func accessibilityDate(_ date: Date?) -> String? {
-        if let date {
-            let formatted = date.formatted(date: .long, time: .shortened)
-//            print(formatted)
-            return formatted
-        }
-        return nil
-    }
-
     func loadTransaction() async {
         do {
             let reloadedTransaction = try await reloadAction(transactionId)
@@ -71,7 +62,7 @@ struct TransactionDetailView: View {
             if let transition = notification.userInfo?[TRANSACTIONTRANSITION] 
as? TransactionTransition {
                 if transition.transactionId == 
transaction.common.transactionId {       // is the transition for THIS 
transaction?
                     symLog.log(logStr)
-                    doneAction()        // if this view is in a sheet then 
dissmiss the sheet
+                    doneAction(stack.push())        // if this view is in a 
sheet then dissmiss the sheet
                     return true
                 }
             }
@@ -99,7 +90,7 @@ struct TransactionDetailView: View {
     }
 
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -107,18 +98,18 @@ struct TransactionDetailView: View {
         let pending = transaction.isPending
         let locale = TalerDater.shared.locale
         let (dateString, date) = TalerDater.dateString(from: common.timestamp)
-        let accessibilityDate = accessibilityDate(date) ?? dateString
+        let accessibilityDate = TalerDater.accessibilityDate(date) ?? 
dateString
         let navTitle2 = transaction.localizedType
         VStack {
             List {
                 if developerMode {
                     if transaction.isSuspendable { if let suspendAction {
                         TransactionButton(transactionId: common.transactionId,
-                                          command: .suspend, action: 
suspendAction)
+                                          command: .suspend, warning: nil, 
action: suspendAction)
                     } }
                     if transaction.isResumable { if let resumeAction {
                         TransactionButton(transactionId: common.transactionId,
-                                          command: .resume, action: 
resumeAction)
+                                          command: .resume, warning: nil, 
action: resumeAction)
                     } }
                 } // Suspend + Resume buttons
                 Text(dateString)
@@ -126,36 +117,48 @@ struct TransactionDetailView: View {
                     .accessibilityLabel(accessibilityDate)
                     .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
                     .listRowSeparator(.hidden)
-                HStack {
-                    Text(verbatim: "|")   // only reason for this 
leading-aligned text is to get a nice full length listRowSeparator
-                        .accessibilityHidden(true)
-                        .foregroundColor(Color.clear)
-                    Spacer()
-                    Text("Status: \(common.txState.major.localizedState)")
+                VStack(alignment: .trailing) {
+                    let majorState = common.txState.major.localizedState
+                    let minorState = common.txState.minor?.localizedState ?? 
nil
+                    let state = transaction.isPending ? minorState ?? 
majorState
+                                                      : majorState
+                    HStack {
+                        Text(verbatim: "|")   // only reason for this 
leading-aligned text is to get a nice full length listRowSeparator
+                            .accessibilityHidden(true)
+                            .foregroundColor(Color.clear)
+                        Spacer()
+                        Text("Status: \(state)")
+                            .multilineTextAlignment(.trailing)
+                    }
                 }    .listRowSeparator(.automatic)
                     .accessibilityFont(.title)
                 TypeDetail(transaction: $transaction, hasDone: doneAction != 
nil)
 
+//                if transaction.isRetryable { if let retryAction {
+//                    TransactionButton(transactionId: common.transactionId, 
command: .retry,
+//                                      warning: nil, action: abortAction)
+//                } } // Retry button
                 if transaction.isAbortable { if let abortAction {
-                    TransactionButton(transactionId: common.transactionId,
-                                      command: .abort, action: abortAction)
+                    TransactionButton(transactionId: common.transactionId, 
command: .abort,
+                                      warning: String(localized: "Are you sure 
you want to abort this transaction?"),
+                                      action: abortAction)
                 } } // Abort button
                 if transaction.isFailable { if let failAction {
-                    TransactionButton(transactionId: common.transactionId,
-                                      command: .fail, action: failAction)
+                    TransactionButton(transactionId: common.transactionId, 
command: .fail,
+                                      warning: String(localized: "Are you sure 
you want to fail this transaction?"),
+                                      action: failAction)
                 } } // Fail button
                 if transaction.isDeleteable { if let deleteAction {
-                    TransactionButton(transactionId: common.transactionId,
-                                      command: .delete, action: deleteAction)
+                    TransactionButton(transactionId: common.transactionId, 
command: .delete,
+                                      warning: String(localized: "Are you sure 
you want to delete this transaction?"),
+                                      action: deleteAction)
                 } } // Delete button
                 if let doneAction {
-                    Button(transaction.shouldConfirm ? "Confirm later" : 
"Done", action: doneAction)
+                    Button(transaction.shouldConfirm ? "Confirm later" : 
"Done", action: { doneAction(stack.push()) } )
                         .buttonStyle(TalerButtonStyle(type: 
transaction.shouldConfirm ? .bordered : .prominent))
                 } // Done button
             }.id(viewId)    // change viewId to enforce a draw update
             .listStyle(myListStyle.style).anyView
-//            .safeAreaInset(edge: .bottom) {
-//            }
         } // Group
         .onNotification(.TransactionExpired) { notification in
             // TODO: Alert user that this tx just expired
@@ -217,12 +220,12 @@ struct TransactionDetailView: View {
         var body: some View {
             VStack(alignment: .leading) {  // Show Hint that User should 
Confirm on bank website
                 if !iconOnly {
-                    Text("Waiting for bank confirmation")
-                        .fixedSize(horizontal: false, vertical: true)       // 
wrap in scrollview
+                    Text("The bank is waiting for your confirmation.")
+//                        .fixedSize(horizontal: false, vertical: true)       
// wrap in scrollview
                         .multilineTextAlignment(.leading)                   // 
otherwise
                         .listRowSeparator(.hidden)
                 }
-                Link("Confirm with bank", destination: destination)
+                Link("Confirm now", destination: destination)
                     .buttonStyle(TalerButtonStyle(type: .prominent, badge: 
CONFIRM_BANK))
                     .accessibilityHint("Will go to bank website to confirm 
this withdrawal.")
             }
@@ -239,10 +242,10 @@ struct TransactionDetailView: View {
             Group {
                 switch transaction {
                     case .dummy(_):
-                        let title = ""
+                        let title = EMPTYSTRING
                         Text(title)
                             .accessibilityFont(.body)
-                    case .withdrawal(let withdrawalTransaction):
+                    case .withdrawal(let withdrawalTransaction): Group {
                         let details = withdrawalTransaction.details
                         if pending {
                             if transaction.isPendingKYC {
@@ -257,50 +260,57 @@ struct TransactionDetailView: View {
                                 case .manual:               // "Make a wire 
transfer of \(amount) to"
                                     ManualDetailsV(common: common, details: 
withdrawalDetails)
 
-                                case .bankIntegrated:       // "Confirm with 
bank"
-                                    if !transaction.isPendingKYC {             
 // both should never happen, but...
+                                case .bankIntegrated:       // "Confirm now" 
(with bank)
+                                    if !transaction.isPendingKYC {             
 // cannot confirm if KYC is needed first
                                         let confirmed = 
withdrawalDetails.confirmed ?? false
                                         if !confirmed {
                                             if let confirmationUrl = 
withdrawalDetails.bankConfirmationUrl {
                                                 if let destination = 
URL(string: confirmationUrl) {
                                                     
ConfirmationButton(destination: destination)
-                                                }
-                                            }
-                                        }
-                                    }
+                                    }   }   }   }
                             } // switch
-                        } // ManualDetails or Confirm with bank
+                        } // ManualDetails or Confirm now (with bank)
                         ThreeAmountsSheet(common: common, topAbbrev: 
String(localized: "Chosen:"),
                                         topTitle: String(localized: "Chosen 
amount to withdraw:"),
-                                         baseURL: details.exchangeBaseUrl, 
large: false)
-                    case .payment(let paymentTransaction):
+                                         baseURL: details.exchangeBaseUrl, 
large: false, summary: nil)
+                    }
+                    case .payment(let paymentTransaction): Group {
                         let details = paymentTransaction.details
                         Text(details.info.summary)
                             .accessibilityFont(.title3)
                         ThreeAmountsSheet(common: common, topAbbrev: 
String(localized: "Pay:"),
                                         topTitle: String(localized: "Sum to be 
paid:"),
-                                         baseURL: nil, large: true)     // 
TODO: baseURL
-                    case .refund(let refundTransaction):
+                                          baseURL: nil, large: true, summary: 
details.info.summary)     // TODO: baseURL
+                    }
+                    case .refund(let refundTransaction): Group {
                         let details = refundTransaction.details                
 // TODO: more details
                         ThreeAmountsSheet(common: common, topAbbrev: 
String(localized: "Refunded:"),
                                         topTitle: String(localized: "Refunded 
amount:"),
-                                         baseURL: nil, large: true)     // 
TODO: baseURL
-                    case .reward(let rewardTransaction):
-                        let details = rewardTransaction.details                
 // TODO: more details
+                                         baseURL: nil, large: true, summary: 
nil)     // TODO: baseURL, summary
+                    }
+                    case .reward(let rewardTransaction): Group {
+                        let details = rewardTransaction.details
                         ThreeAmountsSheet(common: common, topAbbrev: 
String(localized: "Reward:"),
                                         topTitle: String(localized: "Received 
Reward:"),
-                                         baseURL: details.exchangeBaseUrl, 
large: true)
-                    case .refresh(let refreshTransaction):
+                                         baseURL: details.exchangeBaseUrl, 
large: true, summary: nil)       // TODO: summary
+                    }
+                    case .refresh(let refreshTransaction): Group {
                         let details = refreshTransaction.details               
 // TODO: details
+                        Text(details.refreshReason.rawValue)
                         ThreeAmountsSheet(common: common, topAbbrev: 
String(localized: "Refreshed:"),
                                         topTitle: String(localized: "Refreshed 
amount:"),
-                                         baseURL: nil, large: true)     // 
TODO: baseURL
-                    case .peer2peer(let p2pTransaction):
-                        let details = p2pTransaction.details                   
 // TODO: details
-                        Text(details.info.summary)
-                            .accessibilityFont(.title2)
-                            .lineLimit(4)
-                            .padding(.bottom)
+                                         baseURL: nil, large: true, summary: 
nil)     // TODO: baseURL
+                    }
+                    case .peer2peer(let p2pTransaction): Group {
+                        let details = p2pTransaction.details
+                        let expiration = details.info.expiration
+                        let (dateString, date) = TalerDater.dateString(from: 
expiration)
+                        let accessibilityDate = 
TalerDater.accessibilityDate(date) ?? dateString
+                        let accessibilityLabel = String(localized: "Expires: 
\(accessibilityDate)")
+                        Text("Expires: \(dateString)")
+                            .accessibilityFont(.body)
+                            .accessibilityLabel(accessibilityLabel)
+//                        .foregroundColor(colorSchemeContrast == .increased ? 
.primary : .secondary)
                         // TODO: isSendCoins should show QR only while not yet 
expired  - either set timer or wallet-core should do so and send a 
state-changed notification
                         if pending {
                             if transaction.isPendingReady {
@@ -320,7 +330,9 @@ struct TransactionDetailView: View {
                         let colon = ":"
                         ThreeAmountsSheet(common: common, topAbbrev: 
transaction.localizedType + colon,
                                         topTitle: transaction.localizedType + 
colon,
-                                         baseURL: details.exchangeBaseUrl, 
large: false)
+                                         baseURL: details.exchangeBaseUrl, 
large: false,
+                                         summary: details.info.summary)
+                    } // p2p
                 } // switch
             } // Group
         }
diff --git a/TalerWallet1/Views/Transactions/TransactionRowView.swift 
b/TalerWallet1/Views/Transactions/TransactionRowView.swift
index aafc403..1b80bc2 100644
--- a/TalerWallet1/Views/Transactions/TransactionRowView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionRowView.swift
@@ -35,74 +35,13 @@ struct IconBadge: View {
         }.accessibilityHidden(true)
     }
 }
-struct TransactionRowContentV: View {
-    var centerTop: String
-    var centerBottom: String
-    let isHorizontal: Bool
-    let pending: Bool
-    let incoming: Bool
-    let shouldConfirm: Bool
-    let needsKYC: Bool
-    let done: Bool
-    let foreColor:Color
-
-    @Environment(\.colorSchemeContrast) private var colorSchemeContrast
-
-    public static func width(titles: (String, String?), isHorizontal: Bool,
-                             sizeCategory: ContentSizeCategory) -> CGFloat {
-        let imageFont = TalerFont.uiFont(.largeTitle)
-        let uiFont1 = TalerFont.uiFont(.headline)
-        let uiFont2 = TalerFont.uiFont(.callout)
-
-        let image = "++"
-        let imageWidth = image.widthOfString(usingUIFont: imageFont, 
sizeCategory) + 8.0     // spacing: 8
-        let (title1, title2) = titles
-        let title1Width = title1.widthOfString(usingUIFont: uiFont1, 
sizeCategory)
-        var title2Width = 0.0
-        var totalWidth = title1Width
-        if let title2 {
-            title2Width = title2.widthOfString(usingUIFont: uiFont2, 
sizeCategory)
-            let blankStr = " "
-            totalWidth += blankStr.widthOfString(usingUIFont: uiFont1, 
sizeCategory) + title2Width
-        }
-
-//    let logStr = String(format: "image: %.2f   title: %.2f   total: %.2f", 
imageWidth, max(title1Width, title2Width), totalWidth)
-//    print(logStr)
-        return imageWidth + (isHorizontal ? totalWidth
-                                          : max(title1Width, title2Width))
-    }
-
-    var body: some View {
-        let iconBadge = IconBadge(foreColor: foreColor, done: done, incoming: 
incoming,
-                              shouldConfirm: shouldConfirm, needsKYC: needsKYC)
-        let doneOrPending = done || pending
-        let increasedContrast = colorSchemeContrast == .increased
-        let textColor = doneOrPending ? .primary :
-                    increasedContrast ? .secondary : WalletColors().gray3
-        HStack(spacing: 8) {
-            iconBadge
-            VStack(alignment: .leading) {
-                Text(centerTop)
-                    .foregroundColor(textColor)
-                    .strikethrough(!doneOrPending, color: .red)
-                    .accessibilityFont(.headline)
-//                  .fontWeight(.medium)      iOS 16
-                    .padding(.bottom, -2.0)
-                    .accessibilityLabel(doneOrPending ? centerTop : centerTop 
+  ", canceled")
-                Text(centerBottom)
-                    .foregroundColor(textColor)
-                    .accessibilityFont(.callout)
-            }
-        }
-    }
-}
 
 struct TransactionRowView: View {
     let transaction : Transaction
     let currency: String
 
     @Environment(\.sizeCategory) var sizeCategory
-    @EnvironmentObject private var controller: Controller
+    @Environment(\.colorSchemeContrast) private var colorSchemeContrast
 
     func needVStack(available: CGFloat, contentWidth: CGFloat, valueWidth: 
CGFloat) -> Bool {
         available < (contentWidth + valueWidth + 40)
@@ -110,11 +49,12 @@ struct TransactionRowView: View {
 
     var body: some View {
         let common = transaction.common
-        let amount = common.amountEffective
         let pending = transaction.isPending
         let needsKYC = transaction.isPendingKYC
         let shouldConfirm = transaction.shouldConfirm
         let done = transaction.isDone
+        let doneOrPending = done || pending
+        let increasedContrast = colorSchemeContrast == .increased
         let details = transaction.detailsToShow()
         let keys = details.keys
 
@@ -122,32 +62,91 @@ struct TransactionRowView: View {
         let incoming = common.incoming()
         let foreColor = pending ? WalletColors().pendingColor(incoming)
                       : done ? WalletColors().transactionColor(incoming)
-                             : WalletColors().incompleteColor
-        let currencyInfo = controller.info(for: currency, 
controller.currencyTicker)
-        SingleAxisGeometryReader { width in
-            Group {
-                let amountStr = amount.string(currencyInfo)
-                let amountWidth = amountStr.width(largeAmountFont: false, 
sizeCategory)
+                             : WalletColors().uncompletedColor
+        let textColor = doneOrPending ? .primary :
+                    increasedContrast ? .secondary : Color(.tertiaryLabel)
 
-                let contentWidth = TransactionRowContentV.width(titles: 
(transaction.localizedType, dateString),
-                                                          isHorizontal: false, 
sizeCategory: sizeCategory)
-                let needVStack = needVStack(available: width, contentWidth: 
contentWidth, valueWidth: amountWidth)
+        let iconBadge = IconBadge(foreColor: foreColor, done: done, incoming: 
incoming,
+                                  shouldConfirm: shouldConfirm, needsKYC: 
needsKYC)
+        let amountV = AmountV(common.amountEffective)
+            .foregroundColor(foreColor)
 
-                AmountRowV(amountStr: amountStr, amountColor: foreColor, 
doneOrPending: done || pending,
-                     largeAmountFont: false, fitsHorizontal: !needVStack, 
vertAlignment: .center) {
+        let topString = transaction.localizedType
+        let centerTop = Text(topString)
+            .foregroundColor(textColor)
+            .strikethrough(!doneOrPending, color: .red)
+            .accessibilityFont(.headline)
+//            .fontWeight(.medium)      iOS 16
+            .padding(.bottom, -2.0)
+            .accessibilityLabel(doneOrPending ? topString : topString +  ", 
canceled")
+        let centerBottom = Text(dateString)
+            .foregroundColor(textColor)
+            .accessibilityFont(.callout)
 
-                    TransactionRowContentV(centerTop: 
transaction.localizedType,
-                                        centerBottom: dateString, 
isHorizontal: true,
-                                             pending: pending, incoming: 
incoming,
-                                       shouldConfirm: shouldConfirm, needsKYC: 
needsKYC,
-                                                done: done, foreColor: 
foreColor)
+        let layout1 = HStack(spacing: 0) {
+            HStack(spacing: -4) {
+                iconBadge
+                Spacer(minLength: 0)
+                VStack(alignment: .leading) {
+                    centerTop
+                    centerBottom
                 }
-                    .accessibilityElement(children: .combine)
-                    .accessibilityValue(needsKYC ? ". Needs K Y C" :
-                                   shouldConfirm ? ". Needs bank confirmation" 
: EMPTYSTRING)
-                    .accessibilityHint("Will go to detail view.")
+                Spacer(minLength: 2)
             }
+            amountV
+        }
+
+        let layout2 = VStack(spacing: 0) {
+            centerTop
+            HStack(spacing: 8) {
+                HStack(spacing: 0) {
+                    iconBadge//.border(.blue)
+                    Spacer(minLength: 0)
+                    centerBottom
+                    Spacer(minLength: 2)
+                }//.border(.green)
+                amountV//.border(.red)
+            }//.border(.orange)
+        }
+
+        let layout3 = VStack(spacing: 0) {
+            HStack(spacing: 8) {
+                HStack(spacing: 0) {
+                    iconBadge
+                    Spacer(minLength: 0)
+                    centerTop
+                    Spacer(minLength: 2)
+                }
+                amountV
+            }
+            centerBottom
+        }
+
+        let layout4 = VStack(spacing: 0) {
+            centerTop
+            HStack(spacing: -4) {
+                iconBadge//.border(.green)
+                Spacer(minLength: 2
+                )
+                amountV//.border(.green)
+            }//.border(.orange)
+            centerBottom
+        }
+
+        Group {
+            if #available(iOS 16.0, *) {
+                ViewThatFits(in: .horizontal) {
+                    layout1//.border(.green)
+                    layout2
+                    layout3//.border(.red)
+                    layout4//.border(.blue)
+                }
+            } else { layout4 } // view for iOS 15
         }
+            .accessibilityElement(children: .combine)
+            .accessibilityValue(needsKYC ? ". Needs K Y C" :
+                           shouldConfirm ? ". Needs bank confirmation" : 
EMPTYSTRING)
+            .accessibilityHint("Will go to detail view.")
     }
 }
 // MARK: -
@@ -187,7 +186,7 @@ extension Transaction {             // for PreViews
                                                                     : 
WithdrawalDetails.WithdrawalType.bankIntegrated,
                                                 reservePub: 
"PuBlIc_KeY_oF_tHe_ReSeRvE",
                                             reserveIsReady: false,
-                                         exchangePaytoUris: pending ? [payto] 
: nil)
+                                                 confirmed: false)
             let wDetails = WithdrawalTransactionDetails(exchangeBaseUrl: 
DEMOEXCHANGE,
                                                       withdrawalDetails: 
withdrawalDetails)
             self = .withdrawal(WithdrawalTransaction(common: common, details: 
wDetails))
diff --git a/TalerWallet1/Views/Transactions/TransactionsListView.swift 
b/TalerWallet1/Views/Transactions/TransactionsListView.swift
index cce00e6..7614fe5 100644
--- a/TalerWallet1/Views/Transactions/TransactionsListView.swift
+++ b/TalerWallet1/Views/Transactions/TransactionsListView.swift
@@ -21,7 +21,7 @@ struct TransactionsListView: View {
     @State private var upAction: () -> Void = {}
     @State private var downAction: () -> Void = {}
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
@@ -87,7 +87,7 @@ struct TransactionsArraySliceV: View {
 
     @EnvironmentObject private var model: WalletModel
     var body: some View {
-#if DEBUG
+#if PRINT_CHANGES
         let _ = Self._printChanges()
         let _ = symLog?.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
diff --git a/Taler_Wallet Info.plist b/Taler_Wallet Info.plist
index 811f070..11d4a04 100644
--- a/Taler_Wallet Info.plist   
+++ b/Taler_Wallet Info.plist   
@@ -18,7 +18,6 @@
                                <string>taler</string>
                                <string>ext+taler</string>
                                <string>web+taler</string>
-                               <string>payto</string>
                        </array>
                </dict>
        </array>
diff --git a/TestFlight/WhatToTest.en-US.txt b/TestFlight/WhatToTest.en-US.txt
index 82745ef..1b2e711 100644
--- a/TestFlight/WhatToTest.en-US.txt
+++ b/TestFlight/WhatToTest.en-US.txt
@@ -1,3 +1,37 @@
+Version 0.9.4 (1)
+
+- now accepts uppercase TALER://PAY URIs
+
+
+Version 0.9.4 (0)
+
+- bug fixes
+- first release of 0.9.4
+
+
+Version 0.9.3 (34)
+
+• New feature: Demo Bank Website button in Banking
+- Balances is shown after a transaction finished
+
+
+Version 0.9.3 (33)
+
+• New feature: ToS with Markdown
+- background colors adapted for WCAG AA
+
+
+Version 0.9.3 (32)
+
+• New feature: Currency Conversion
+• New feature: ToS with multiple languages
+• New feature: Landscape
+• New feature: Warnings for Delete, Fail & Abort
+
+- Bugfix: Manual withdrawals no longer create multiple identical transactions
+- "Banking" instead of "Exchanges" - withdraw and deposit
+- Haptic feedback for Copy/Share buttons
+
 
 Version 0.9.3 (31)
 
diff --git a/taler-swift/Sources/taler-swift/Amount.swift 
b/taler-swift/Sources/taler-swift/Amount.swift
index 301f555..9866301 100644
--- a/taler-swift/Sources/taler-swift/Amount.swift
+++ b/taler-swift/Sources/taler-swift/Amount.swift
@@ -66,6 +66,7 @@ public final class Amount: Codable, Hashable, @unchecked 
Sendable, CustomStringC
 
     public static let decimalSeparator = "."
 
+    /// 3-char ISO 4217 code for global currency. Regional MUST be >= 4 letters
     /// The currency of the amount. Cannot be changed later, except...
     private var currency: String
 
@@ -250,7 +251,7 @@ public final class Amount: Codable, Hashable, @unchecked 
Sendable, CustomStringC
     public init(currency: String, cent: UInt64) {
         self.currency = currency
         self.integer = cent / 100   // For existing currencies, fractional 
digits could be 0, 2 or 3
-        self.fraction = UInt32(cent - (self.integer * 100))
+        self.fraction = UInt32(cent - (self.integer * 100)) * 
(Self.fractionalBase() / 100)
     }
 
     /// Initializes an amount from a decoder.

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]