gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 01/02: pending operations WIP


From: gnunet
Subject: [taler-wallet-core] 01/02: pending operations WIP
Date: Mon, 02 Dec 2019 18:13:07 +0100

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

dold pushed a commit to branch master
in repository wallet-core.

commit b5ee6b7b4ee506712f51e1b90e9256c4b0c0c603
Author: Florian Dold <address@hidden>
AuthorDate: Mon Dec 2 17:35:47 2019 +0100

    pending operations WIP
---
 src/android/index.ts            |  26 +++++--
 src/crypto/cryptoApi.ts         |   5 +-
 src/i18n/de.po                  | 106 ++++++++++++++---------------
 src/i18n/en-US.po               | 106 ++++++++++++++---------------
 src/i18n/fr.po                  | 106 ++++++++++++++---------------
 src/i18n/it.po                  | 106 ++++++++++++++---------------
 src/i18n/strings.ts             | 146 ++++++++++++++++++++--------------------
 src/i18n/sv.po                  | 106 ++++++++++++++---------------
 src/i18n/taler-wallet-webex.pot | 106 ++++++++++++++---------------
 src/util/asyncMemo.ts           |   4 +-
 src/util/promiseUtils.ts        |  21 ++++++
 src/util/query.ts               |  19 +++++-
 src/util/timer.ts               |   8 +++
 src/wallet-impl/balance.ts      |   6 +-
 src/wallet-impl/pay.ts          |  11 ++-
 src/wallet-impl/pending.ts      |  44 ++++++++----
 src/wallet-impl/reserves.ts     |   1 +
 src/wallet-impl/state.ts        |   1 +
 src/wallet-impl/withdraw.ts     | 138 ++++++++++++++++++++-----------------
 src/wallet.ts                   |  86 +++++++++++++++--------
 src/walletTypes.ts              |   1 +
 src/webex/wxBackend.ts          |   3 +
 22 files changed, 643 insertions(+), 513 deletions(-)

diff --git a/src/android/index.ts b/src/android/index.ts
index 6a29f794..71144176 100644
--- a/src/android/index.ts
+++ b/src/android/index.ts
@@ -26,12 +26,10 @@ import {
 } from "../headless/helpers";
 import { openPromise, OpenedPromise } from "../util/promiseUtils";
 import fs = require("fs");
-import axios from "axios";
 import { HttpRequestLibrary, HttpResponse } from "../util/http";
-import querystring = require("querystring");
 
 // @ts-ignore: special built-in module
-import akono = require("akono");
+//import akono = require("akono");
 
 export class AndroidHttpLib implements HttpRequestLibrary {
   useNfcTunnel: boolean = false;
@@ -102,7 +100,7 @@ export class AndroidHttpLib implements HttpRequestLibrary {
 
 export function installAndroidWalletListener() {
   // @ts-ignore
-  const sendMessage: (m: string) => void = akono.sendMessage;
+  const sendMessage: (m: string) => void = globalThis.__akono_sendMessage;
   if (typeof sendMessage !== "function") {
     const errMsg =
       "FATAL: cannot install android wallet listener: akono functions missing";
@@ -137,8 +135,12 @@ export function installAndroidWalletListener() {
           persistentStoragePath: msg.args.persistentStoragePath,
           httpLib: httpLib,
         };
-        maybeWallet = await getDefaultNodeWallet(walletArgs);
-        wp.resolve(maybeWallet);
+        const w = await getDefaultNodeWallet(walletArgs);
+        maybeWallet = w;
+        w.runLoopScheduledRetries().catch((e) => {
+          console.error("Error during wallet retry loop", e);
+        });
+        wp.resolve(w);
         result = true;
         break;
       }
@@ -147,9 +149,19 @@ export function installAndroidWalletListener() {
         result = await wallet.getBalances();
         break;
       }
+      case "getPendingOperations": {
+        const wallet = await wp.promise;
+        result = await wallet.getPendingOperations();
+        break;
+      }
       case "withdrawTestkudos": {
         const wallet = await wp.promise;
-        result = await withdrawTestBalance(wallet);
+        await withdrawTestBalance(wallet);
+        break;
+      }
+      case "getHistory": {
+        const wallet = await wp.promise;
+        result = await wallet.getHistory();
         break;
       }
       case "preparePay": {
diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts
index 5ef78771..ee760007 100644
--- a/src/crypto/cryptoApi.ts
+++ b/src/crypto/cryptoApi.ts
@@ -28,7 +28,6 @@ import {
   CoinRecord,
   DenominationRecord,
   RefreshSessionRecord,
-  ReserveRecord,
   TipPlanchet,
   WireFee,
 } from "../dbTypes";
@@ -195,7 +194,7 @@ export class CryptoApi {
     };
     this.resetWorkerTimeout(ws);
     work.startTime = timer.performanceNow();
-    ws.w!.postMessage(msg);
+    setImmediate(() => ws.w!.postMessage(msg));
   }
 
   resetWorkerTimeout(ws: WorkerState) {
@@ -316,6 +315,7 @@ export class CryptoApi {
           throw Error("assertion failed");
         }
         this.workQueues[priority].push(workItem);
+        console.log("queueing crypto work");
         return;
       }
 
@@ -323,7 +323,6 @@ export class CryptoApi {
         if (ws.currentWorkItem !== null) {
           continue;
         }
-
         this.wake(ws, workItem);
         return;
       }
diff --git a/src/i18n/de.po b/src/i18n/de.po
index 0181420a..e559cc07 100644
--- a/src/i18n/de.po
+++ b/src/i18n/de.po
@@ -27,6 +27,26 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+#: src/util/wire.ts:38
+#, c-format
+msgid "Invalid Wire"
+msgstr ""
+
+#: src/util/wire.ts:43 src/util/wire.ts:46
+#, c-format
+msgid "Invalid Test Wire Detail"
+msgstr ""
+
+#: src/util/wire.ts:48
+#, c-format
+msgid "Test Wire Acct #%1$s on %2$s"
+msgstr ""
+
+#: src/util/wire.ts:50
+#, c-format
+msgid "Unknown Wire Detail"
+msgstr ""
+
 #: src/webex/pages/benchmark.tsx:57
 #, c-format
 msgid "Operation"
@@ -37,77 +57,77 @@ msgstr ""
 msgid "time (ms/op)"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:118
+#: src/webex/pages/pay.tsx:117
 #, fuzzy, c-format
 msgid "The merchant %1$s offers you to purchase:"
 msgstr "Der Händler %1$s möchte einen Vertrag über %2$s mit Ihnen abschließen."
 
-#: src/webex/pages/pay.tsx:124
+#: src/webex/pages/pay.tsx:123
 #, c-format
 msgid "The total price is %1$s (plus %2$s fees)."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:129
+#: src/webex/pages/pay.tsx:128
 #, c-format
 msgid "The total price is %1$s."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:149
+#: src/webex/pages/pay.tsx:148
 #, c-format
 msgid "Retry"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:158
+#: src/webex/pages/pay.tsx:157
 #, fuzzy, c-format
 msgid "Confirm payment"
 msgstr "Bezahlung bestätigen"
 
-#: src/webex/pages/popup.tsx:162
+#: src/webex/pages/popup.tsx:159
 #, c-format
 msgid "Balance"
 msgstr "Saldo"
 
-#: src/webex/pages/popup.tsx:163
+#: src/webex/pages/popup.tsx:160
 #, c-format
 msgid "History"
 msgstr "Verlauf"
 
-#: src/webex/pages/popup.tsx:164
+#: src/webex/pages/popup.tsx:161
 #, c-format
 msgid "Debug"
 msgstr "Debug"
 
-#: src/webex/pages/popup.tsx:200
+#: src/webex/pages/popup.tsx:197
 #, fuzzy, c-format
 msgid "You have no balance to show. Need some %1$s getting started?"
 msgstr "Sie haben kein Digitalgeld. Wollen Sie %1$s? abheben?"
 
-#: src/webex/pages/popup.tsx:263
+#: src/webex/pages/popup.tsx:260
 #, c-format
 msgid "%1$s incoming"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:275
+#: src/webex/pages/popup.tsx:272
 #, c-format
 msgid "%1$s being spent"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:306
+#: src/webex/pages/popup.tsx:303
 #, c-format
 msgid "Error: could not retrieve balance information."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:335
+#: src/webex/pages/popup.tsx:332
 #, fuzzy, c-format
 msgid "Bank requested reserve (%1$s) for %2$s."
 msgstr "Bank bestätig anlegen der Reserve (%1$s) bei %2$s"
 
-#: src/webex/pages/popup.tsx:344
+#: src/webex/pages/popup.tsx:341
 #, fuzzy, c-format
 msgid "Started to withdraw %1$s from %2$s (%3$s)."
 msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
 
-#: src/webex/pages/popup.tsx:353
+#: src/webex/pages/popup.tsx:350
 #, fuzzy, c-format
 msgid "Merchant %1$s offered contract %2$s."
 msgstr ""
@@ -115,17 +135,17 @@ msgstr ""
 "               möchte einen Vertrag über %2$s\n"
 "               mit Ihnen abschließen."
 
-#: src/webex/pages/popup.tsx:366
+#: src/webex/pages/popup.tsx:363
 #, fuzzy, c-format
 msgid "Withdrew %1$s from %2$s ( %3$s)."
 msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
 
-#: src/webex/pages/popup.tsx:381
+#: src/webex/pages/popup.tsx:378
 #, fuzzy, c-format
 msgid "Paid %1$s to merchant %2$s.%3$s( %4$s)"
 msgstr "Reserve (%1$s) mit %2$s bei %3$s erzeugt"
 
-#: src/webex/pages/popup.tsx:391
+#: src/webex/pages/popup.tsx:388
 #, fuzzy, c-format
 msgid "Merchant %1$s gave a refund over %2$s."
 msgstr ""
@@ -133,12 +153,12 @@ msgstr ""
 "               möchte einen Vertrag über %2$s\n"
 "               mit Ihnen abschließen."
 
-#: src/webex/pages/popup.tsx:404
+#: src/webex/pages/popup.tsx:400
 #, c-format
 msgid "tip"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:408
+#: src/webex/pages/popup.tsx:404
 #, fuzzy, c-format
 msgid "Merchant %1$s gave a %2$s of %3$s."
 msgstr ""
@@ -146,22 +166,22 @@ msgstr ""
 "               möchte einen Vertrag über %2$s\n"
 "               mit Ihnen abschließen."
 
-#: src/webex/pages/popup.tsx:414
+#: src/webex/pages/popup.tsx:410
 #, c-format
 msgid "You did not accept the tip yet."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:422
+#: src/webex/pages/popup.tsx:418
 #, c-format
 msgid "Unknown event (%1$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:464
+#: src/webex/pages/popup.tsx:460
 #, c-format
 msgid "Error: could not retrieve event history"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:489
+#: src/webex/pages/popup.tsx:485
 #, c-format
 msgid "Your wallet has no events recorded."
 msgstr "Ihre Geldbörse verzeichnet keine Vorkommnisse."
@@ -183,49 +203,49 @@ msgstr "Saldo"
 
 #. #-#-#-#-#  - (PACKAGE VERSION)  #-#-#-#-#
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/tip.tsx:118 src/webex/pages/withdraw.tsx:216
+#: src/webex/pages/tip.tsx:116 src/webex/pages/withdraw.tsx:214
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:73
+#: src/webex/pages/withdraw.tsx:72
 #, c-format
 msgid "Could not get details for withdraw operation:"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:89 src/webex/pages/withdraw.tsx:179
+#: src/webex/pages/withdraw.tsx:88 src/webex/pages/withdraw.tsx:178
 #, c-format
 msgid "Chose different exchange provider"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:108
+#: src/webex/pages/withdraw.tsx:107
 #, c-format
 msgid ""
 "Please select an exchange.  You can review the details before after your "
 "selection."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:120
+#: src/webex/pages/withdraw.tsx:119
 #, c-format
 msgid "Select %1$s"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:142
+#: src/webex/pages/withdraw.tsx:141
 #, c-format
 msgid "Select custom exchange"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:159
+#: src/webex/pages/withdraw.tsx:158
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:170
+#: src/webex/pages/withdraw.tsx:169
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:188
+#: src/webex/pages/withdraw.tsx:187
 #, c-format
 msgid "Cancel withdraw operation"
 msgstr ""
@@ -270,26 +290,6 @@ msgstr ""
 msgid "Deposit Fee"
 msgstr ""
 
-#: src/wire.ts:38
-#, c-format
-msgid "Invalid Wire"
-msgstr ""
-
-#: src/wire.ts:43 src/wire.ts:46
-#, c-format
-msgid "Invalid Test Wire Detail"
-msgstr ""
-
-#: src/wire.ts:48
-#, c-format
-msgid "Test Wire Acct #%1$s on %2$s"
-msgstr ""
-
-#: src/wire.ts:50
-#, c-format
-msgid "Unknown Wire Detail"
-msgstr ""
-
 #, fuzzy, c-format
 #~ msgid "Submitting payment"
 #~ msgstr "Bezahlung bestätigen"
diff --git a/src/i18n/en-US.po b/src/i18n/en-US.po
index fd4a9c98..a490b6dc 100644
--- a/src/i18n/en-US.po
+++ b/src/i18n/en-US.po
@@ -27,6 +27,26 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+#: src/util/wire.ts:38
+#, c-format
+msgid "Invalid Wire"
+msgstr ""
+
+#: src/util/wire.ts:43 src/util/wire.ts:46
+#, c-format
+msgid "Invalid Test Wire Detail"
+msgstr ""
+
+#: src/util/wire.ts:48
+#, c-format
+msgid "Test Wire Acct #%1$s on %2$s"
+msgstr ""
+
+#: src/util/wire.ts:50
+#, c-format
+msgid "Unknown Wire Detail"
+msgstr ""
+
 #: src/webex/pages/benchmark.tsx:57
 #, c-format
 msgid "Operation"
@@ -37,122 +57,122 @@ msgstr ""
 msgid "time (ms/op)"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:118
+#: src/webex/pages/pay.tsx:117
 #, c-format
 msgid "The merchant %1$s offers you to purchase:"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:124
+#: src/webex/pages/pay.tsx:123
 #, c-format
 msgid "The total price is %1$s (plus %2$s fees)."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:129
+#: src/webex/pages/pay.tsx:128
 #, c-format
 msgid "The total price is %1$s."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:149
+#: src/webex/pages/pay.tsx:148
 #, c-format
 msgid "Retry"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:158
+#: src/webex/pages/pay.tsx:157
 #, c-format
 msgid "Confirm payment"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:162
+#: src/webex/pages/popup.tsx:159
 #, c-format
 msgid "Balance"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:163
+#: src/webex/pages/popup.tsx:160
 #, c-format
 msgid "History"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:164
+#: src/webex/pages/popup.tsx:161
 #, c-format
 msgid "Debug"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:200
+#: src/webex/pages/popup.tsx:197
 #, c-format
 msgid "You have no balance to show. Need some %1$s getting started?"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:263
+#: src/webex/pages/popup.tsx:260
 #, c-format
 msgid "%1$s incoming"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:275
+#: src/webex/pages/popup.tsx:272
 #, c-format
 msgid "%1$s being spent"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:306
+#: src/webex/pages/popup.tsx:303
 #, c-format
 msgid "Error: could not retrieve balance information."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:335
+#: src/webex/pages/popup.tsx:332
 #, c-format
 msgid "Bank requested reserve (%1$s) for %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:344
+#: src/webex/pages/popup.tsx:341
 #, c-format
 msgid "Started to withdraw %1$s from %2$s (%3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:353
+#: src/webex/pages/popup.tsx:350
 #, c-format
 msgid "Merchant %1$s offered contract %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:366
+#: src/webex/pages/popup.tsx:363
 #, c-format
 msgid "Withdrew %1$s from %2$s ( %3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:381
+#: src/webex/pages/popup.tsx:378
 #, c-format
 msgid "Paid %1$s to merchant %2$s.%3$s( %4$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:391
+#: src/webex/pages/popup.tsx:388
 #, c-format
 msgid "Merchant %1$s gave a refund over %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:404
+#: src/webex/pages/popup.tsx:400
 #, c-format
 msgid "tip"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:408
+#: src/webex/pages/popup.tsx:404
 #, c-format
 msgid "Merchant %1$s gave a %2$s of %3$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:414
+#: src/webex/pages/popup.tsx:410
 #, c-format
 msgid "You did not accept the tip yet."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:422
+#: src/webex/pages/popup.tsx:418
 #, c-format
 msgid "Unknown event (%1$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:464
+#: src/webex/pages/popup.tsx:460
 #, c-format
 msgid "Error: could not retrieve event history"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:489
+#: src/webex/pages/popup.tsx:485
 #, c-format
 msgid "Your wallet has no events recorded."
 msgstr ""
@@ -174,49 +194,49 @@ msgstr ""
 
 #. #-#-#-#-#  - (PACKAGE VERSION)  #-#-#-#-#
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/tip.tsx:118 src/webex/pages/withdraw.tsx:216
+#: src/webex/pages/tip.tsx:116 src/webex/pages/withdraw.tsx:214
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:73
+#: src/webex/pages/withdraw.tsx:72
 #, c-format
 msgid "Could not get details for withdraw operation:"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:89 src/webex/pages/withdraw.tsx:179
+#: src/webex/pages/withdraw.tsx:88 src/webex/pages/withdraw.tsx:178
 #, c-format
 msgid "Chose different exchange provider"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:108
+#: src/webex/pages/withdraw.tsx:107
 #, c-format
 msgid ""
 "Please select an exchange.  You can review the details before after your "
 "selection."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:120
+#: src/webex/pages/withdraw.tsx:119
 #, c-format
 msgid "Select %1$s"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:142
+#: src/webex/pages/withdraw.tsx:141
 #, c-format
 msgid "Select custom exchange"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:159
+#: src/webex/pages/withdraw.tsx:158
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:170
+#: src/webex/pages/withdraw.tsx:169
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:188
+#: src/webex/pages/withdraw.tsx:187
 #, c-format
 msgid "Cancel withdraw operation"
 msgstr ""
@@ -261,26 +281,6 @@ msgstr ""
 msgid "Deposit Fee"
 msgstr ""
 
-#: src/wire.ts:38
-#, c-format
-msgid "Invalid Wire"
-msgstr ""
-
-#: src/wire.ts:43 src/wire.ts:46
-#, c-format
-msgid "Invalid Test Wire Detail"
-msgstr ""
-
-#: src/wire.ts:48
-#, c-format
-msgid "Test Wire Acct #%1$s on %2$s"
-msgstr ""
-
-#: src/wire.ts:50
-#, c-format
-msgid "Unknown Wire Detail"
-msgstr ""
-
 #, fuzzy
 #~ msgid "DEBUG: Your balance on %1$s is %2$s KUDO. Get more at %3$s"
 #~ msgstr "DEBUG: Your balance is %2$s KUDO on %1$s. Get more at %3$s"
diff --git a/src/i18n/fr.po b/src/i18n/fr.po
index bbc02869..63256d55 100644
--- a/src/i18n/fr.po
+++ b/src/i18n/fr.po
@@ -27,6 +27,26 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+#: src/util/wire.ts:38
+#, c-format
+msgid "Invalid Wire"
+msgstr ""
+
+#: src/util/wire.ts:43 src/util/wire.ts:46
+#, c-format
+msgid "Invalid Test Wire Detail"
+msgstr ""
+
+#: src/util/wire.ts:48
+#, c-format
+msgid "Test Wire Acct #%1$s on %2$s"
+msgstr ""
+
+#: src/util/wire.ts:50
+#, c-format
+msgid "Unknown Wire Detail"
+msgstr ""
+
 #: src/webex/pages/benchmark.tsx:57
 #, c-format
 msgid "Operation"
@@ -37,122 +57,122 @@ msgstr ""
 msgid "time (ms/op)"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:118
+#: src/webex/pages/pay.tsx:117
 #, c-format
 msgid "The merchant %1$s offers you to purchase:"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:124
+#: src/webex/pages/pay.tsx:123
 #, c-format
 msgid "The total price is %1$s (plus %2$s fees)."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:129
+#: src/webex/pages/pay.tsx:128
 #, c-format
 msgid "The total price is %1$s."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:149
+#: src/webex/pages/pay.tsx:148
 #, c-format
 msgid "Retry"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:158
+#: src/webex/pages/pay.tsx:157
 #, c-format
 msgid "Confirm payment"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:162
+#: src/webex/pages/popup.tsx:159
 #, c-format
 msgid "Balance"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:163
+#: src/webex/pages/popup.tsx:160
 #, c-format
 msgid "History"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:164
+#: src/webex/pages/popup.tsx:161
 #, c-format
 msgid "Debug"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:200
+#: src/webex/pages/popup.tsx:197
 #, c-format
 msgid "You have no balance to show. Need some %1$s getting started?"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:263
+#: src/webex/pages/popup.tsx:260
 #, c-format
 msgid "%1$s incoming"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:275
+#: src/webex/pages/popup.tsx:272
 #, c-format
 msgid "%1$s being spent"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:306
+#: src/webex/pages/popup.tsx:303
 #, c-format
 msgid "Error: could not retrieve balance information."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:335
+#: src/webex/pages/popup.tsx:332
 #, c-format
 msgid "Bank requested reserve (%1$s) for %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:344
+#: src/webex/pages/popup.tsx:341
 #, c-format
 msgid "Started to withdraw %1$s from %2$s (%3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:353
+#: src/webex/pages/popup.tsx:350
 #, c-format
 msgid "Merchant %1$s offered contract %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:366
+#: src/webex/pages/popup.tsx:363
 #, c-format
 msgid "Withdrew %1$s from %2$s ( %3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:381
+#: src/webex/pages/popup.tsx:378
 #, c-format
 msgid "Paid %1$s to merchant %2$s.%3$s( %4$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:391
+#: src/webex/pages/popup.tsx:388
 #, c-format
 msgid "Merchant %1$s gave a refund over %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:404
+#: src/webex/pages/popup.tsx:400
 #, c-format
 msgid "tip"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:408
+#: src/webex/pages/popup.tsx:404
 #, c-format
 msgid "Merchant %1$s gave a %2$s of %3$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:414
+#: src/webex/pages/popup.tsx:410
 #, c-format
 msgid "You did not accept the tip yet."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:422
+#: src/webex/pages/popup.tsx:418
 #, c-format
 msgid "Unknown event (%1$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:464
+#: src/webex/pages/popup.tsx:460
 #, c-format
 msgid "Error: could not retrieve event history"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:489
+#: src/webex/pages/popup.tsx:485
 #, c-format
 msgid "Your wallet has no events recorded."
 msgstr ""
@@ -174,49 +194,49 @@ msgstr ""
 
 #. #-#-#-#-#  - (PACKAGE VERSION)  #-#-#-#-#
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/tip.tsx:118 src/webex/pages/withdraw.tsx:216
+#: src/webex/pages/tip.tsx:116 src/webex/pages/withdraw.tsx:214
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:73
+#: src/webex/pages/withdraw.tsx:72
 #, c-format
 msgid "Could not get details for withdraw operation:"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:89 src/webex/pages/withdraw.tsx:179
+#: src/webex/pages/withdraw.tsx:88 src/webex/pages/withdraw.tsx:178
 #, c-format
 msgid "Chose different exchange provider"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:108
+#: src/webex/pages/withdraw.tsx:107
 #, c-format
 msgid ""
 "Please select an exchange.  You can review the details before after your "
 "selection."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:120
+#: src/webex/pages/withdraw.tsx:119
 #, c-format
 msgid "Select %1$s"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:142
+#: src/webex/pages/withdraw.tsx:141
 #, c-format
 msgid "Select custom exchange"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:159
+#: src/webex/pages/withdraw.tsx:158
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:170
+#: src/webex/pages/withdraw.tsx:169
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:188
+#: src/webex/pages/withdraw.tsx:187
 #, c-format
 msgid "Cancel withdraw operation"
 msgstr ""
@@ -260,23 +280,3 @@ msgstr ""
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
-
-#: src/wire.ts:38
-#, c-format
-msgid "Invalid Wire"
-msgstr ""
-
-#: src/wire.ts:43 src/wire.ts:46
-#, c-format
-msgid "Invalid Test Wire Detail"
-msgstr ""
-
-#: src/wire.ts:48
-#, c-format
-msgid "Test Wire Acct #%1$s on %2$s"
-msgstr ""
-
-#: src/wire.ts:50
-#, c-format
-msgid "Unknown Wire Detail"
-msgstr ""
diff --git a/src/i18n/it.po b/src/i18n/it.po
index bbc02869..63256d55 100644
--- a/src/i18n/it.po
+++ b/src/i18n/it.po
@@ -27,6 +27,26 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+#: src/util/wire.ts:38
+#, c-format
+msgid "Invalid Wire"
+msgstr ""
+
+#: src/util/wire.ts:43 src/util/wire.ts:46
+#, c-format
+msgid "Invalid Test Wire Detail"
+msgstr ""
+
+#: src/util/wire.ts:48
+#, c-format
+msgid "Test Wire Acct #%1$s on %2$s"
+msgstr ""
+
+#: src/util/wire.ts:50
+#, c-format
+msgid "Unknown Wire Detail"
+msgstr ""
+
 #: src/webex/pages/benchmark.tsx:57
 #, c-format
 msgid "Operation"
@@ -37,122 +57,122 @@ msgstr ""
 msgid "time (ms/op)"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:118
+#: src/webex/pages/pay.tsx:117
 #, c-format
 msgid "The merchant %1$s offers you to purchase:"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:124
+#: src/webex/pages/pay.tsx:123
 #, c-format
 msgid "The total price is %1$s (plus %2$s fees)."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:129
+#: src/webex/pages/pay.tsx:128
 #, c-format
 msgid "The total price is %1$s."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:149
+#: src/webex/pages/pay.tsx:148
 #, c-format
 msgid "Retry"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:158
+#: src/webex/pages/pay.tsx:157
 #, c-format
 msgid "Confirm payment"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:162
+#: src/webex/pages/popup.tsx:159
 #, c-format
 msgid "Balance"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:163
+#: src/webex/pages/popup.tsx:160
 #, c-format
 msgid "History"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:164
+#: src/webex/pages/popup.tsx:161
 #, c-format
 msgid "Debug"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:200
+#: src/webex/pages/popup.tsx:197
 #, c-format
 msgid "You have no balance to show. Need some %1$s getting started?"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:263
+#: src/webex/pages/popup.tsx:260
 #, c-format
 msgid "%1$s incoming"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:275
+#: src/webex/pages/popup.tsx:272
 #, c-format
 msgid "%1$s being spent"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:306
+#: src/webex/pages/popup.tsx:303
 #, c-format
 msgid "Error: could not retrieve balance information."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:335
+#: src/webex/pages/popup.tsx:332
 #, c-format
 msgid "Bank requested reserve (%1$s) for %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:344
+#: src/webex/pages/popup.tsx:341
 #, c-format
 msgid "Started to withdraw %1$s from %2$s (%3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:353
+#: src/webex/pages/popup.tsx:350
 #, c-format
 msgid "Merchant %1$s offered contract %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:366
+#: src/webex/pages/popup.tsx:363
 #, c-format
 msgid "Withdrew %1$s from %2$s ( %3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:381
+#: src/webex/pages/popup.tsx:378
 #, c-format
 msgid "Paid %1$s to merchant %2$s.%3$s( %4$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:391
+#: src/webex/pages/popup.tsx:388
 #, c-format
 msgid "Merchant %1$s gave a refund over %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:404
+#: src/webex/pages/popup.tsx:400
 #, c-format
 msgid "tip"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:408
+#: src/webex/pages/popup.tsx:404
 #, c-format
 msgid "Merchant %1$s gave a %2$s of %3$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:414
+#: src/webex/pages/popup.tsx:410
 #, c-format
 msgid "You did not accept the tip yet."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:422
+#: src/webex/pages/popup.tsx:418
 #, c-format
 msgid "Unknown event (%1$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:464
+#: src/webex/pages/popup.tsx:460
 #, c-format
 msgid "Error: could not retrieve event history"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:489
+#: src/webex/pages/popup.tsx:485
 #, c-format
 msgid "Your wallet has no events recorded."
 msgstr ""
@@ -174,49 +194,49 @@ msgstr ""
 
 #. #-#-#-#-#  - (PACKAGE VERSION)  #-#-#-#-#
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/tip.tsx:118 src/webex/pages/withdraw.tsx:216
+#: src/webex/pages/tip.tsx:116 src/webex/pages/withdraw.tsx:214
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:73
+#: src/webex/pages/withdraw.tsx:72
 #, c-format
 msgid "Could not get details for withdraw operation:"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:89 src/webex/pages/withdraw.tsx:179
+#: src/webex/pages/withdraw.tsx:88 src/webex/pages/withdraw.tsx:178
 #, c-format
 msgid "Chose different exchange provider"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:108
+#: src/webex/pages/withdraw.tsx:107
 #, c-format
 msgid ""
 "Please select an exchange.  You can review the details before after your "
 "selection."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:120
+#: src/webex/pages/withdraw.tsx:119
 #, c-format
 msgid "Select %1$s"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:142
+#: src/webex/pages/withdraw.tsx:141
 #, c-format
 msgid "Select custom exchange"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:159
+#: src/webex/pages/withdraw.tsx:158
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:170
+#: src/webex/pages/withdraw.tsx:169
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:188
+#: src/webex/pages/withdraw.tsx:187
 #, c-format
 msgid "Cancel withdraw operation"
 msgstr ""
@@ -260,23 +280,3 @@ msgstr ""
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
-
-#: src/wire.ts:38
-#, c-format
-msgid "Invalid Wire"
-msgstr ""
-
-#: src/wire.ts:43 src/wire.ts:46
-#, c-format
-msgid "Invalid Test Wire Detail"
-msgstr ""
-
-#: src/wire.ts:48
-#, c-format
-msgid "Test Wire Acct #%1$s on %2$s"
-msgstr ""
-
-#: src/wire.ts:50
-#, c-format
-msgid "Unknown Wire Detail"
-msgstr ""
diff --git a/src/i18n/strings.ts b/src/i18n/strings.ts
index 86de3d8b..65a1d3f9 100644
--- a/src/i18n/strings.ts
+++ b/src/i18n/strings.ts
@@ -24,6 +24,22 @@ strings['de'] = {
         "plural_forms": "nplurals=2; plural=(n != 1);",
         "lang": ""
       },
+      "Invalid Wire": [
+        null,
+        ""
+      ],
+      "Invalid Test Wire Detail": [
+        null,
+        ""
+      ],
+      "Test Wire Acct #%1$s on %2$s": [
+        null,
+        ""
+      ],
+      "Unknown Wire Detail": [
+        null,
+        ""
+      ],
       "Operation": [
         null,
         ""
@@ -207,7 +223,19 @@ strings['de'] = {
       "Deposit Fee": [
         null,
         ""
-      ],
+      ]
+    }
+  }
+};
+strings['en-US'] = {
+  "domain": "messages",
+  "locale_data": {
+    "messages": {
+      "": {
+        "domain": "messages",
+        "plural_forms": "nplurals=2; plural=(n != 1);",
+        "lang": ""
+      },
       "Invalid Wire": [
         null,
         ""
@@ -223,19 +251,7 @@ strings['de'] = {
       "Unknown Wire Detail": [
         null,
         ""
-      ]
-    }
-  }
-};
-strings['en-US'] = {
-  "domain": "messages",
-  "locale_data": {
-    "messages": {
-      "": {
-        "domain": "messages",
-        "plural_forms": "nplurals=2; plural=(n != 1);",
-        "lang": ""
-      },
+      ],
       "Operation": [
         null,
         ""
@@ -419,7 +435,19 @@ strings['en-US'] = {
       "Deposit Fee": [
         null,
         ""
-      ],
+      ]
+    }
+  }
+};
+strings['fr'] = {
+  "domain": "messages",
+  "locale_data": {
+    "messages": {
+      "": {
+        "domain": "messages",
+        "plural_forms": "nplurals=2; plural=(n != 1);",
+        "lang": ""
+      },
       "Invalid Wire": [
         null,
         ""
@@ -435,19 +463,7 @@ strings['en-US'] = {
       "Unknown Wire Detail": [
         null,
         ""
-      ]
-    }
-  }
-};
-strings['fr'] = {
-  "domain": "messages",
-  "locale_data": {
-    "messages": {
-      "": {
-        "domain": "messages",
-        "plural_forms": "nplurals=2; plural=(n != 1);",
-        "lang": ""
-      },
+      ],
       "Operation": [
         null,
         ""
@@ -631,7 +647,19 @@ strings['fr'] = {
       "Deposit Fee": [
         null,
         ""
-      ],
+      ]
+    }
+  }
+};
+strings['it'] = {
+  "domain": "messages",
+  "locale_data": {
+    "messages": {
+      "": {
+        "domain": "messages",
+        "plural_forms": "nplurals=2; plural=(n != 1);",
+        "lang": ""
+      },
       "Invalid Wire": [
         null,
         ""
@@ -647,19 +675,7 @@ strings['fr'] = {
       "Unknown Wire Detail": [
         null,
         ""
-      ]
-    }
-  }
-};
-strings['it'] = {
-  "domain": "messages",
-  "locale_data": {
-    "messages": {
-      "": {
-        "domain": "messages",
-        "plural_forms": "nplurals=2; plural=(n != 1);",
-        "lang": ""
-      },
+      ],
       "Operation": [
         null,
         ""
@@ -843,7 +859,19 @@ strings['it'] = {
       "Deposit Fee": [
         null,
         ""
-      ],
+      ]
+    }
+  }
+};
+strings['sv'] = {
+  "domain": "messages",
+  "locale_data": {
+    "messages": {
+      "": {
+        "domain": "messages",
+        "plural_forms": "nplurals=2; plural=(n != 1);",
+        "lang": ""
+      },
       "Invalid Wire": [
         null,
         ""
@@ -858,20 +886,8 @@ strings['it'] = {
       ],
       "Unknown Wire Detail": [
         null,
-        ""
-      ]
-    }
-  }
-};
-strings['sv'] = {
-  "domain": "messages",
-  "locale_data": {
-    "messages": {
-      "": {
-        "domain": "messages",
-        "plural_forms": "nplurals=2; plural=(n != 1);",
-        "lang": ""
-      },
+        "visa mer"
+      ],
       "Operation": [
         null,
         ""
@@ -1055,22 +1071,6 @@ strings['sv'] = {
       "Deposit Fee": [
         null,
         "Depostitions avgift"
-      ],
-      "Invalid Wire": [
-        null,
-        ""
-      ],
-      "Invalid Test Wire Detail": [
-        null,
-        ""
-      ],
-      "Test Wire Acct #%1$s on %2$s": [
-        null,
-        ""
-      ],
-      "Unknown Wire Detail": [
-        null,
-        "visa mer"
       ]
     }
   }
diff --git a/src/i18n/sv.po b/src/i18n/sv.po
index dd546b77..03eab67f 100644
--- a/src/i18n/sv.po
+++ b/src/i18n/sv.po
@@ -27,6 +27,26 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+#: src/util/wire.ts:38
+#, c-format
+msgid "Invalid Wire"
+msgstr ""
+
+#: src/util/wire.ts:43 src/util/wire.ts:46
+#, c-format
+msgid "Invalid Test Wire Detail"
+msgstr ""
+
+#: src/util/wire.ts:48
+#, c-format
+msgid "Test Wire Acct #%1$s on %2$s"
+msgstr ""
+
+#: src/util/wire.ts:50
+#, fuzzy, c-format
+msgid "Unknown Wire Detail"
+msgstr "visa mer"
+
 #: src/webex/pages/benchmark.tsx:57
 #, c-format
 msgid "Operation"
@@ -37,124 +57,124 @@ msgstr ""
 msgid "time (ms/op)"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:118
+#: src/webex/pages/pay.tsx:117
 #, fuzzy, c-format
 msgid "The merchant %1$s offers you to purchase:"
 msgstr "Säljaren %1$s erbjuder följande:"
 
-#: src/webex/pages/pay.tsx:124
+#: src/webex/pages/pay.tsx:123
 #, fuzzy, c-format
 msgid "The total price is %1$s (plus %2$s fees)."
 msgstr "Det totala priset är %1$s (plus %2$s avgifter).\n"
 
-#: src/webex/pages/pay.tsx:129
+#: src/webex/pages/pay.tsx:128
 #, fuzzy, c-format
 msgid "The total price is %1$s."
 msgstr "Det totala priset är %1$s."
 
-#: src/webex/pages/pay.tsx:149
+#: src/webex/pages/pay.tsx:148
 #, c-format
 msgid "Retry"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:158
+#: src/webex/pages/pay.tsx:157
 #, c-format
 msgid "Confirm payment"
 msgstr "Godkän betalning"
 
-#: src/webex/pages/popup.tsx:162
+#: src/webex/pages/popup.tsx:159
 #, c-format
 msgid "Balance"
 msgstr "Balans"
 
-#: src/webex/pages/popup.tsx:163
+#: src/webex/pages/popup.tsx:160
 #, c-format
 msgid "History"
 msgstr "Historia"
 
-#: src/webex/pages/popup.tsx:164
+#: src/webex/pages/popup.tsx:161
 #, c-format
 msgid "Debug"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:200
+#: src/webex/pages/popup.tsx:197
 #, fuzzy, c-format
 msgid "You have no balance to show. Need some %1$s getting started?"
 msgstr ""
 "Du har ingen balans att visa. Behöver du\n"
 " %1$s att börja?\n"
 
-#: src/webex/pages/popup.tsx:263
+#: src/webex/pages/popup.tsx:260
 #, fuzzy, c-format
 msgid "%1$s incoming"
 msgstr "%1$s inkommande"
 
-#: src/webex/pages/popup.tsx:275
+#: src/webex/pages/popup.tsx:272
 #, c-format
 msgid "%1$s being spent"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:306
+#: src/webex/pages/popup.tsx:303
 #, c-format
 msgid "Error: could not retrieve balance information."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:335
+#: src/webex/pages/popup.tsx:332
 #, c-format
 msgid "Bank requested reserve (%1$s) for %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:344
+#: src/webex/pages/popup.tsx:341
 #, c-format
 msgid "Started to withdraw %1$s from %2$s (%3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:353
+#: src/webex/pages/popup.tsx:350
 #, fuzzy, c-format
 msgid "Merchant %1$s offered contract %2$s."
 msgstr "Säljaren %1$s erbjöd kontrakt %2$s.\n"
 
-#: src/webex/pages/popup.tsx:366
+#: src/webex/pages/popup.tsx:363
 #, c-format
 msgid "Withdrew %1$s from %2$s ( %3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:381
+#: src/webex/pages/popup.tsx:378
 #, c-format
 msgid "Paid %1$s to merchant %2$s.%3$s( %4$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:391
+#: src/webex/pages/popup.tsx:388
 #, fuzzy, c-format
 msgid "Merchant %1$s gave a refund over %2$s."
 msgstr "Säljaren %1$sgav en återbetalning på %2$s.\n"
 
-#: src/webex/pages/popup.tsx:404
+#: src/webex/pages/popup.tsx:400
 #, c-format
 msgid "tip"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:408
+#: src/webex/pages/popup.tsx:404
 #, fuzzy, c-format
 msgid "Merchant %1$s gave a %2$s of %3$s."
 msgstr "Säljaren %1$sgav en återbetalning på %2$s.\n"
 
-#: src/webex/pages/popup.tsx:414
+#: src/webex/pages/popup.tsx:410
 #, c-format
 msgid "You did not accept the tip yet."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:422
+#: src/webex/pages/popup.tsx:418
 #, c-format
 msgid "Unknown event (%1$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:464
+#: src/webex/pages/popup.tsx:460
 #, c-format
 msgid "Error: could not retrieve event history"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:489
+#: src/webex/pages/popup.tsx:485
 #, c-format
 msgid "Your wallet has no events recorded."
 msgstr "plånboken"
@@ -176,51 +196,51 @@ msgstr "Avbryt"
 
 #. #-#-#-#-#  - (PACKAGE VERSION)  #-#-#-#-#
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/tip.tsx:118 src/webex/pages/withdraw.tsx:216
+#: src/webex/pages/tip.tsx:116 src/webex/pages/withdraw.tsx:214
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:73
+#: src/webex/pages/withdraw.tsx:72
 #, c-format
 msgid "Could not get details for withdraw operation:"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:89 src/webex/pages/withdraw.tsx:179
+#: src/webex/pages/withdraw.tsx:88 src/webex/pages/withdraw.tsx:178
 #, fuzzy, c-format
 msgid "Chose different exchange provider"
 msgstr "Ändra tjänsteleverantörer"
 
-#: src/webex/pages/withdraw.tsx:108
+#: src/webex/pages/withdraw.tsx:107
 #, c-format
 msgid ""
 "Please select an exchange.  You can review the details before after your "
 "selection."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:120
+#: src/webex/pages/withdraw.tsx:119
 #, fuzzy, c-format
 msgid "Select %1$s"
 msgstr "Välj %1$s"
 
-#: src/webex/pages/withdraw.tsx:142
+#: src/webex/pages/withdraw.tsx:141
 #, c-format
 msgid "Select custom exchange"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:159
+#: src/webex/pages/withdraw.tsx:158
 #, fuzzy, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 "Du är på väg att ta ut\n"
 " %1$s från ditt bankkonto till din plånbok.\n"
 
-#: src/webex/pages/withdraw.tsx:170
+#: src/webex/pages/withdraw.tsx:169
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr "Acceptera avgifter och utbetala"
 
-#: src/webex/pages/withdraw.tsx:188
+#: src/webex/pages/withdraw.tsx:187
 #, c-format
 msgid "Cancel withdraw operation"
 msgstr ""
@@ -265,26 +285,6 @@ msgstr "Återhämtnings avgift"
 msgid "Deposit Fee"
 msgstr "Depostitions avgift"
 
-#: src/wire.ts:38
-#, c-format
-msgid "Invalid Wire"
-msgstr ""
-
-#: src/wire.ts:43 src/wire.ts:46
-#, c-format
-msgid "Invalid Test Wire Detail"
-msgstr ""
-
-#: src/wire.ts:48
-#, c-format
-msgid "Test Wire Acct #%1$s on %2$s"
-msgstr ""
-
-#: src/wire.ts:50
-#, fuzzy, c-format
-msgid "Unknown Wire Detail"
-msgstr "visa mer"
-
 #, c-format
 #~ msgid "help"
 #~ msgstr "hjälp"
diff --git a/src/i18n/taler-wallet-webex.pot b/src/i18n/taler-wallet-webex.pot
index bbc02869..63256d55 100644
--- a/src/i18n/taler-wallet-webex.pot
+++ b/src/i18n/taler-wallet-webex.pot
@@ -27,6 +27,26 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+#: src/util/wire.ts:38
+#, c-format
+msgid "Invalid Wire"
+msgstr ""
+
+#: src/util/wire.ts:43 src/util/wire.ts:46
+#, c-format
+msgid "Invalid Test Wire Detail"
+msgstr ""
+
+#: src/util/wire.ts:48
+#, c-format
+msgid "Test Wire Acct #%1$s on %2$s"
+msgstr ""
+
+#: src/util/wire.ts:50
+#, c-format
+msgid "Unknown Wire Detail"
+msgstr ""
+
 #: src/webex/pages/benchmark.tsx:57
 #, c-format
 msgid "Operation"
@@ -37,122 +57,122 @@ msgstr ""
 msgid "time (ms/op)"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:118
+#: src/webex/pages/pay.tsx:117
 #, c-format
 msgid "The merchant %1$s offers you to purchase:"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:124
+#: src/webex/pages/pay.tsx:123
 #, c-format
 msgid "The total price is %1$s (plus %2$s fees)."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:129
+#: src/webex/pages/pay.tsx:128
 #, c-format
 msgid "The total price is %1$s."
 msgstr ""
 
-#: src/webex/pages/pay.tsx:149
+#: src/webex/pages/pay.tsx:148
 #, c-format
 msgid "Retry"
 msgstr ""
 
-#: src/webex/pages/pay.tsx:158
+#: src/webex/pages/pay.tsx:157
 #, c-format
 msgid "Confirm payment"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:162
+#: src/webex/pages/popup.tsx:159
 #, c-format
 msgid "Balance"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:163
+#: src/webex/pages/popup.tsx:160
 #, c-format
 msgid "History"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:164
+#: src/webex/pages/popup.tsx:161
 #, c-format
 msgid "Debug"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:200
+#: src/webex/pages/popup.tsx:197
 #, c-format
 msgid "You have no balance to show. Need some %1$s getting started?"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:263
+#: src/webex/pages/popup.tsx:260
 #, c-format
 msgid "%1$s incoming"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:275
+#: src/webex/pages/popup.tsx:272
 #, c-format
 msgid "%1$s being spent"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:306
+#: src/webex/pages/popup.tsx:303
 #, c-format
 msgid "Error: could not retrieve balance information."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:335
+#: src/webex/pages/popup.tsx:332
 #, c-format
 msgid "Bank requested reserve (%1$s) for %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:344
+#: src/webex/pages/popup.tsx:341
 #, c-format
 msgid "Started to withdraw %1$s from %2$s (%3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:353
+#: src/webex/pages/popup.tsx:350
 #, c-format
 msgid "Merchant %1$s offered contract %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:366
+#: src/webex/pages/popup.tsx:363
 #, c-format
 msgid "Withdrew %1$s from %2$s ( %3$s)."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:381
+#: src/webex/pages/popup.tsx:378
 #, c-format
 msgid "Paid %1$s to merchant %2$s.%3$s( %4$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:391
+#: src/webex/pages/popup.tsx:388
 #, c-format
 msgid "Merchant %1$s gave a refund over %2$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:404
+#: src/webex/pages/popup.tsx:400
 #, c-format
 msgid "tip"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:408
+#: src/webex/pages/popup.tsx:404
 #, c-format
 msgid "Merchant %1$s gave a %2$s of %3$s."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:414
+#: src/webex/pages/popup.tsx:410
 #, c-format
 msgid "You did not accept the tip yet."
 msgstr ""
 
-#: src/webex/pages/popup.tsx:422
+#: src/webex/pages/popup.tsx:418
 #, c-format
 msgid "Unknown event (%1$s)"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:464
+#: src/webex/pages/popup.tsx:460
 #, c-format
 msgid "Error: could not retrieve event history"
 msgstr ""
 
-#: src/webex/pages/popup.tsx:489
+#: src/webex/pages/popup.tsx:485
 #, c-format
 msgid "Your wallet has no events recorded."
 msgstr ""
@@ -174,49 +194,49 @@ msgstr ""
 
 #. #-#-#-#-#  - (PACKAGE VERSION)  #-#-#-#-#
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/tip.tsx:118 src/webex/pages/withdraw.tsx:216
+#: src/webex/pages/tip.tsx:116 src/webex/pages/withdraw.tsx:214
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:73
+#: src/webex/pages/withdraw.tsx:72
 #, c-format
 msgid "Could not get details for withdraw operation:"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:89 src/webex/pages/withdraw.tsx:179
+#: src/webex/pages/withdraw.tsx:88 src/webex/pages/withdraw.tsx:178
 #, c-format
 msgid "Chose different exchange provider"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:108
+#: src/webex/pages/withdraw.tsx:107
 #, c-format
 msgid ""
 "Please select an exchange.  You can review the details before after your "
 "selection."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:120
+#: src/webex/pages/withdraw.tsx:119
 #, c-format
 msgid "Select %1$s"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:142
+#: src/webex/pages/withdraw.tsx:141
 #, c-format
 msgid "Select custom exchange"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:159
+#: src/webex/pages/withdraw.tsx:158
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:170
+#: src/webex/pages/withdraw.tsx:169
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/withdraw.tsx:188
+#: src/webex/pages/withdraw.tsx:187
 #, c-format
 msgid "Cancel withdraw operation"
 msgstr ""
@@ -260,23 +280,3 @@ msgstr ""
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
-
-#: src/wire.ts:38
-#, c-format
-msgid "Invalid Wire"
-msgstr ""
-
-#: src/wire.ts:43 src/wire.ts:46
-#, c-format
-msgid "Invalid Test Wire Detail"
-msgstr ""
-
-#: src/wire.ts:48
-#, c-format
-msgid "Test Wire Acct #%1$s on %2$s"
-msgstr ""
-
-#: src/wire.ts:50
-#, c-format
-msgid "Unknown Wire Detail"
-msgstr ""
diff --git a/src/util/asyncMemo.ts b/src/util/asyncMemo.ts
index 8b7b1c9b..34868ab4 100644
--- a/src/util/asyncMemo.ts
+++ b/src/util/asyncMemo.ts
@@ -21,8 +21,8 @@ export interface MemoEntry<T> {
 }
 
 export class AsyncOpMemo<T> {
-  n = 0;
-  memo: { [k: string]: MemoEntry<T> } = {};
+  private n = 0;
+  private memo: { [k: string]: MemoEntry<T> } = {};
   put(key: string, p: Promise<T>): Promise<T> {
     const n = this.n++;
     this.memo[key] = {
diff --git a/src/util/promiseUtils.ts b/src/util/promiseUtils.ts
index eb649471..9add2c40 100644
--- a/src/util/promiseUtils.ts
+++ b/src/util/promiseUtils.ts
@@ -36,4 +36,25 @@ export function openPromise<T>(): OpenedPromise<T> {
     throw Error();
   }
   return { resolve, reject, promise };
+}
+
+export class AsyncCondition {
+  private _waitPromise: Promise<void>;
+  private _resolveWaitPromise: (val: void) => void;
+  constructor() {
+    const op = openPromise<void>();
+    this._waitPromise = op.promise;
+    this._resolveWaitPromise = op.resolve;
+  }
+
+  wait(): Promise<void> {
+    return this._waitPromise;
+  }
+
+  trigger(): void {
+    this._resolveWaitPromise();
+    const op = openPromise<void>();
+    this._waitPromise = op.promise;
+    this._resolveWaitPromise = op.resolve;
+  }
 }
\ No newline at end of file
diff --git a/src/util/query.ts b/src/util/query.ts
index 5726bcaa..6942d471 100644
--- a/src/util/query.ts
+++ b/src/util/query.ts
@@ -351,15 +351,32 @@ class TransactionHandle {
   }
 }
 
+export function runWithReadTransaction<T>(
+  db: IDBDatabase,
+  stores: Store<any>[],
+  f: (t: TransactionHandle) => Promise<T>,
+): Promise<T> {
+  return runWithTransaction<T>(db, stores, f, "readonly");
+}
+
 export function runWithWriteTransaction<T>(
   db: IDBDatabase,
   stores: Store<any>[],
   f: (t: TransactionHandle) => Promise<T>,
+): Promise<T> {
+  return runWithTransaction<T>(db, stores, f, "readwrite");
+}
+
+function runWithTransaction<T>(
+  db: IDBDatabase,
+  stores: Store<any>[],
+  f: (t: TransactionHandle) => Promise<T>,
+  mode: "readonly" | "readwrite",
 ): Promise<T> {
   const stack = Error("Failed transaction was started here.");
   return new Promise((resolve, reject) => {
     const storeName = stores.map(x => x.name);
-    const tx = db.transaction(storeName, "readwrite");
+    const tx = db.transaction(storeName, mode);
     let funResult: any = undefined;
     let gotFunResult: boolean = false;
     tx.oncomplete = () => {
diff --git a/src/util/timer.ts b/src/util/timer.ts
index d3bb5d48..865c17fa 100644
--- a/src/util/timer.ts
+++ b/src/util/timer.ts
@@ -105,6 +105,14 @@ export class TimerGroup {
     }
   }
 
+  resolveAfter(delayMs: number): Promise<void> {
+    return new Promise<void>((resolve, reject) => {
+      this.after(delayMs, () => {
+        resolve();
+      });
+    });
+  }
+
   after(delayMs: number, callback: () => void): TimerHandle {
     if (this.stopped) {
       console.warn("dropping timer since timer group is stopped");
diff --git a/src/wallet-impl/balance.ts b/src/wallet-impl/balance.ts
index 1d8e077a..0abc9663 100644
--- a/src/wallet-impl/balance.ts
+++ b/src/wallet-impl/balance.ts
@@ -18,12 +18,10 @@
  * Imports.
  */
 import {
-  HistoryQuery,
-  HistoryEvent,
   WalletBalance,
   WalletBalanceEntry,
 } from "../walletTypes";
-import { oneShotIter, runWithWriteTransaction } from "../util/query";
+import { runWithReadTransaction } from "../util/query";
 import { InternalWalletState } from "./state";
 import { Stores, TipRecord, CoinStatus } from "../dbTypes";
 import * as Amounts from "../util/amounts";
@@ -77,7 +75,7 @@ export async function getBalances(
     byExchange: {},
   };
 
-  await runWithWriteTransaction(
+  await runWithReadTransaction(
     ws.db,
     [Stores.coins, Stores.refresh, Stores.reserves, Stores.purchases],
     async tx => {
diff --git a/src/wallet-impl/pay.ts b/src/wallet-impl/pay.ts
index d4d2b3cd..31a1500e 100644
--- a/src/wallet-impl/pay.ts
+++ b/src/wallet-impl/pay.ts
@@ -358,9 +358,14 @@ async function recordConfirmPay(
 }
 
 function getNextUrl(contractTerms: ContractTerms): string {
-  const fu = new URL(contractTerms.fulfillment_url)
-  fu.searchParams.set("order_id", contractTerms.order_id);
-  return fu.href;
+  const f = contractTerms.fulfillment_url;
+  if (f.startsWith("http://";) || f.startsWith("https://";)) {
+    const fu = new URL(contractTerms.fulfillment_url)
+    fu.searchParams.set("order_id", contractTerms.order_id);
+    return fu.href;
+  } else {
+    return f;
+  }
 }
 
 export async function abortFailedPayment(
diff --git a/src/wallet-impl/pending.ts b/src/wallet-impl/pending.ts
index a66571a3..dbc6672c 100644
--- a/src/wallet-impl/pending.ts
+++ b/src/wallet-impl/pending.ts
@@ -14,18 +14,29 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
- /**
-  * Imports.
-  */
-import { PendingOperationInfo, PendingOperationsResponse } from 
"../walletTypes";
+/**
+ * Imports.
+ */
+import {
+  PendingOperationInfo,
+  PendingOperationsResponse,
+  getTimestampNow,
+} from "../walletTypes";
 import { oneShotIter } from "../util/query";
 import { InternalWalletState } from "./state";
-import { Stores, ExchangeUpdateStatus, ReserveRecordStatus, CoinStatus, 
ProposalStatus } from "../dbTypes";
+import {
+  Stores,
+  ExchangeUpdateStatus,
+  ReserveRecordStatus,
+  CoinStatus,
+  ProposalStatus,
+} from "../dbTypes";
 
 export async function getPendingOperations(
   ws: InternalWalletState,
 ): Promise<PendingOperationsResponse> {
   const pendingOperations: PendingOperationInfo[] = [];
+  let minRetryDurationMs = 5000;
   const exchanges = await oneShotIter(ws.db, Stores.exchanges).toArray();
   for (let e of exchanges) {
     switch (e.updateStatus) {
@@ -92,9 +103,8 @@ export async function getPendingOperations(
     }
   }
   await oneShotIter(ws.db, Stores.reserves).forEach(reserve => {
-    const reserveType = reserve.bankWithdrawStatusUrl
-      ? "taler-bank"
-      : "manual";
+    const reserveType = reserve.bankWithdrawStatusUrl ? "taler-bank" : 
"manual";
+    const now = getTimestampNow();
     switch (reserve.reserveStatus) {
       case ReserveRecordStatus.DORMANT:
         // nothing to report as pending
@@ -110,6 +120,11 @@ export async function getPendingOperations(
           reserveType,
           reservePub: reserve.reservePub,
         });
+        if (reserve.created.t_ms < now.t_ms - 5000) {
+          minRetryDurationMs = 500;
+        } else if (reserve.created.t_ms < now.t_ms - 30000) {
+          minRetryDurationMs = 2000;
+        }
         break;
       case ReserveRecordStatus.WAIT_CONFIRM_BANK:
         pendingOperations.push({
@@ -120,6 +135,11 @@ export async function getPendingOperations(
           reservePub: reserve.reservePub,
           bankWithdrawConfirmUrl: reserve.bankWithdrawConfirmUrl,
         });
+        if (reserve.created.t_ms < now.t_ms - 5000) {
+          minRetryDurationMs = 500;
+        } else if (reserve.created.t_ms < now.t_ms - 30000) {
+          minRetryDurationMs = 2000;
+        }
         break;
       default:
         pendingOperations.push({
@@ -164,10 +184,7 @@ export async function getPendingOperations(
   });
 
   await oneShotIter(ws.db, Stores.withdrawalSession).forEach(ws => {
-    const numCoinsWithdrawn = ws.withdrawn.reduce(
-      (a, x) => a + (x ? 1 : 0),
-      0,
-    );
+    const numCoinsWithdrawn = ws.withdrawn.reduce((a, x) => a + (x ? 1 : 0), 
0);
     const numCoinsTotal = ws.withdrawn.length;
     if (numCoinsWithdrawn < numCoinsTotal) {
       pendingOperations.push({
@@ -204,5 +221,8 @@ export async function getPendingOperations(
 
   return {
     pendingOperations,
+    nextRetryDelay: {
+      d_ms: minRetryDurationMs,
+    },
   };
 }
diff --git a/src/wallet-impl/reserves.ts b/src/wallet-impl/reserves.ts
index 265eddce..5d624fe2 100644
--- a/src/wallet-impl/reserves.ts
+++ b/src/wallet-impl/reserves.ts
@@ -105,6 +105,7 @@ export async function createReserve(
   const exchangeInfo = await updateExchangeFromUrl(ws, req.exchange);
   const exchangeDetails = exchangeInfo.details;
   if (!exchangeDetails) {
+    console.log(exchangeDetails);
     throw Error("exchange not updated");
   }
   const { isAudited, isTrusted } = await getExchangeTrust(ws, exchangeInfo);
diff --git a/src/wallet-impl/state.ts b/src/wallet-impl/state.ts
index 3d6bb8bd..a04a7dd1 100644
--- a/src/wallet-impl/state.ts
+++ b/src/wallet-impl/state.ts
@@ -29,4 +29,5 @@ export interface InternalWalletState {
   speculativePayData: SpeculativePayData | undefined;
   cachedNextUrl: { [fulfillmentUrl: string]: NextUrlResult };
   memoProcessReserve: AsyncOpMemo<void>;
+  memoMakePlanchet: AsyncOpMemo<void>;
 }
\ No newline at end of file
diff --git a/src/wallet-impl/withdraw.ts b/src/wallet-impl/withdraw.ts
index 4e2d8055..baeae1a5 100644
--- a/src/wallet-impl/withdraw.ts
+++ b/src/wallet-impl/withdraw.ts
@@ -189,7 +189,6 @@ async function processPlanchet(
     return;
   }
   if (withdrawalSession.source.type === "reserve") {
-
   }
   const planchet = withdrawalSession.planchets[coinIdx];
   if (!planchet) {
@@ -251,10 +250,7 @@ async function processPlanchet(
     ws.db,
     [Stores.coins, Stores.withdrawalSession, Stores.reserves],
     async tx => {
-      const ws = await tx.get(
-        Stores.withdrawalSession,
-        withdrawalSessionId,
-      );
+      const ws = await tx.get(Stores.withdrawalSession, withdrawalSessionId);
       if (!ws) {
         return;
       }
@@ -350,12 +346,72 @@ export async function getVerifiedWithdrawDenomList(
   return selectedDenoms;
 }
 
+async function makePlanchet(
+  ws: InternalWalletState,
+  withdrawalSessionId: string,
+  coinIndex: number,
+): Promise<void> {
+  const withdrawalSession = await oneShotGet(
+    ws.db,
+    Stores.withdrawalSession,
+    withdrawalSessionId,
+  );
+  if (!withdrawalSession) {
+    return;
+  }
+  const src = withdrawalSession.source;
+  if (src.type !== "reserve") {
+    throw Error("invalid state");
+  }
+  const reserve = await oneShotGet(ws.db, Stores.reserves, src.reservePub);
+  if (!reserve) {
+    return;
+  }
+  const denom = await oneShotGet(ws.db, Stores.denominations, [
+    withdrawalSession.exchangeBaseUrl,
+    withdrawalSession.denoms[coinIndex],
+  ]);
+  if (!denom) {
+    return;
+  }
+  const r = await ws.cryptoApi.createPlanchet({
+    denomPub: denom.denomPub,
+    feeWithdraw: denom.feeWithdraw,
+    reservePriv: reserve.reservePriv,
+    reservePub: reserve.reservePub,
+    value: denom.value,
+  });
+  const newPlanchet: PlanchetRecord = {
+    blindingKey: r.blindingKey,
+    coinEv: r.coinEv,
+    coinPriv: r.coinPriv,
+    coinPub: r.coinPub,
+    coinValue: r.coinValue,
+    denomPub: r.denomPub,
+    denomPubHash: r.denomPubHash,
+    isFromTip: false,
+    reservePub: r.reservePub,
+    withdrawSig: r.withdrawSig,
+  };
+  await runWithWriteTransaction(ws.db, [Stores.withdrawalSession], async tx => 
{
+    const myWs = await tx.get(Stores.withdrawalSession, withdrawalSessionId);
+    if (!myWs) {
+      return;
+    }
+    if (myWs.planchets[coinIndex]) {
+      return;
+    }
+    myWs.planchets[coinIndex] = newPlanchet;
+    await tx.put(Stores.withdrawalSession, myWs);
+  });
+}
+
 async function processWithdrawCoin(
   ws: InternalWalletState,
   withdrawalSessionId: string,
   coinIndex: number,
 ) {
-  logger.info("starting withdraw for coin");
+  logger.trace("starting withdraw for coin", coinIndex);
   const withdrawalSession = await oneShotGet(
     ws.db,
     Stores.withdrawalSession,
@@ -377,63 +433,23 @@ async function processWithdrawCoin(
     return;
   }
 
-  if (withdrawalSession.planchets[coinIndex]) {
-    return processPlanchet(ws, withdrawalSessionId, coinIndex);
-  } else {
-    const src = withdrawalSession.source;
-    if (src.type !== "reserve") {
-      throw Error("invalid state");
-    }
-    const reserve = await oneShotGet(ws.db, Stores.reserves, src.reservePub)
-    if (!reserve) {
-      return;
-    }
-    const denom = await oneShotGet(ws.db, Stores.denominations, [
-      withdrawalSession.exchangeBaseUrl,
-      withdrawalSession.denoms[coinIndex],
-    ]);
-    if (!denom) {
-      return;
+  if (!withdrawalSession.planchets[coinIndex]) {
+    logger.trace("creating planchet for coin", coinIndex);
+    const key = `${withdrawalSessionId}-${coinIndex}`;
+    const p = ws.memoMakePlanchet.find(key);
+    if (p) {
+      await p;
+    } else {
+      ws.memoMakePlanchet.put(
+        key,
+        makePlanchet(ws, withdrawalSessionId, coinIndex),
+      );
     }
-    const r = await ws.cryptoApi.createPlanchet({
-      denomPub: denom.denomPub,
-      feeWithdraw: denom.feeWithdraw,
-      reservePriv: reserve.reservePriv,
-      reservePub: reserve.reservePub,
-      value: denom.value,
-    });
-    const newPlanchet: PlanchetRecord = {
-      blindingKey: r.blindingKey,
-      coinEv: r.coinEv,
-      coinPriv: r.coinPriv,
-      coinPub: r.coinPub,
-      coinValue: r.coinValue,
-      denomPub: r.denomPub,
-      denomPubHash: r.denomPubHash,
-      isFromTip: false,
-      reservePub: r.reservePub,
-      withdrawSig: r.withdrawSig,
-    };
-    await runWithWriteTransaction(
-      ws.db,
-      [Stores.withdrawalSession],
-      async tx => {
-        const myWs = await tx.get(
-          Stores.withdrawalSession,
-          withdrawalSessionId,
-        );
-        if (!myWs) {
-          return;
-        }
-        if (myWs.planchets[coinIndex]) {
-          return;
-        }
-        myWs.planchets[coinIndex] = newPlanchet;
-        await tx.put(Stores.withdrawalSession, myWs);
-      },
-    );
-    await processPlanchet(ws, withdrawalSessionId, coinIndex);
+    await makePlanchet(ws, withdrawalSessionId, coinIndex);
+    logger.trace("done creating planchet for coin", coinIndex);
   }
+  await processPlanchet(ws, withdrawalSessionId, coinIndex);
+  logger.trace("starting withdraw for coin", coinIndex);
 }
 
 export async function processWithdrawSession(
diff --git a/src/wallet.ts b/src/wallet.ts
index 91f6c0cc..a6eecb8a 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -46,7 +46,6 @@ import {
   abortFailedPayment,
   preparePay,
   confirmPay,
-  SpeculativePayData,
 } from "./wallet-impl/pay";
 
 import {
@@ -55,7 +54,6 @@ import {
   CurrencyRecord,
   DenominationRecord,
   ExchangeRecord,
-  PlanchetRecord,
   ProposalRecord,
   PurchaseRecord,
   ReserveRecord,
@@ -107,9 +105,11 @@ import { processWithdrawSession } from 
"./wallet-impl/withdraw";
 import { getHistory } from "./wallet-impl/history";
 import { getPendingOperations } from "./wallet-impl/pending";
 import { getBalances } from "./wallet-impl/balance";
-import { acceptTip, getTipStatus } from "./wallet-impl/tip";
+import { acceptTip, getTipStatus, processTip } from "./wallet-impl/tip";
 import { returnCoins } from "./wallet-impl/return";
 import { payback } from "./wallet-impl/payback";
+import { TimerGroup } from "./util/timer";
+import { AsyncCondition } from "./util/promiseUtils";
 
 /**
  * Wallet protocol version spoken with the exchange
@@ -155,6 +155,9 @@ const logger = new Logger("wallet.ts");
  */
 export class Wallet {
   private ws: InternalWalletState;
+  private timerGroup: TimerGroup = new TimerGroup();
+  private latch = new AsyncCondition();
+  private stopped: boolean = false;
 
   get db(): IDBDatabase {
     return this.ws.db;
@@ -188,6 +191,7 @@ export class Wallet {
       notifier,
       speculativePayData: undefined,
       memoProcessReserve: new AsyncOpMemo<void>(),
+      memoMakePlanchet: new AsyncOpMemo<void>(),
     };
   }
 
@@ -195,7 +199,6 @@ export class Wallet {
     return getExchangePaytoUri(this.ws, exchangeBaseUrl, supportedTargetTypes);
   }
 
-
   getWithdrawDetailsForAmount(baseUrl: any, amount: AmountJson): any {
     return getWithdrawDetailsForAmount(this.ws, baseUrl, amount);
   }
@@ -210,26 +213,26 @@ export class Wallet {
       case "bug":
         return;
       case "dirty-coin":
-        await this.refresh(pending.coinPub);
+        await refresh(this.ws, pending.coinPub);
         break;
       case "exchange-update":
-        await this.updateExchangeFromUrl(pending.exchangeBaseUrl);
-        break;
-      case "planchet":
-        // Nothing to do, since the withdraw session will process the planchet
+        await updateExchangeFromUrl(this.ws, pending.exchangeBaseUrl);
         break;
       case "refresh":
-        await this.processRefreshSession(pending.refreshSessionId);
+        await processRefreshSession(this.ws, pending.refreshSessionId);
         break;
       case "reserve":
-        await this.processReserve(pending.reservePub);
+        await processReserve(this.ws, pending.reservePub);
         break;
       case "withdraw":
-        await this.processWithdrawSession(pending.withdrawSessionId);
+        await processWithdrawSession(this.ws, pending.withdrawSessionId);
         break;
       case "proposal":
         // Nothing to do, user needs to accept/reject
         break;
+      case "tip":
+        await processTip(this.ws, pending.tipId);
+        break;
       default:
         assertUnreachable(pending);
     }
@@ -253,8 +256,22 @@ export class Wallet {
    * Process pending operations and wait for scheduled operations in
    * a loop until the wallet is stopped explicitly.
    */
-  public async runUntilStopped(): Promise<void> {
-    throw Error("not implemented");
+  public async runLoopScheduledRetries(): Promise<void> {
+    while (!this.stopped) {
+      console.log("running wallet retry loop iteration");
+      let pending = await this.getPendingOperations();
+      console.log("waiting for", pending.nextRetryDelay);
+      const timeout = 
this.timerGroup.resolveAfter(pending.nextRetryDelay.d_ms);
+      await Promise.race([timeout, this.latch.wait()]);
+      pending = await this.getPendingOperations();
+      for (const p of pending.pendingOperations) {
+        try {
+          this.processOnePendingOperation(p);
+        } catch (e) {
+          console.error(e);
+        }
+      }
+    }
   }
 
   /**
@@ -267,9 +284,12 @@ export class Wallet {
       const allPending = r.pendingOperations;
       const relevantPending = allPending.filter(x => {
         switch (x.type) {
-          case "planchet":
           case "reserve":
             return x.reservePub === reservePub;
+          case "withdraw":
+            return (
+              x.source.type === "reserve" && x.source.reservePub === reservePub
+            );
           default:
             return false;
         }
@@ -347,7 +367,11 @@ export class Wallet {
     proposalId: string,
     sessionIdOverride: string | undefined,
   ): Promise<ConfirmPayResult> {
-    return confirmPay(this.ws, proposalId, sessionIdOverride);
+    try {
+      return await confirmPay(this.ws, proposalId, sessionIdOverride);
+    } finally {
+      this.latch.trigger();
+    }
   }
 
   /**
@@ -358,7 +382,11 @@ export class Wallet {
    * state DORMANT.
    */
   async processReserve(reservePub: string): Promise<void> {
-    return processReserve(this.ws, reservePub);
+    try {
+      return await processReserve(this.ws, reservePub);
+    } finally {
+      this.latch.trigger();
+    }
   }
 
   /**
@@ -370,7 +398,11 @@ export class Wallet {
   async createReserve(
     req: CreateReserveRequest,
   ): Promise<CreateReserveResponse> {
-    return createReserve(this.ws, req);
+    try {
+      return createReserve(this.ws, req);
+    } finally {
+      this.latch.trigger();
+    }
   }
 
   /**
@@ -383,14 +415,13 @@ export class Wallet {
    * an unconfirmed reserve should be hidden.
    */
   async confirmReserve(req: ConfirmReserveRequest): Promise<void> {
-    return confirmReserve(this.ws, req);
+    try {
+      return confirmReserve(this.ws, req);
+    } finally {
+      this.latch.trigger();
+    }
   }
 
-  private async processWithdrawSession(
-    withdrawalSessionId: string,
-  ): Promise<void> {
-    return processWithdrawSession(this.ws, withdrawalSessionId);
-  }
 
   /**
    * Check if and how an exchange is trusted and/or audited.
@@ -435,10 +466,6 @@ export class Wallet {
     return refresh(this.ws, oldCoinPub, force);
   }
 
-  async processRefreshSession(refreshSessionId: string) {
-    return processRefreshSession(this.ws, refreshSessionId);
-  }
-
   async findExchange(
     exchangeBaseUrl: string,
   ): Promise<ExchangeRecord | undefined> {
@@ -516,7 +543,8 @@ export class Wallet {
    * Stop ongoing processing.
    */
   stop() {
-    //this.timerGroup.stopCurrentAndFutureTimers();
+    this.stopped = true;
+    this.timerGroup.stopCurrentAndFutureTimers();
     this.cryptoApi.stop();
   }
 
diff --git a/src/walletTypes.ts b/src/walletTypes.ts
index 5736282e..b12b29c5 100644
--- a/src/walletTypes.ts
+++ b/src/walletTypes.ts
@@ -609,6 +609,7 @@ export type PendingOperationInfo =
 
 export interface PendingOperationsResponse {
   pendingOperations: PendingOperationInfo[];
+  nextRetryDelay: Duration;
 }
 
 export interface HistoryQuery {
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 2d7f963e..752027b7 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -467,6 +467,9 @@ async function reinitWallet() {
     notifier,
     new BrowserCryptoWorkerFactory(),
   );
+  wallet.runLoopScheduledRetries().catch((e) => {
+    console.log("error during wallet retry loop", e);
+  });
   // Useful for debugging in the background page.
   (window as any).talerWallet = wallet;
   currentWallet = wallet;

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

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