gnunet-svn
[Top][All Lists]
Advanced

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

[taler-taler-ios] branch master updated: download taler-wallet-embedded.


From: gnunet
Subject: [taler-taler-ios] branch master updated: download taler-wallet-embedded.js in build and init backend
Date: Wed, 28 Jul 2021 08:52:17 +0200

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

jonathan-buchanan pushed a commit to branch master
in repository taler-ios.

The following commit(s) were added to refs/heads/master by this push:
     new 956bcb6  download taler-wallet-embedded.js in build and init backend
956bcb6 is described below

commit 956bcb66ef54d7098ce825fb8055dd10b5d4b1f5
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
AuthorDate: Wed Jul 28 02:51:57 2021 -0400

    download taler-wallet-embedded.js in build and init backend
---
 .gitignore                      |     1 +
 Taler.xcodeproj/project.pbxproj |    76 +-
 Taler/AppDelegate.swift         |    14 +-
 Taler/Info.plist                |     2 +-
 Taler/WalletBackend.swift       |   130 +
 Taler/iono.js                   |    69 -
 Taler/taler-wallet-ios.js       | 31483 --------------------------------------
 bootstrap                       |     6 +-
 iono                            |     2 +-
 9 files changed, 205 insertions(+), 31578 deletions(-)

diff --git a/.gitignore b/.gitignore
index 4ad3cd8..4437c4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 Taler.xcodeproj/xcuserdata
+taler-wallet-embedded.js
diff --git a/Taler.xcodeproj/project.pbxproj b/Taler.xcodeproj/project.pbxproj
index b0f4d87..a8a4b3d 100644
--- a/Taler.xcodeproj/project.pbxproj
+++ b/Taler.xcodeproj/project.pbxproj
@@ -3,15 +3,14 @@
        archiveVersion = 1;
        classes = {
        };
-       objectVersion = 50;
+       objectVersion = 54;
        objects = {
 
 /* Begin PBXBuildFile section */
+               D112510026B12E3200D02E00 /* taler-wallet-embedded.js in 
CopyFiles */ = {isa = PBXBuildFile; fileRef = D11250FF26B12E3200D02E00 /* 
taler-wallet-embedded.js */; };
                D14AFD2124D232B300C51073 /* AppDelegate.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD2024D232B300C51073 /* AppDelegate.swift 
*/; };
                D14AFD2324D232B300C51073 /* SceneDelegate.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD2224D232B300C51073 /* SceneDelegate.swift 
*/; };
                D14AFD2524D232B300C51073 /* ContentView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD2424D232B300C51073 /* ContentView.swift 
*/; };
-               D14AFD2724D232B500C51073 /* Assets.xcassets in Resources */ = 
{isa = PBXBuildFile; fileRef = D14AFD2624D232B500C51073 /* Assets.xcassets */; 
};
-               D14AFD2D24D232B500C51073 /* LaunchScreen.storyboard in 
Resources */ = {isa = PBXBuildFile; fileRef = D14AFD2B24D232B500C51073 /* 
LaunchScreen.storyboard */; };
                D14AFD3824D232B500C51073 /* TalerTests.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD3724D232B500C51073 /* TalerTests.swift */; 
};
                D14AFD4324D232B500C51073 /* TalerUITests.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD4224D232B500C51073 /* TalerUITests.swift 
*/; };
                D17D8B7225ADB29A001BD43D /* libbrotli.a in Frameworks */ = {isa 
= PBXBuildFile; fileRef = D17D8B4F25ADB12D001BD43D /* libbrotli.a */; };
@@ -35,7 +34,7 @@
                D17D8B8425ADB29B001BD43D /* libhistogram.a in Frameworks */ = 
{isa = PBXBuildFile; fileRef = D17D8B5625ADB130001BD43D /* libhistogram.a */; };
                D17D8B8525ADB29B001BD43D /* libcares.a in Frameworks */ = {isa 
= PBXBuildFile; fileRef = D17D8B4825ADB12B001BD43D /* libcares.a */; };
                D1AFF0F3268D59C200FBB744 /* libiono.a in Frameworks */ = {isa = 
PBXBuildFile; fileRef = D1AFF0F2268D59A500FBB744 /* libiono.a */; };
-               D1D6435F2681290200A22334 /* taler-wallet-ios.js in Resources */ 
= {isa = PBXBuildFile; fileRef = D1D6435E2681290200A22334 /* 
taler-wallet-ios.js */; };
+               D1D65B9826992E4600C1012A /* WalletBackend.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D1D65B9726992E4600C1012A /* WalletBackend.swift 
*/; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -55,7 +54,21 @@
                };
 /* End PBXContainerItemProxy section */
 
+/* Begin PBXCopyFilesBuildPhase section */
+               D11250FA26B12D4400D02E00 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 12;
+                       dstPath = "";
+                       dstSubfolderSpec = 7;
+                       files = (
+                               D112510026B12E3200D02E00 /* 
taler-wallet-embedded.js in CopyFiles */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXCopyFilesBuildPhase section */
+
 /* Begin PBXFileReference section */
+               D11250FF26B12E3200D02E00 /* taler-wallet-embedded.js */ = {isa 
= PBXFileReference; lastKnownFileType = sourcecode.javascript; path = 
"taler-wallet-embedded.js"; sourceTree = "<group>"; };
                D11DB44E25A5C487009CF0BC /* libnode.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libnode.a; path = 
"nodejs-mobile/out/Release/libnode.a"; sourceTree = "<group>"; };
                D11DB45625A5C5C7009CF0BC /* libv8_initializers.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libv8_initializers.a; 
path = "nodejs-mobile/out/Release/libv8_initializers.a"; sourceTree = 
"<group>"; };
                D11DB45725A5C5C7009CF0BC /* libv8_compiler.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libv8_compiler.a; path 
= "nodejs-mobile/out/Release/libv8_compiler.a"; sourceTree = "<group>"; };
@@ -135,7 +148,7 @@
                D17D8B5725ADB130001BD43D /* libtorque_base.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libtorque_base.a; path 
= "ios-node-v8/taler-ios-build/compiled/node-arm64/libtorque_base.a"; 
sourceTree = "<group>"; };
                D1AB963B259EB13D00DEAB23 /* libnode.89.dylib */ = {isa = 
PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = 
libnode.89.dylib; path = 
"ios-node-v8/taler-ios-build/compiled/x64-v8a/libnode.89.dylib"; sourceTree = 
"<group>"; };
                D1AFF0F2268D59A500FBB744 /* libiono.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libiono.a; path = 
iono/compiled/x64/libiono.a; sourceTree = "<group>"; };
-               D1D6435E2681290200A22334 /* taler-wallet-ios.js */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; 
path = "taler-wallet-ios.js"; sourceTree = "<group>"; };
+               D1D65B9726992E4600C1012A /* WalletBackend.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
WalletBackend.swift; sourceTree = "<group>"; };
                D1F0C22F25A958AE00C3179D /* libllhttp.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libllhttp.a; path = 
"ios-node-v8/tools/ios-framework/bin/x64/libllhttp.a"; sourceTree = "<group>"; 
};
                D1F0C23025A958AE00C3179D /* libv8_initializers.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libv8_initializers.a; 
path = "ios-node-v8/tools/ios-framework/bin/x64/libv8_initializers.a"; 
sourceTree = "<group>"; };
                D1F0C23125A958AE00C3179D /* libuv.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libuv.a; path = 
"ios-node-v8/tools/ios-framework/bin/x64/libuv.a"; sourceTree = "<group>"; };
@@ -228,6 +241,7 @@
                D14AFD1424D232B300C51073 = {
                        isa = PBXGroup;
                        children = (
+                               D11250FF26B12E3200D02E00 /* 
taler-wallet-embedded.js */,
                                D14AFD1F24D232B300C51073 /* Taler */,
                                D14AFD3624D232B500C51073 /* TalerTests */,
                                D14AFD4124D232B500C51073 /* TalerUITests */,
@@ -249,9 +263,9 @@
                D14AFD1F24D232B300C51073 /* Taler */ = {
                        isa = PBXGroup;
                        children = (
-                               D1D6435E2681290200A22334 /* taler-wallet-ios.js 
*/,
                                D14AFD2024D232B300C51073 /* AppDelegate.swift 
*/,
                                D14AFD2224D232B300C51073 /* SceneDelegate.swift 
*/,
+                               D1D65B9726992E4600C1012A /* WalletBackend.swift 
*/,
                                D14AFD2424D232B300C51073 /* ContentView.swift 
*/,
                                D14AFD2624D232B500C51073 /* Assets.xcassets */,
                                D14AFD2B24D232B500C51073 /* 
LaunchScreen.storyboard */,
@@ -401,6 +415,8 @@
                        buildPhases = (
                                D14AFD1924D232B300C51073 /* Sources */,
                                D14AFD1A24D232B300C51073 /* Frameworks */,
+                               D11250F726B12A3500D02E00 /* ShellScript */,
+                               D11250FA26B12D4400D02E00 /* CopyFiles */,
                                D14AFD1B24D232B300C51073 /* Resources */,
                        );
                        buildRules = (
@@ -419,6 +435,7 @@
                                D14AFD2F24D232B500C51073 /* Sources */,
                                D14AFD3024D232B500C51073 /* Frameworks */,
                                D14AFD3124D232B500C51073 /* Resources */,
+                               D11250F626B1278700D02E00 /* Run Script */,
                        );
                        buildRules = (
                        );
@@ -497,9 +514,6 @@
                        isa = PBXResourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               D14AFD2D24D232B500C51073 /* 
LaunchScreen.storyboard in Resources */,
-                               D1D6435F2681290200A22334 /* taler-wallet-ios.js 
in Resources */,
-                               D14AFD2724D232B500C51073 /* Assets.xcassets in 
Resources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
@@ -519,6 +533,47 @@
                };
 /* End PBXResourcesBuildPhase section */
 
+/* Begin PBXShellScriptBuildPhase section */
+               D11250F626B1278700D02E00 /* Run Script */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       alwaysOutOfDate = 1;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputFileListPaths = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "Run Script";
+                       outputFileListPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "
+";
+               };
+               D11250F726B12A3500D02E00 /* ShellScript */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputFileListPaths = (
+                       );
+                       inputPaths = (
+                       );
+                       outputFileListPaths = (
+                       );
+                       outputPaths = (
+                               "$(SRCROOT)/taler-wallet-embedded.js",
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = 
"WALLET_CORE_VERSION=\"v0.8.1\"\nWALLET_CORE_HASH=\"23bf89b663f0fd0e84a3d7e54a19766766c7306e5704e43a25df57da72056fa7\"\nWALLET_SRC=\"https://git.taler.net/wallet-core.git/plain/${WALLET_CORE_VERSION}/taler-wallet-embedded.js?h=prebuilt\"\nWALLET_DST=\"${SRCROOT}/taler-wallet-embedded.js\"\n\n[
 ! -e $WALLET_DST ] || rm $WALLET_DST\ncurl $WALLET_SRC --output 
$WALLET_DST\n\nRECEIVED_HASH=$(openssl sha256 -r 
$WALLET_DST)\nRECEIVED_HASH_SPLIT=($RECEIVED_HASH)\nif [ $WALLET_CO [...]
+               };
+/* End PBXShellScriptBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
                D14AFD1924D232B300C51073 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
@@ -527,6 +582,7 @@
                                D14AFD2124D232B300C51073 /* AppDelegate.swift 
in Sources */,
                                D14AFD2324D232B300C51073 /* SceneDelegate.swift 
in Sources */,
                                D14AFD2524D232B300C51073 /* ContentView.swift 
in Sources */,
+                               D1D65B9826992E4600C1012A /* WalletBackend.swift 
in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
@@ -698,6 +754,7 @@
                                CLANG_ENABLE_MODULES = YES;
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
+                               CURRENT_PROJECT_VERSION = 2;
                                DEVELOPMENT_ASSET_PATHS = "\"Taler/Preview 
Content\"";
                                DEVELOPMENT_TEAM = AY2R7MK22Y;
                                ENABLE_PREVIEWS = YES;
@@ -747,6 +804,7 @@
                                CLANG_ENABLE_MODULES = YES;
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
+                               CURRENT_PROJECT_VERSION = 2;
                                DEVELOPMENT_ASSET_PATHS = "\"Taler/Preview 
Content\"";
                                DEVELOPMENT_TEAM = AY2R7MK22Y;
                                ENABLE_PREVIEWS = YES;
diff --git a/Taler/AppDelegate.swift b/Taler/AppDelegate.swift
index 075bf73..93fc474 100644
--- a/Taler/AppDelegate.swift
+++ b/Taler/AppDelegate.swift
@@ -24,19 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 
     func application(_ application: UIApplication, 
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: 
Any]?) -> Bool {
         // Override point for customization after application launch.
-        // TODO: generate 'taler-wallet-ios.js' from wallet-core instead of 
pasting it in.
-        let js_path = URL(fileURLWithPath: Bundle.main.path(forResource: 
"taler-wallet-ios", ofType: "js")!)
-        let nodeInstance = Iono()
-        do {
-            let js = try String(contentsOf: js_path, encoding: .utf8)
-            nodeInstance.putModuleCode(modName: "@gnu-taler/taler-wallet-ios", 
code: js)
-            nodeInstance.evalNodeCode(source: "require('iono');")
-            nodeInstance.evalNodeCode(source: "tw = 
require('@gnu-taler/taler-wallet-ios');")
-            nodeInstance.evalNodeCode(source: "tw.installIosWalletListener();")
-        } catch {
-
-        }
-        
+        let backend = WalletBackend()
         return true
     }
 
diff --git a/Taler/Info.plist b/Taler/Info.plist
index 9742bf0..2e34cc0 100644
--- a/Taler/Info.plist
+++ b/Taler/Info.plist
@@ -17,7 +17,7 @@
        <key>CFBundleShortVersionString</key>
        <string>1.0</string>
        <key>CFBundleVersion</key>
-       <string>1</string>
+       <string>$(CURRENT_PROJECT_VERSION)</string>
        <key>LSRequiresIPhoneOS</key>
        <true/>
        <key>UIApplicationSceneManifest</key>
diff --git a/Taler/WalletBackend.swift b/Taler/WalletBackend.swift
new file mode 100644
index 0000000..5a28371
--- /dev/null
+++ b/Taler/WalletBackend.swift
@@ -0,0 +1,130 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2021 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under 
the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+ * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import Foundation
+import iono
+
+protocol WalletBackendRequest {
+    associatedtype Args: Encodable
+    
+    func operation() -> String
+    func args() -> Args
+}
+
+fileprivate struct WalletBackendRequestData<T: WalletBackendRequest>: 
Encodable {
+    var operation: String
+    var id: Int
+    var args: T.Args
+    
+    init(request: T, id: Int) {
+        operation = request.operation()
+        self.id = id
+        args = request.args()
+    }
+}
+
+fileprivate struct WalletBackendInitRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var persistentStoragePath: String
+    }
+    typealias Args = RequestArgs
+    private var requestArgs: RequestArgs
+    
+    init(persistentStoragePath: String) {
+        requestArgs = RequestArgs(persistentStoragePath: persistentStoragePath)
+    }
+    
+    func operation() -> String {
+        return "init"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+}
+
+fileprivate struct WalletBackendGetTransactionsRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        
+    }
+    typealias Args = RequestArgs
+    private var requestArgs: RequestArgs
+    
+    init() {
+        requestArgs = RequestArgs()
+    }
+    
+    func operation() -> String {
+        return "getTransactions"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+}
+
+enum WalletBackendError: Error {
+    case serializationError
+}
+
+class WalletBackend: IonoMessageHandler {
+    private var iono: Iono
+    private var requestsMade: Int
+    
+    init() {
+        iono = Iono()
+        requestsMade = 0
+        
+        iono.messageHandler = self
+        
+        let js_path = URL(fileURLWithPath: Bundle.main.path(forResource: 
"taler-wallet-embedded", ofType: "js")!)
+        do {
+            let js = try String(contentsOf: js_path, encoding: .utf8)
+            iono.putModuleCode(modName: "@gnu-taler/taler-wallet-embedded", 
code: js)
+            iono.evalNodeCode(source: "require('iono');")
+            iono.evalNodeCode(source: "tw = 
require('@gnu-taler/taler-wallet-embedded');")
+            iono.evalNodeCode(source: "tw.installNativeWalletListener();")
+        } catch {
+
+        }
+        
+        // Send the init message
+        let documentUrls = FileManager.default.urls(for: .documentDirectory, 
in: .userDomainMask)
+        if (documentUrls.count > 0) {
+            var storageDir = documentUrls[0]
+            storageDir.appendPathComponent("talerwalletdb-v30", isDirectory: 
false)
+            storageDir.appendPathExtension("json")
+            try! sendRequest(request: 
WalletBackendInitRequest(persistentStoragePath: storageDir.path))
+        }
+        //try! sendRequest(request: WalletBackendGetTransactionsRequest())
+    }
+    
+    func handleMessage(message: String) {
+        print("received message \(message)")
+    }
+    
+    func sendRequest<T: WalletBackendRequest>(request: T) throws {
+        let data = WalletBackendRequestData<T>(request: request, id: 
requestsMade)
+        requestsMade += 1
+        do {
+            let encoded = try JSONEncoder().encode(data)
+            guard let jsonString = String(data: encoded, encoding: .utf8) else 
{ throw WalletBackendError.serializationError }
+            iono.sendMessage(message: jsonString)
+        } catch {
+            throw WalletBackendError.serializationError
+        }
+    }
+}
diff --git a/Taler/iono.js b/Taler/iono.js
deleted file mode 100644
index 5d4e84d..0000000
--- a/Taler/iono.js
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2021 Taler Systems SA
-//
-// This file is part of GNU Taler.
-//
-// GNU Taler is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation, either version 3 of the License, or (at your option)
-// any later version.
-//
-// GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-// FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
-// more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with GNU.  If not, see <https://www.gnu.org/licenses/>.
-
-
-'use strict';
-
-const iono = internalBinding("iono");
-
-const mod = require('module');
-mod._saved_findPath = mod._findPath;
-mod._ionoMods = {};
-mod._findPath = (request, paths, isMain) => {
-  const res = mod._saved_findPath(request, paths, isMain);
-  if (res !== false) return res;
-  const loadResult = iono.getModuleCode(request);
-  if (!loadResult) return false;
-  const p = `/vmod/${request}`;
-  mod._ionoMods[p] = loadResult;
-  return p;
-};
-
-function stripBOM(content) {
-  if (content.charCodeAt(0) === 0xFEFF) {
-    content = content.slice(1);
-  }
-  return content;
-}
-
-mod._saved_js_extension = mod._extensions[".js"];
-mod._extensions[".js"] = (module, filename) => {
-  if (mod._ionoMods.hasOwnProperty(filename)) {
-    const imod = mod._ionoMods[filename];
-    const content = imod;
-    module._compile(stripBOM(content), filename);
-    return;
-  }
-  return mod._saved_js_extension(module, filename);
-};
-
-mod._saved_json_extension = mod._extensions[".json"];
-mod._extensions[".json"] = (module, filename) => {
-  if (mod._ionoMods.hasOwnProperty(filename)) {
-    const imod = mod._ionoMods[filename];
-    const content = imod;
-    try {
-      module.exports = JSON.parse(stripBOM(content));
-      return;
-    } catch (err) {
-      err.message = filename + ': ' + err.message;
-      throw err;
-    }
-  }
-  return mod._saved_json_extension(module, filename);
-};
-
diff --git a/Taler/taler-wallet-ios.js b/Taler/taler-wallet-ios.js
deleted file mode 100644
index 7d8ad90..0000000
--- a/Taler/taler-wallet-ios.js
+++ /dev/null
@@ -1,31483 +0,0 @@
-'use strict';
-
-Object.defineProperty(exports, '__esModule', { value: true });
-
-var http = require('http');
-var https = require('https');
-var url = require('url');
-var require$$0 = require('stream');
-var assert = require('assert');
-var tty = require('tty');
-var util = require('util');
-var os = require('os');
-var zlib = require('zlib');
-var cr = require('crypto');
-var fs = require('fs');
-
-function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 
'default' in e ? e : { 'default': e }; }
-
-var http__default = /*#__PURE__*/_interopDefaultLegacy(http);
-var https__default = /*#__PURE__*/_interopDefaultLegacy(https);
-var url__default = /*#__PURE__*/_interopDefaultLegacy(url);
-var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0);
-var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
-var tty__default = /*#__PURE__*/_interopDefaultLegacy(tty);
-var util__default = /*#__PURE__*/_interopDefaultLegacy(util);
-var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
-var zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib);
-var cr__default = /*#__PURE__*/_interopDefaultLegacy(cr);
-var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
-
-/*! 
*****************************************************************************
-Copyright (c) Microsoft Corporation.
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-PERFORMANCE OF THIS SOFTWARE.
-***************************************************************************** 
*/
-
-function __awaiter$1(thisArg, _arguments, P, generator) {
-    function adopt(value) { return value instanceof P ? value : new P(function 
(resolve) { resolve(value); }); }
-    return new (P || (P = Promise))(function (resolve, reject) {
-        function fulfilled(value) { try { step(generator.next(value)); } catch 
(e) { reject(e); } }
-        function rejected(value) { try { step(generator["throw"](value)); } 
catch (e) { reject(e); } }
-        function step(result) { result.done ? resolve(result.value) : 
adopt(result.value).then(fulfilled, rejected); }
-        step((generator = generator.apply(thisArg, _arguments || [])).next());
-    });
-}
-
-/*
-  This file is part of GNU Taler
-  Copyright (C) 2012-2020 Taler Systems SA
-
-  GNU Taler is free software: you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation, either version 3 of the License,
-  or (at your option) any later version.
-
-  GNU Taler is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-  SPDX-License-Identifier: LGPL3.0-or-later
-
-  Note: the LGPL does not apply to all components of GNU Taler,
-  but it does apply to this file.
- */
-var TalerErrorCode;
-(function (TalerErrorCode) {
-    /**
-     * Special code to indicate success (no error).
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["NONE"] = 0] = "NONE";
-    /**
-     * A non-integer error code was returned in the JSON response.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["INVALID"] = 1] = "INVALID";
-    /**
-     * The response we got from the server was not even in JSON format.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_INVALID_RESPONSE"] = 10] = 
"GENERIC_INVALID_RESPONSE";
-    /**
-     * An operation timed out.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_TIMEOUT"] = 11] = "GENERIC_TIMEOUT";
-    /**
-     * The version string given does not follow the expected 
CURRENT:REVISION:AGE Format.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_VERSION_MALFORMED"] = 12] = 
"GENERIC_VERSION_MALFORMED";
-    /**
-     * The service responded with a reply that was in JSON but did not satsify 
the protocol. Note that invalid cryptographic signatures should have 
signature-specific error codes.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_REPLY_MALFORMED"] = 13] = 
"GENERIC_REPLY_MALFORMED";
-    /**
-     * There is an error in the client-side configuration, for example the 
base URL specified is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_CONFIGURATION_INVALID"] = 14] = 
"GENERIC_CONFIGURATION_INVALID";
-    /**
-     * The HTTP method used is invalid for this endpoint.
-     * Returned with an HTTP status code of #MHD_HTTP_METHOD_NOT_ALLOWED (405).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_METHOD_INVALID"] = 20] = 
"GENERIC_METHOD_INVALID";
-    /**
-     * There is no endpoint defined for the URL provided by the client.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_ENDPOINT_UNKNOWN"] = 21] = 
"GENERIC_ENDPOINT_UNKNOWN";
-    /**
-     * The JSON in the client's request was malformed (generic parse error).
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_JSON_INVALID"] = 22] = 
"GENERIC_JSON_INVALID";
-    /**
-     * The payto:// URI provided by the client is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_PAYTO_URI_MALFORMED"] = 24] = 
"GENERIC_PAYTO_URI_MALFORMED";
-    /**
-     * A required parameter in the request was missing.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_PARAMETER_MISSING"] = 25] = 
"GENERIC_PARAMETER_MISSING";
-    /**
-     * A parameter in the request was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_PARAMETER_MALFORMED"] = 26] = 
"GENERIC_PARAMETER_MALFORMED";
-    /**
-     * The currencies involved in the operation do not match.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_CURRENCY_MISMATCH"] = 30] = 
"GENERIC_CURRENCY_MISMATCH";
-    /**
-     * The URI is longer than the longest URI the HTTP server is willing to 
parse.
-     * Returned with an HTTP status code of #MHD_HTTP_URI_TOO_LONG (414).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_URI_TOO_LONG"] = 31] = 
"GENERIC_URI_TOO_LONG";
-    /**
-     * The body is too large to be permissible for the endpoint.
-     * Returned with an HTTP status code of #MHD_HTTP_PAYLOAD_TOO_LARGE (413).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_UPLOAD_EXCEEDS_LIMIT"] = 32] = 
"GENERIC_UPLOAD_EXCEEDS_LIMIT";
-    /**
-     * The service failed initialize its connection to the database.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_DB_SETUP_FAILED"] = 50] = 
"GENERIC_DB_SETUP_FAILED";
-    /**
-     * The service encountered an error event to just start the database 
transaction.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_DB_START_FAILED"] = 51] = 
"GENERIC_DB_START_FAILED";
-    /**
-     * The service failed to store information in its database.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_DB_STORE_FAILED"] = 52] = 
"GENERIC_DB_STORE_FAILED";
-    /**
-     * The service failed to fetch information from its database.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_DB_FETCH_FAILED"] = 53] = 
"GENERIC_DB_FETCH_FAILED";
-    /**
-     * The service encountered an error event to commit the database 
transaction (hard, unrecoverable error).
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_DB_COMMIT_FAILED"] = 54] = 
"GENERIC_DB_COMMIT_FAILED";
-    /**
-     * The service encountered an error event to commit the database 
transaction, even after repeatedly retrying it there was always a conflicting 
transaction. (This indicates a repeated serialization error; should only happen 
if some client maliciously tries to create conflicting concurrent transactions.)
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_DB_SOFT_FAILURE"] = 55] = 
"GENERIC_DB_SOFT_FAILURE";
-    /**
-     * The service's database is inconsistent and violates service-internal 
invariants.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_DB_INVARIANT_FAILURE"] = 56] = 
"GENERIC_DB_INVARIANT_FAILURE";
-    /**
-     * The HTTP server experienced an internal invariant failure (bug).
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_INTERNAL_INVARIANT_FAILURE"] = 60] 
= "GENERIC_INTERNAL_INVARIANT_FAILURE";
-    /**
-     * The service could not compute a cryptographic hash over some JSON value.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_FAILED_COMPUTE_JSON_HASH"] = 61] = 
"GENERIC_FAILED_COMPUTE_JSON_HASH";
-    /**
-     * The HTTP server had insufficient memory to parse the request.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_PARSER_OUT_OF_MEMORY"] = 70] = 
"GENERIC_PARSER_OUT_OF_MEMORY";
-    /**
-     * The HTTP server failed to allocate memory.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_ALLOCATION_FAILURE"] = 71] = 
"GENERIC_ALLOCATION_FAILURE";
-    /**
-     * The HTTP server failed to allocate memory for building JSON reply.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["GENERIC_JSON_ALLOCATION_FAILURE"] = 72] = 
"GENERIC_JSON_ALLOCATION_FAILURE";
-    /**
-     * Exchange is badly configured and thus cannot operate.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_BAD_CONFIGURATION"] = 
1000] = "EXCHANGE_GENERIC_BAD_CONFIGURATION";
-    /**
-     * Operation specified unknown for this endpoint.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_OPERATION_UNKNOWN"] = 
1001] = "EXCHANGE_GENERIC_OPERATION_UNKNOWN";
-    /**
-     * The number of segments included in the URI does not match the number of 
segments expected by the endpoint.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS"] 
= 1002] = "EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS";
-    /**
-     * The same coin was already used with a different denomination previously.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY"]
 = 1003] = "EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY";
-    /**
-     * The public key of given to a "/coins/" endpoint of the exchange was 
malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB"] = 
1004] = "EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB";
-    /**
-     * The exchange is not aware of the denomination key the wallet requested 
for the operation.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN"] 
= 1005] = "EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN";
-    /**
-     * The signature of the denomination key over the coin is not valid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DENOMINATION_SIGNATURE_INVALID"] = 
1006] = "EXCHANGE_DENOMINATION_SIGNATURE_INVALID";
-    /**
-     * The exchange failed to perform the operation as it could not find the 
private keys. This is a problem with the exchange setup, not with the client's 
request.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_KEYS_MISSING"] = 1007] = 
"EXCHANGE_GENERIC_KEYS_MISSING";
-    /**
-     * Validity period of the denomination lies in the future.
-     * Returned with an HTTP status code of #MHD_HTTP_PRECONDITION_FAILED 
(412).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE"]
 = 1008] = "EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE";
-    /**
-     * Denomination key of the coin is past its expiration time for the 
requested operation.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_DENOMINATION_EXPIRED"] = 
1009] = "EXCHANGE_GENERIC_DENOMINATION_EXPIRED";
-    /**
-     * Denomination key of the coin has been revoked.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_GENERIC_DENOMINATION_REVOKED"] = 
1010] = "EXCHANGE_GENERIC_DENOMINATION_REVOKED";
-    /**
-     * The exchange did not find information about the specified transaction 
in the database.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSITS_GET_NOT_FOUND"] = 1100] = 
"EXCHANGE_DEPOSITS_GET_NOT_FOUND";
-    /**
-     * The wire hash of given to a "/deposits/" handler was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSITS_GET_INVALID_H_WIRE"] = 
1101] = "EXCHANGE_DEPOSITS_GET_INVALID_H_WIRE";
-    /**
-     * The merchant key of given to a "/deposits/" handler was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSITS_GET_INVALID_MERCHANT_PUB"] = 
1102] = "EXCHANGE_DEPOSITS_GET_INVALID_MERCHANT_PUB";
-    /**
-     * The hash of the contract terms given to a "/deposits/" handler was 
malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSITS_GET_INVALID_H_CONTRACT_TERMS"] 
= 1103] = "EXCHANGE_DEPOSITS_GET_INVALID_H_CONTRACT_TERMS";
-    /**
-     * The coin public key of given to a "/deposits/" handler was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSITS_GET_INVALID_COIN_PUB"] = 
1104] = "EXCHANGE_DEPOSITS_GET_INVALID_COIN_PUB";
-    /**
-     * The signature returned by the exchange in a /deposits/ request was 
malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE"]
 = 1105] = "EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE";
-    /**
-     * The signature of the merchant is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSITS_GET_MERCHANT_SIGNATURE_INVALID"]
 = 1106] = "EXCHANGE_DEPOSITS_GET_MERCHANT_SIGNATURE_INVALID";
-    /**
-     * The given reserve does not have sufficient funds to admit the requested 
withdraw operation at this time.  The response includes the current "balance" 
of the reserve as well as the transaction "history" that lead to this balance.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS"] = 
1150] = "EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS";
-    /**
-     * The exchange has no information about the "reserve_pub" that was given.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_WITHDRAW_RESERVE_UNKNOWN"] = 1151] 
= "EXCHANGE_WITHDRAW_RESERVE_UNKNOWN";
-    /**
-     * The amount to withdraw together with the fee exceeds the numeric range 
for Taler amounts.  This is not a client failure, as the coin value and fees 
come from the exchange's configuration.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW"] = 
1152] = "EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW";
-    /**
-     * The exchange failed to create the signature using the denomination key.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_WITHDRAW_SIGNATURE_FAILED"] = 
1153] = "EXCHANGE_WITHDRAW_SIGNATURE_FAILED";
-    /**
-     * The signature of the reserve is not valid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID"] = 
1154] = "EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID";
-    /**
-     * When computing the reserve history, we ended up with a negative overall 
balance, which should be impossible.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_WITHDRAW_HISTORY_ERROR_INSUFFICIENT_FUNDS"]
 = 1155] = "EXCHANGE_WITHDRAW_HISTORY_ERROR_INSUFFICIENT_FUNDS";
-    /**
-     * Withdraw period of the coin to be withdrawn is in the past.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_WITHDRAW_DENOMINATION_KEY_LOST"] = 
1158] = "EXCHANGE_WITHDRAW_DENOMINATION_KEY_LOST";
-    /**
-     * The client failed to unblind the blind signature.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_WITHDRAW_UNBLIND_FAILURE"] = 1159] 
= "EXCHANGE_WITHDRAW_UNBLIND_FAILURE";
-    /**
-     * The respective coin did not have sufficient residual value for the 
/deposit operation (i.e. due to double spending). The "history" in the response 
provides the transaction history of the coin proving this fact.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS"] = 
1200] = "EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS";
-    /**
-     * The signature made by the coin over the deposit permission is not valid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID"] = 
1205] = "EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID";
-    /**
-     * The stated value of the coin after the deposit fee is subtracted would 
be negative.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE"] 
= 1207] = "EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE";
-    /**
-     * The stated refund deadline is after the wire deadline.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE"]
 = 1208] = "EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE";
-    /**
-     * The exchange failed to canonicalize and hash the given wire format. For 
example, the merchant failed to provide the "salt" or a valid payto:// URI in 
the wire details.  Note that while the exchange will do some basic sanity 
checking on the wire details, it cannot warrant that the banking system will 
ultimately be able to route to the specified address, even if this check passed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSIT_INVALID_WIRE_FORMAT_JSON"] 
= 1210] = "EXCHANGE_DEPOSIT_INVALID_WIRE_FORMAT_JSON";
-    /**
-     * The hash of the given wire address does not match the wire hash 
specified in the proposal data.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSIT_INVALID_WIRE_FORMAT_CONTRACT_HASH_CONFLICT"]
 = 1211] = "EXCHANGE_DEPOSIT_INVALID_WIRE_FORMAT_CONTRACT_HASH_CONFLICT";
-    /**
-     * The signature provided by the exchange is not valid.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE"] 
= 1221] = "EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE";
-    /**
-     * The reserve status was requested using a unknown key.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_RESERVES_GET_STATUS_UNKNOWN"] = 
1250] = "EXCHANGE_RESERVES_GET_STATUS_UNKNOWN";
-    /**
-     * The respective coin did not have sufficient residual value for the 
/refresh/melt operation.  The "history" in this response provdes the 
"residual_value" of the coin, which may be less than its "original_value".
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_MELT_INSUFFICIENT_FUNDS"] = 1300] 
= "EXCHANGE_MELT_INSUFFICIENT_FUNDS";
-    /**
-     * The exchange had an internal error reconstructing the transaction 
history of the coin that was being melted.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MELT_COIN_HISTORY_COMPUTATION_FAILED"] 
= 1301] = "EXCHANGE_MELT_COIN_HISTORY_COMPUTATION_FAILED";
-    /**
-     * The exchange encountered melt fees exceeding the melted coin's 
contribution.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION"] = 
1302] = "EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION";
-    /**
-     * The signature made with the coin to be melted is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_MELT_COIN_SIGNATURE_INVALID"] = 
1303] = "EXCHANGE_MELT_COIN_SIGNATURE_INVALID";
-    /**
-     * The exchange failed to obtain the transaction history of the given coin 
from the database while generating an insufficient funds errors.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS"]
 = 1304] = "EXCHANGE_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS";
-    /**
-     * The denomination of the given coin has past its expiration date and it 
is also not a valid zombie (that is, was not refreshed with the fresh coin 
being subjected to recoup).
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE"] = 
1305] = "EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE";
-    /**
-     * The signature returned by the exchange in a melt request was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE"] = 
1306] = "EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE";
-    /**
-     * The provided transfer keys do not match up with the original 
commitment.  Information about the original commitment is included in the 
response.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_COMMITMENT_VIOLATION"] 
= 1353] = "EXCHANGE_REFRESHES_REVEAL_COMMITMENT_VIOLATION";
-    /**
-     * Failed to produce the blinded signatures over the coins to be returned.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_SIGNING_ERROR"] = 
1354] = "EXCHANGE_REFRESHES_REVEAL_SIGNING_ERROR";
-    /**
-     * The exchange is unaware of the refresh session specified in the request.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN"] 
= 1355] = "EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN";
-    /**
-     * The size of the cut-and-choose dimension of the private transfer keys 
request does not match #TALER_CNC_KAPPA - 1.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID"]
 = 1356] = "EXCHANGE_REFRESHES_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID";
-    /**
-     * The number of coins to be created in refresh exceeds the limits of the 
exchange. private transfer keys request does not match #TALER_CNC_KAPPA - 1.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE"]
 = 1357] = "EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE";
-    /**
-     * The number of envelopes given does not match the number of denomination 
keys given.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISMATCH"]
 = 1358] = "EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISMATCH";
-    /**
-     * The exchange encountered a numeric overflow totaling up the cost for 
the refresh operation.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW"]
 = 1359] = "EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW";
-    /**
-     * The exchange's cost calculation shows that the melt amount is below the 
costs of the transaction.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_AMOUNT_INSUFFICIENT"] 
= 1360] = "EXCHANGE_REFRESHES_REVEAL_AMOUNT_INSUFFICIENT";
-    /**
-     * The signature made with the coin over the link data is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_LINK_SIGNATURE_INVALID"]
 = 1361] = "EXCHANGE_REFRESHES_REVEAL_LINK_SIGNATURE_INVALID";
-    /**
-     * The refresh session hash given to a /refreshes/ handler was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_INVALID_RCH"] = 
1362] = "EXCHANGE_REFRESHES_REVEAL_INVALID_RCH";
-    /**
-     * Operation specified invalid for this endpoint.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFRESHES_REVEAL_OPERATION_INVALID"] = 
1363] = "EXCHANGE_REFRESHES_REVEAL_OPERATION_INVALID";
-    /**
-     * The coin specified in the link request is unknown to the exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_LINK_COIN_UNKNOWN"] = 1400] = 
"EXCHANGE_LINK_COIN_UNKNOWN";
-    /**
-     * The public key of given to a /transfers/ handler was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_TRANSFERS_GET_WTID_MALFORMED"] = 
1450] = "EXCHANGE_TRANSFERS_GET_WTID_MALFORMED";
-    /**
-     * The exchange did not find information about the specified wire transfer 
identifier in the database.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_TRANSFERS_GET_WTID_NOT_FOUND"] = 
1451] = "EXCHANGE_TRANSFERS_GET_WTID_NOT_FOUND";
-    /**
-     * The exchange did not find information about the wire transfer fees it 
charged.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_TRANSFERS_GET_WIRE_FEE_NOT_FOUND"] 
= 1452] = "EXCHANGE_TRANSFERS_GET_WIRE_FEE_NOT_FOUND";
-    /**
-     * The exchange found a wire fee that was above the total transfer value 
(and thus could not have been charged).
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_TRANSFERS_GET_WIRE_FEE_INCONSISTENT"] = 
1453] = "EXCHANGE_TRANSFERS_GET_WIRE_FEE_INCONSISTENT";
-    /**
-     * The exchange knows literally nothing about the coin we were asked to 
refund. But without a transaction history, we cannot issue a refund. This is 
kind-of OK, the owner should just refresh it directly without executing the 
refund.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_COIN_NOT_FOUND"] = 1500] = 
"EXCHANGE_REFUND_COIN_NOT_FOUND";
-    /**
-     * We could not process the refund request as the coin's transaction 
history does not permit the requested refund because then refunds would exceed 
the deposit amount.  The "history" in the response proves this.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_CONFLICT_DEPOSIT_INSUFFICIENT"] 
= 1501] = "EXCHANGE_REFUND_CONFLICT_DEPOSIT_INSUFFICIENT";
-    /**
-     * The exchange knows about the coin we were asked to refund, but not 
about the specific /deposit operation.  Hence, we cannot issue a refund (as we 
do not know if this merchant public key is authorized to do a refund).
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_DEPOSIT_NOT_FOUND"] = 1502] 
= "EXCHANGE_REFUND_DEPOSIT_NOT_FOUND";
-    /**
-     * The exchange can no longer refund the customer/coin as the money was 
already transferred (paid out) to the merchant. (It should be past the refund 
deadline.)
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_MERCHANT_ALREADY_PAID"] = 
1503] = "EXCHANGE_REFUND_MERCHANT_ALREADY_PAID";
-    /**
-     * The refund fee specified for the request is lower than the refund fee 
charged by the exchange for the given denomination key of the refunded coin.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_FEE_TOO_LOW"] = 1504] = 
"EXCHANGE_REFUND_FEE_TOO_LOW";
-    /**
-     * The refunded amount is smaller than the refund fee, which would result 
in a negative refund.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_FEE_ABOVE_AMOUNT"] = 1505] 
= "EXCHANGE_REFUND_FEE_ABOVE_AMOUNT";
-    /**
-     * The signature of the merchant is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_MERCHANT_SIGNATURE_INVALID"] = 
1506] = "EXCHANGE_REFUND_MERCHANT_SIGNATURE_INVALID";
-    /**
-     * Merchant backend failed to create the refund confirmation signature.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_MERCHANT_SIGNING_FAILED"] = 
1507] = "EXCHANGE_REFUND_MERCHANT_SIGNING_FAILED";
-    /**
-     * The signature returned by the exchange in a refund request was 
malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_INVALID_SIGNATURE_BY_EXCHANGE"] 
= 1508] = "EXCHANGE_REFUND_INVALID_SIGNATURE_BY_EXCHANGE";
-    /**
-     * The failure proof returned by the exchange is incorrect.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE"]
 = 1509] = "EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE";
-    /**
-     * Conflicting refund granted before with different amount but same refund 
transaction ID.
-     * Returned with an HTTP status code of #MHD_HTTP_FAILED_DEPENDENCY (424).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_REFUND_INCONSISTENT_AMOUNT"] = 
1510] = "EXCHANGE_REFUND_INCONSISTENT_AMOUNT";
-    /**
-     * The given coin signature is invalid for the request.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_RECOUP_SIGNATURE_INVALID"] = 1550] 
= "EXCHANGE_RECOUP_SIGNATURE_INVALID";
-    /**
-     * The exchange could not find the corresponding withdraw operation. The 
request is denied.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_RECOUP_WITHDRAW_NOT_FOUND"] = 
1551] = "EXCHANGE_RECOUP_WITHDRAW_NOT_FOUND";
-    /**
-     * The coin's remaining balance is zero.  The request is denied.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_RECOUP_COIN_BALANCE_ZERO"] = 1552] 
= "EXCHANGE_RECOUP_COIN_BALANCE_ZERO";
-    /**
-     * The exchange failed to reproduce the coin's blinding.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_RECOUP_BLINDING_FAILED"] = 1553] = 
"EXCHANGE_RECOUP_BLINDING_FAILED";
-    /**
-     * The coin's remaining balance is zero.  The request is denied.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_RECOUP_COIN_BALANCE_NEGATIVE"] = 
1554] = "EXCHANGE_RECOUP_COIN_BALANCE_NEGATIVE";
-    /**
-     * The coin's denomination has not been revoked yet.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_RECOUP_NOT_ELIGIBLE"] = 1555] = 
"EXCHANGE_RECOUP_NOT_ELIGIBLE";
-    /**
-     * This exchange does not allow clients to request /keys for times other 
than the current (exchange) time.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_KEYS_TIMETRAVEL_FORBIDDEN"] = 
1600] = "EXCHANGE_KEYS_TIMETRAVEL_FORBIDDEN";
-    /**
-     * A signature in the server's response was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_WIRE_SIGNATURE_INVALID"] = 1650] = 
"EXCHANGE_WIRE_SIGNATURE_INVALID";
-    /**
-     * The exchange failed to talk to the process responsible for its private 
denomination keys.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE"] 
= 1700] = "EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE";
-    /**
-     * The response from the denomination key helper process was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DENOMINATION_HELPER_BUG"] = 1701] 
= "EXCHANGE_DENOMINATION_HELPER_BUG";
-    /**
-     * The helper refuses to sign with the key, because it is too early: the 
validity period has not yet started.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_DENOMINATION_HELPER_TOO_EARLY"] = 
1702] = "EXCHANGE_DENOMINATION_HELPER_TOO_EARLY";
-    /**
-     * The exchange failed to talk to the process responsible for its private 
signing keys.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE"] = 
1750] = "EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE";
-    /**
-     * The response from the online signing key helper process was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_SIGNKEY_HELPER_BUG"] = 1751] = 
"EXCHANGE_SIGNKEY_HELPER_BUG";
-    /**
-     * The helper refuses to sign with the key, because it is too early: the 
validity period has not yet started.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_SIGNKEY_HELPER_TOO_EARLY"] = 1752] 
= "EXCHANGE_SIGNKEY_HELPER_TOO_EARLY";
-    /**
-     * The auditor that was supposed to be disabled is unknown to this 
exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_AUDITOR_NOT_FOUND"] = 
1800] = "EXCHANGE_MANAGEMENT_AUDITOR_NOT_FOUND";
-    /**
-     * The exchange has a more recently signed conflicting instruction and is 
thus refusing the current change (replay detected).
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_AUDITOR_MORE_RECENT_PRESENT"]
 = 1801] = "EXCHANGE_MANAGEMENT_AUDITOR_MORE_RECENT_PRESENT";
-    /**
-     * The signature to add or enable the auditor does not validate.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_AUDITOR_ADD_SIGNATURE_INVALID"]
 = 1802] = "EXCHANGE_MANAGEMENT_AUDITOR_ADD_SIGNATURE_INVALID";
-    /**
-     * The signature to disable the auditor does not validate.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_AUDITOR_DEL_SIGNATURE_INVALID"]
 = 1803] = "EXCHANGE_MANAGEMENT_AUDITOR_DEL_SIGNATURE_INVALID";
-    /**
-     * The signature to revoke the denomination does not validate.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_DENOMINATION_REVOKE_SIGNATURE_INVALID"]
 = 1804] = "EXCHANGE_MANAGEMENT_DENOMINATION_REVOKE_SIGNATURE_INVALID";
-    /**
-     * The signature to revoke the online signing key does not validate.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_SIGNKEY_REVOKE_SIGNATURE_INVALID"]
 = 1805] = "EXCHANGE_MANAGEMENT_SIGNKEY_REVOKE_SIGNATURE_INVALID";
-    /**
-     * The exchange has a more recently signed conflicting instruction and is 
thus refusing the current change (replay detected).
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_WIRE_MORE_RECENT_PRESENT"] = 
1806] = "EXCHANGE_MANAGEMENT_WIRE_MORE_RECENT_PRESENT";
-    /**
-     * The signingkey specified is unknown to the exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN"] 
= 1807] = "EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN";
-    /**
-     * The signature to publish wire account does not validate.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_WIRE_DETAILS_SIGNATURE_INVALID"]
 = 1808] = "EXCHANGE_MANAGEMENT_WIRE_DETAILS_SIGNATURE_INVALID";
-    /**
-     * The signature to add the wire account does not validate.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_WIRE_ADD_SIGNATURE_INVALID"] 
= 1809] = "EXCHANGE_MANAGEMENT_WIRE_ADD_SIGNATURE_INVALID";
-    /**
-     * The signature to disable the wire account does not validate.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_WIRE_DEL_SIGNATURE_INVALID"] 
= 1810] = "EXCHANGE_MANAGEMENT_WIRE_DEL_SIGNATURE_INVALID";
-    /**
-     * The wire account to be disabled is unknown to the exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_WIRE_NOT_FOUND"] = 
1811] = "EXCHANGE_MANAGEMENT_WIRE_NOT_FOUND";
-    /**
-     * The signature to affirm wire fees does not validate.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_WIRE_FEE_SIGNATURE_INVALID"] 
= 1812] = "EXCHANGE_MANAGEMENT_WIRE_FEE_SIGNATURE_INVALID";
-    /**
-     * The signature conflicts with a previous signature affirming different 
fees.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_WIRE_FEE_MISMATCH"] = 
1813] = "EXCHANGE_MANAGEMENT_WIRE_FEE_MISMATCH";
-    /**
-     * The signature affirming the denomination key is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID"]
 = 1814] = "EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID";
-    /**
-     * The signature affirming the signing key is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID"]
 = 1815] = "EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID";
-    /**
-     * The auditor signature over the denomination meta data is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["EXCHANGE_AUDITORS_AUDITOR_SIGNATURE_INVALID"] = 
1900] = "EXCHANGE_AUDITORS_AUDITOR_SIGNATURE_INVALID";
-    /**
-     * The auditor that was specified is unknown to this exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_PRECONDITION_FAILED 
(412).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_AUDITORS_AUDITOR_UNKNOWN"] = 1901] 
= "EXCHANGE_AUDITORS_AUDITOR_UNKNOWN";
-    /**
-     * The auditor that was specified is no longer used by this exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["EXCHANGE_AUDITORS_AUDITOR_INACTIVE"] = 
1902] = "EXCHANGE_AUDITORS_AUDITOR_INACTIVE";
-    /**
-     * The backend could not find the merchant instance specified in the 
request.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_INSTANCE_UNKNOWN"] = 2000] 
= "MERCHANT_GENERIC_INSTANCE_UNKNOWN";
-    /**
-     * The start and end-times in the wire fee structure leave a hole. This is 
not allowed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_HOLE_IN_WIRE_FEE_STRUCTURE"] = 
2001] = "MERCHANT_GENERIC_HOLE_IN_WIRE_FEE_STRUCTURE";
-    /**
-     * The reserve key of given to a /reserves/ handler was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_RESERVE_PUB_MALFORMED"] = 
2002] = "MERCHANT_GENERIC_RESERVE_PUB_MALFORMED";
-    /**
-     * The backend could not locate a required template to generate an HTML 
reply.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_ACCEPTABLE (406).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_FAILED_TO_LOAD_TEMPLATE"] 
= 2003] = "MERCHANT_GENERIC_FAILED_TO_LOAD_TEMPLATE";
-    /**
-     * The backend could not expand the template to generate an HTML reply.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_FAILED_TO_EXPAND_TEMPLATE"] = 
2004] = "MERCHANT_GENERIC_FAILED_TO_EXPAND_TEMPLATE";
-    /**
-     * The proposal is not known to the backend.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_ORDER_UNKNOWN"] = 2005] = 
"MERCHANT_GENERIC_ORDER_UNKNOWN";
-    /**
-     * The order provided to the backend could not be completed, because a 
product to be completed via inventory data is not actually in our inventory.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_PRODUCT_UNKNOWN"] = 2006] 
= "MERCHANT_GENERIC_PRODUCT_UNKNOWN";
-    /**
-     * The tip ID is unknown.  This could happen if the tip has expired.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_TIP_ID_UNKNOWN"] = 2007] = 
"MERCHANT_GENERIC_TIP_ID_UNKNOWN";
-    /**
-     * The contract obtained from the merchant backend was malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID"] = 
2008] = "MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID";
-    /**
-     * The order we found does not match the provided contract hash.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER"]
 = 2009] = "MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER";
-    /**
-     * The exchange failed to provide a valid response to the merchant's /keys 
request.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE"] = 
2010] = "MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE";
-    /**
-     * The exchange failed to respond to the merchant on time.
-     * Returned with an HTTP status code of #MHD_HTTP_GATEWAY_TIMEOUT (504).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_EXCHANGE_TIMEOUT"] = 2011] 
= "MERCHANT_GENERIC_EXCHANGE_TIMEOUT";
-    /**
-     * The merchant failed to talk to the exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE"] 
= 2012] = "MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE";
-    /**
-     * The exchange returned a maformed response.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_EXCHANGE_REPLY_MALFORMED"] 
= 2013] = "MERCHANT_GENERIC_EXCHANGE_REPLY_MALFORMED";
-    /**
-     * The exchange returned an unexpected response status.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS"] = 
2014] = "MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS";
-    /**
-     * The merchant refused the request due to lack of authorization.
-     * Returned with an HTTP status code of #MHD_HTTP_UNAUTHORIZED (401).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_UNAUTHORIZED"] = 2015] = 
"MERCHANT_GENERIC_UNAUTHORIZED";
-    /**
-     * The merchant instance specified in the request was deleted.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GENERIC_INSTANCE_DELETED"] = 2016] 
= "MERCHANT_GENERIC_INSTANCE_DELETED";
-    /**
-     * The exchange failed to provide a valid answer to the tracking request, 
thus those details are not in the response.
-     * Returned with an HTTP status code of #MHD_HTTP_OK (200).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_GET_ORDERS_EXCHANGE_TRACKING_FAILURE"] 
= 2100] = "MERCHANT_GET_ORDERS_EXCHANGE_TRACKING_FAILURE";
-    /**
-     * The merchant backend failed to construct the request for tracking to 
the exchange, thus tracking details are not in the response.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_GET_ORDERS_ID_EXCHANGE_REQUEST_FAILURE"]
 = 2103] = "MERCHANT_GET_ORDERS_ID_EXCHANGE_REQUEST_FAILURE";
-    /**
-     * The merchant backend failed trying to contact the exchange for tracking 
details, thus those details are not in the response.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_GET_ORDERS_ID_EXCHANGE_LOOKUP_START_FAILURE"]
 = 2104] = "MERCHANT_GET_ORDERS_ID_EXCHANGE_LOOKUP_START_FAILURE";
-    /**
-     * The token used to authenticate the client is invalid for this order.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_GET_ORDERS_ID_INVALID_TOKEN"] = 
2105] = "MERCHANT_GET_ORDERS_ID_INVALID_TOKEN";
-    /**
-     * The exchange responded saying that funds were insufficient (for 
example, due to double-spending).
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS"] 
= 2150] = "MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS";
-    /**
-     * The denomination key used for payment is not listed among the 
denomination keys of the exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND"]
 = 2151] = "MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND";
-    /**
-     * The denomination key used for payment is not audited by an auditor 
approved by the merchant.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_AUDITOR_FAILURE"]
 = 2152] = "MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_AUDITOR_FAILURE";
-    /**
-     * There was an integer overflow totaling up the amounts or deposit fees 
in the payment.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW"] = 
2153] = "MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW";
-    /**
-     * The deposit fees exceed the total value of the payment.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_FEES_EXCEED_PAYMENT"]
 = 2154] = "MERCHANT_POST_ORDERS_ID_PAY_FEES_EXCEED_PAYMENT";
-    /**
-     * After considering deposit and wire fees, the payment is insufficient to 
satisfy the required amount for the contract.  The client should revisit the 
logic used to calculate fees it must cover.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_ACCEPTABLE (406).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES"]
 = 2155] = "MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES";
-    /**
-     * Even if we do not consider deposit and wire fees, the payment is 
insufficient to satisfy the required amount for the contract.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_ACCEPTABLE (406).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT"]
 = 2156] = "MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT";
-    /**
-     * The signature over the contract of one of the coins was invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_COIN_SIGNATURE_INVALID"]
 = 2157] = "MERCHANT_POST_ORDERS_ID_PAY_COIN_SIGNATURE_INVALID";
-    /**
-     * When we tried to find information about the exchange to issue the 
deposit, we failed.  This usually only happens if the merchant backend is 
somehow unable to get its own HTTP client logic to work.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LOOKUP_FAILED"]
 = 2158] = "MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LOOKUP_FAILED";
-    /**
-     * The refund deadline in the contract is after the transfer deadline.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE"]
 = 2159] = 
"MERCHANT_POST_ORDERS_ID_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE";
-    /**
-     * The payment is too late, the offer has expired.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED"] 
= 2161] = "MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED";
-    /**
-     * The "merchant" field is missing in the proposal data. This is an 
internal error as the proposal is from the merchant's own database at this 
point.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_MERCHANT_FIELD_MISSING"]
 = 2162] = "MERCHANT_POST_ORDERS_ID_PAY_MERCHANT_FIELD_MISSING";
-    /**
-     * Failed to locate merchant's account information matching the wire hash 
given in the proposal.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_WIRE_HASH_UNKNOWN"] 
= 2163] = "MERCHANT_POST_ORDERS_ID_PAY_WIRE_HASH_UNKNOWN";
-    /**
-     * The deposit time for the denomination has expired.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_DEPOSIT_EXPIRED"]
 = 2165] = "MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_DEPOSIT_EXPIRED";
-    /**
-     * The exchange of the deposited coin charges a wire fee that could not be 
added to the total (total amount too high).
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED"]
 = 2166] = "MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED";
-    /**
-     * The contract was not fully paid because of refunds. Note that clients 
MAY treat this as paid if, for example, contracts must be executed despite of 
refunds.
-     * Returned with an HTTP status code of #MHD_HTTP_PAYMENT_REQUIRED (402).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_REFUNDED"] = 
2167] = "MERCHANT_POST_ORDERS_ID_PAY_REFUNDED";
-    /**
-     * According to our database, we have refunded more than we were paid 
(which should not be possible).
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_REFUNDS_EXCEED_PAYMENTS"]
 = 2168] = "MERCHANT_POST_ORDERS_ID_PAY_REFUNDS_EXCEED_PAYMENTS";
-    /**
-     * Legacy stuff. Remove me with protocol v1.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["DEAD_QQQ_PAY_MERCHANT_POST_ORDERS_ID_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE"]
 = 2169] = 
"DEAD_QQQ_PAY_MERCHANT_POST_ORDERS_ID_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE";
-    /**
-     * The payment failed at the exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_FAILED"] = 
2170] = "MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_FAILED";
-    /**
-     * The contract hash does not match the given order ID.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAID_CONTRACT_HASH_MISMATCH"]
 = 2200] = "MERCHANT_POST_ORDERS_ID_PAID_CONTRACT_HASH_MISMATCH";
-    /**
-     * The signature of the merchant is not valid for the given contract hash.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_PAID_COIN_SIGNATURE_INVALID"]
 = 2201] = "MERCHANT_POST_ORDERS_ID_PAID_COIN_SIGNATURE_INVALID";
-    /**
-     * The merchant failed to send the exchange the refund request.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_ABORT_EXCHANGE_REFUND_FAILED"]
 = 2251] = "MERCHANT_POST_ORDERS_ID_ABORT_EXCHANGE_REFUND_FAILED";
-    /**
-     * The merchant failed to find the exchange to process the lookup.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_ABORT_EXCHANGE_LOOKUP_FAILED"]
 = 2252] = "MERCHANT_POST_ORDERS_ID_ABORT_EXCHANGE_LOOKUP_FAILED";
-    /**
-     * The merchant could not find the contract.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_ABORT_CONTRACT_NOT_FOUND"]
 = 2253] = "MERCHANT_POST_ORDERS_ID_ABORT_CONTRACT_NOT_FOUND";
-    /**
-     * The payment was already completed and thus cannot be aborted anymore.
-     * Returned with an HTTP status code of #MHD_HTTP_PRECONDITION_FAILED 
(412).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE"]
 = 2254] = "MERCHANT_POST_ORDERS_ID_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE";
-    /**
-     * The hash provided by the wallet does not match the order.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_ABORT_CONTRACT_HASH_MISSMATCH"]
 = 2255] = "MERCHANT_POST_ORDERS_ID_ABORT_CONTRACT_HASH_MISSMATCH";
-    /**
-     * The array of coins cannot be empty.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_ABORT_COINS_ARRAY_EMPTY"]
 = 2256] = "MERCHANT_POST_ORDERS_ID_ABORT_COINS_ARRAY_EMPTY";
-    /**
-     * We could not claim the order because the backend is unaware of it.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_CLAIM_NOT_FOUND"] = 
2300] = "MERCHANT_POST_ORDERS_ID_CLAIM_NOT_FOUND";
-    /**
-     * We could not claim the order because someone else claimed it first.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_CLAIM_ALREADY_CLAIMED"] 
= 2301] = "MERCHANT_POST_ORDERS_ID_CLAIM_ALREADY_CLAIMED";
-    /**
-     * The client-side experienced an internal failure.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE"]
 = 2302] = "MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE";
-    /**
-     * The backend failed to sign the refund request.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_POST_ORDERS_ID_REFUND_SIGNATURE_FAILED"]
 = 2350] = "MERCHANT_POST_ORDERS_ID_REFUND_SIGNATURE_FAILED";
-    /**
-     * The client failed to unblind the signature returned by the merchant.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_TIP_PICKUP_UNBLIND_FAILURE"] = 
2400] = "MERCHANT_TIP_PICKUP_UNBLIND_FAILURE";
-    /**
-     * The exchange returned a failure code for the withdraw operation.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_TIP_PICKUP_EXCHANGE_ERROR"] = 
2403] = "MERCHANT_TIP_PICKUP_EXCHANGE_ERROR";
-    /**
-     * The merchant failed to add up the amounts to compute the pick up value.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_TIP_PICKUP_SUMMATION_FAILED"] = 
2404] = "MERCHANT_TIP_PICKUP_SUMMATION_FAILED";
-    /**
-     * The tip expired.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_TIP_PICKUP_HAS_EXPIRED"] = 2405] = 
"MERCHANT_TIP_PICKUP_HAS_EXPIRED";
-    /**
-     * The requested withdraw amount exceeds the amount remaining to be picked 
up.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_TIP_PICKUP_AMOUNT_EXCEEDS_TIP_REMAINING"]
 = 2406] = "MERCHANT_TIP_PICKUP_AMOUNT_EXCEEDS_TIP_REMAINING";
-    /**
-     * The merchant did not find the specified denomination key in the 
exchange's key set.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_TIP_PICKUP_DENOMINATION_UNKNOWN"] 
= 2407] = "MERCHANT_TIP_PICKUP_DENOMINATION_UNKNOWN";
-    /**
-     * The backend lacks a wire transfer method configuration option for the 
given instance. Thus, this instance is unavailable (not findable for creating 
new orders).
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE"]
 = 2500] = "MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE";
-    /**
-     * The proposal had no timestamp and the backend failed to obtain the 
local time. Likely to be an internal error.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME"] 
= 2501] = "MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME";
-    /**
-     * The order provided to the backend could not be parsed, some required 
fields were missing or ill-formed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR"]
 = 2502] = "MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR";
-    /**
-     * The backend encountered an error: the proposal already exists.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS"] = 
2503] = "MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS";
-    /**
-     * The request is invalid: the wire deadline is before the refund deadline.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE"]
 = 2504] = "MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE";
-    /**
-     * One of the paths to forget is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT"]
 = 2510] = "MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT";
-    /**
-     * One of the paths to forget was not marked as forgettable.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_NOT_FORGETTABLE"]
 = 2511] = "MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_NOT_FORGETTABLE";
-    /**
-     * The order provided to the backend could not be deleted, our offer is 
still valid and awaiting payment.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_DELETE_ORDERS_AWAITING_PAYMENT"]
 = 2520] = "MERCHANT_PRIVATE_DELETE_ORDERS_AWAITING_PAYMENT";
-    /**
-     * The amount to be refunded is inconsistent: either is lower than the 
previous amount being awarded, or it is too big to be paid back. In this second 
case, the fault stays on the business dept. side.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_INCONSISTENT_AMOUNT"]
 = 2530] = "MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_INCONSISTENT_AMOUNT";
-    /**
-     * The frontend gave an unpaid order id to issue the refund to.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_ORDER_UNPAID"]
 = 2531] = "MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_ORDER_UNPAID";
-    /**
-     * The refund delay was set to 0 and thus no refunds are allowed for this 
order.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_NOT_ALLOWED_BY_CONTRACT"]
 = 2532] = "MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_NOT_ALLOWED_BY_CONTRACT";
-    /**
-     * We internally failed to execute the /track/transfer request.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_TRANSFERS_REQUEST_ERROR"] 
= 2551] = "MERCHANT_PRIVATE_POST_TRANSFERS_REQUEST_ERROR";
-    /**
-     * The exchange gave conflicting information about a coin which has been 
wire transferred.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS"]
 = 2553] = "MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS";
-    /**
-     * The exchange charged a different wire fee than what it originally 
advertised, and it is higher.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE"] = 
2554] = "MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE";
-    /**
-     * We did not find the account that the transfer was made to.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_TRANSFERS_ACCOUNT_NOT_FOUND"]
 = 2555] = "MERCHANT_PRIVATE_POST_TRANSFERS_ACCOUNT_NOT_FOUND";
-    /**
-     * The merchant backend cannot create an instance under the given 
identifier as one already exists. Use PATCH to modify the existing entry.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS"] 
= 2600] = "MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS";
-    /**
-     * The merchant backend cannot create an instance because the 
authentication configuration field is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH"] 
= 2601] = "MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH";
-    /**
-     * The merchant backend cannot update an instance's authentication 
settings because the provided authentication settings are malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_INSTANCE_AUTH_BAD_AUTH"] = 
2602] = "MERCHANT_PRIVATE_POST_INSTANCE_AUTH_BAD_AUTH";
-    /**
-     * The merchant backend cannot create an instance under the given 
identifier, the previous one was deleted but must be purged first.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_INSTANCES_PURGE_REQUIRED"] 
= 2603] = "MERCHANT_PRIVATE_POST_INSTANCES_PURGE_REQUIRED";
-    /**
-     * The merchant backend cannot update an instance under the given 
identifier, the previous one was deleted but must be purged first.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_PATCH_INSTANCES_PURGE_REQUIRED"]
 = 2625] = "MERCHANT_PRIVATE_PATCH_INSTANCES_PURGE_REQUIRED";
-    /**
-     * The product ID exists.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_PRODUCTS_CONFLICT_PRODUCT_EXISTS"]
 = 2650] = "MERCHANT_PRIVATE_POST_PRODUCTS_CONFLICT_PRODUCT_EXISTS";
-    /**
-     * The update would have reduced the total amount of product lost, which 
is not allowed.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_REDUCED"]
 = 2660] = "MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_REDUCED";
-    /**
-     * The update would have mean that more stocks were lost than what remains 
from total inventory after sales, which is not allowed.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_EXCEEDS_STOCKS"]
 = 2661] = "MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_LOST_EXCEEDS_STOCKS";
-    /**
-     * The update would have reduced the total amount of product in stock, 
which is not allowed.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_STOCKED_REDUCED"]
 = 2662] = "MERCHANT_PRIVATE_PATCH_PRODUCTS_TOTAL_STOCKED_REDUCED";
-    /**
-     * The lock request is for more products than we have left (unlocked) in 
stock.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_PRODUCTS_LOCK_INSUFFICIENT_STOCKS"]
 = 2670] = "MERCHANT_PRIVATE_POST_PRODUCTS_LOCK_INSUFFICIENT_STOCKS";
-    /**
-     * The deletion request is for a product that is locked.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_DELETE_PRODUCTS_CONFLICTING_LOCK"]
 = 2680] = "MERCHANT_PRIVATE_DELETE_PRODUCTS_CONFLICTING_LOCK";
-    /**
-     * The requested wire method is not supported by the exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_RESERVES_UNSUPPORTED_WIRE_METHOD"]
 = 2700] = "MERCHANT_PRIVATE_POST_RESERVES_UNSUPPORTED_WIRE_METHOD";
-    /**
-     * The reserve could not be deleted because it is unknown.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_DELETE_RESERVES_NO_SUCH_RESERVE"]
 = 2710] = "MERCHANT_PRIVATE_DELETE_RESERVES_NO_SUCH_RESERVE";
-    /**
-     * The reserve that was used to fund the tips has expired.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED"]
 = 2750] = "MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED";
-    /**
-     * The reserve that was used to fund the tips was not found in the DB.
-     * Returned with an HTTP status code of #MHD_HTTP_SERVICE_UNAVAILABLE 
(503).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_UNKNOWN"]
 = 2751] = "MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_UNKNOWN";
-    /**
-     * The backend knows the instance that was supposed to support the tip, 
and it was configured for tipping. However, the funds remaining are 
insufficient to cover the tip, and the merchant should top up the reserve.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS"]
 = 2752] = "MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS";
-    /**
-     * The backend failed to find a reserve needed to authorize the tip.
-     * Returned with an HTTP status code of #MHD_HTTP_SERVICE_UNAVAILABLE 
(503).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND"]
 = 2753] = "MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND";
-    /**
-     * The merchant backend encountered a failure in computing the deposit 
total.
-     * Returned with an HTTP status code of #MHD_HTTP_OK (200).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["MERCHANT_PRIVATE_GET_ORDERS_ID_AMOUNT_ARITHMETIC_FAILURE"]
 = 2800] = "MERCHANT_PRIVATE_GET_ORDERS_ID_AMOUNT_ARITHMETIC_FAILURE";
-    /**
-     * The signature from the exchange on the deposit confirmation is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["AUDITOR_DEPOSIT_CONFIRMATION_SIGNATURE_INVALID"] 
= 3100] = "AUDITOR_DEPOSIT_CONFIRMATION_SIGNATURE_INVALID";
-    /**
-     * The exchange key used for the signature on the deposit confirmation was 
revoked.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["AUDITOR_EXCHANGE_SIGNING_KEY_REVOKED"] = 
3101] = "AUDITOR_EXCHANGE_SIGNING_KEY_REVOKED";
-    /**
-     * Wire transfer attempted with credit and debit party being the same bank 
account.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_SAME_ACCOUNT"] = 5101] = 
"BANK_SAME_ACCOUNT";
-    /**
-     * Wire transfer impossible, due to financial limitation of the party that 
attempted the payment.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_UNALLOWED_DEBIT"] = 5102] = 
"BANK_UNALLOWED_DEBIT";
-    /**
-     * Negative number was used (as value and/or fraction) to initiate a 
Amount object.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_NEGATIVE_NUMBER_AMOUNT"] = 5103] = 
"BANK_NEGATIVE_NUMBER_AMOUNT";
-    /**
-     * A number too big was used (as value and/or fraction) to initiate a 
amount object.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_NUMBER_TOO_BIG"] = 5104] = 
"BANK_NUMBER_TOO_BIG";
-    /**
-     * Could not login for the requested operation.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_LOGIN_FAILED"] = 5105] = 
"BANK_LOGIN_FAILED";
-    /**
-     * The bank account referenced in the requested operation was not found. 
Returned along "400 Not found".
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_UNKNOWN_ACCOUNT"] = 5106] = 
"BANK_UNKNOWN_ACCOUNT";
-    /**
-     * The transaction referenced in the requested operation (typically a 
reject operation), was not found.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_TRANSACTION_NOT_FOUND"] = 5107] = 
"BANK_TRANSACTION_NOT_FOUND";
-    /**
-     * Bank received a malformed amount string.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_BAD_FORMAT_AMOUNT"] = 5108] = 
"BANK_BAD_FORMAT_AMOUNT";
-    /**
-     * The client does not own the account credited by the transaction which 
is to be rejected, so it has no rights do reject it.  To be returned along HTTP 
403 Forbidden.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_REJECT_NO_RIGHTS"] = 5109] = 
"BANK_REJECT_NO_RIGHTS";
-    /**
-     * This error code is returned when no known exception types captured the 
exception, and comes along with a 500 Internal Server Error.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_UNMANAGED_EXCEPTION"] = 5110] = 
"BANK_UNMANAGED_EXCEPTION";
-    /**
-     * This error code is used for all those exceptions that do not really 
need a specific error code to return to the client, but need to signal the 
middleware that the bank is not responding with 500 Internal Server Error.  
Used for example when a client is trying to register with a unavailable 
username.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_SOFT_EXCEPTION"] = 5111] = 
"BANK_SOFT_EXCEPTION";
-    /**
-     * The request UID for a request to transfer funds has already been used, 
but with different details for the transfer.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["BANK_TRANSFER_REQUEST_UID_REUSED"] = 5112] 
= "BANK_TRANSFER_REQUEST_UID_REUSED";
-    /**
-     * The withdrawal operation already has a reserve selected.  The current 
request conflicts with the existing selection.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT"]
 = 5113] = "BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT";
-    /**
-     * The sync service failed find the account in its database.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_ACCOUNT_UNKNOWN"] = 6100] = 
"SYNC_ACCOUNT_UNKNOWN";
-    /**
-     * The SHA-512 hash provided in the If-None-Match header is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_BAD_IF_NONE_MATCH"] = 6101] = 
"SYNC_BAD_IF_NONE_MATCH";
-    /**
-     * The SHA-512 hash provided in the If-Match header is malformed or 
missing.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_BAD_IF_MATCH"] = 6102] = 
"SYNC_BAD_IF_MATCH";
-    /**
-     * The signature provided in the "Sync-Signature" header is malformed or 
missing.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_BAD_SYNC_SIGNATURE"] = 6103] = 
"SYNC_BAD_SYNC_SIGNATURE";
-    /**
-     * The signature provided in the "Sync-Signature" header does not match 
the account, old or new Etags.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_INVALID_SIGNATURE"] = 6104] = 
"SYNC_INVALID_SIGNATURE";
-    /**
-     * The "Content-length" field for the upload is not a number.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_MALFORMED_CONTENT_LENGTH"] = 6105] = 
"SYNC_MALFORMED_CONTENT_LENGTH";
-    /**
-     * The "Content-length" field for the upload is too big based on the 
server's terms of service.
-     * Returned with an HTTP status code of #MHD_HTTP_PAYLOAD_TOO_LARGE (413).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_EXCESSIVE_CONTENT_LENGTH"] = 6106] = 
"SYNC_EXCESSIVE_CONTENT_LENGTH";
-    /**
-     * The server is out of memory to handle the upload. Trying again later 
may succeed.
-     * Returned with an HTTP status code of #MHD_HTTP_PAYLOAD_TOO_LARGE (413).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_OUT_OF_MEMORY_ON_CONTENT_LENGTH"] = 
6107] = "SYNC_OUT_OF_MEMORY_ON_CONTENT_LENGTH";
-    /**
-     * The uploaded data does not match the Etag.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_INVALID_UPLOAD"] = 6108] = 
"SYNC_INVALID_UPLOAD";
-    /**
-     * HTTP server experienced a timeout while awaiting promised payment.
-     * Returned with an HTTP status code of #MHD_HTTP_REQUEST_TIMEOUT (408).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_PAYMENT_GENERIC_TIMEOUT"] = 6109] = 
"SYNC_PAYMENT_GENERIC_TIMEOUT";
-    /**
-     * Sync could not setup the payment request with its own backend.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_PAYMENT_CREATE_BACKEND_ERROR"] = 6110] 
= "SYNC_PAYMENT_CREATE_BACKEND_ERROR";
-    /**
-     * The sync service failed find the backup to be updated in its database.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_PREVIOUS_BACKUP_UNKNOWN"] = 6111] = 
"SYNC_PREVIOUS_BACKUP_UNKNOWN";
-    /**
-     * The "Content-length" field for the upload is missing.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["SYNC_MISSING_CONTENT_LENGTH"] = 6112] = 
"SYNC_MISSING_CONTENT_LENGTH";
-    /**
-     * The wallet does not implement a version of the exchange protocol that 
is compatible with the protocol version of the exchange.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_IMPLEMENTED (501).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE"] 
= 7000] = "WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE";
-    /**
-     * The wallet encountered an unexpected exception.  This is likely a bug 
in the wallet implementation.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_UNEXPECTED_EXCEPTION"] = 7001] = 
"WALLET_UNEXPECTED_EXCEPTION";
-    /**
-     * The wallet received a response from a server, but the response can't be 
parsed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_RECEIVED_MALFORMED_RESPONSE"] = 
7002] = "WALLET_RECEIVED_MALFORMED_RESPONSE";
-    /**
-     * The wallet tried to make a network request, but it received no response.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_NETWORK_ERROR"] = 7003] = 
"WALLET_NETWORK_ERROR";
-    /**
-     * The wallet tried to make a network request, but it was throttled.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_HTTP_REQUEST_THROTTLED"] = 7004] = 
"WALLET_HTTP_REQUEST_THROTTLED";
-    /**
-     * The wallet made a request to a service, but received an error response 
it does not know how to handle.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_UNEXPECTED_REQUEST_ERROR"] = 7005] = 
"WALLET_UNEXPECTED_REQUEST_ERROR";
-    /**
-     * The denominations offered by the exchange are insufficient.  Likely the 
exchange is badly configured or not maintained.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["WALLET_EXCHANGE_DENOMINATIONS_INSUFFICIENT"] = 
7006] = "WALLET_EXCHANGE_DENOMINATIONS_INSUFFICIENT";
-    /**
-     * The wallet does not support the operation requested by a client.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_CORE_API_OPERATION_UNKNOWN"] = 7007] 
= "WALLET_CORE_API_OPERATION_UNKNOWN";
-    /**
-     * The given taler://pay URI is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_INVALID_TALER_PAY_URI"] = 7008] = 
"WALLET_INVALID_TALER_PAY_URI";
-    /**
-     * The signature on a coin by the exchange's denomination key is invalid 
after unblinding it.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_EXCHANGE_COIN_SIGNATURE_INVALID"] = 
7009] = "WALLET_EXCHANGE_COIN_SIGNATURE_INVALID";
-    /**
-     * The exchange does not know about the reserve (yet), and thus withdrawal 
can't progress.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["WALLET_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN_AT_EXCHANGE"]
 = 7010] = "WALLET_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN_AT_EXCHANGE";
-    /**
-     * The wallet core service is not available.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_CORE_NOT_AVAILABLE"] = 7011] = 
"WALLET_CORE_NOT_AVAILABLE";
-    /**
-     * The bank has aborted a withdrawal operation, and thus a withdrawal 
can't complete.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK"] = 
7012] = "WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK";
-    /**
-     * An HTTP request made by the wallet timed out.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_HTTP_REQUEST_GENERIC_TIMEOUT"] = 
7013] = "WALLET_HTTP_REQUEST_GENERIC_TIMEOUT";
-    /**
-     * The order has already been claimed by another wallet.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_ORDER_ALREADY_CLAIMED"] = 7014] = 
"WALLET_ORDER_ALREADY_CLAIMED";
-    /**
-     * A group of withdrawal operations (typically for the same reserve at the 
same exchange) has errors and will be tried again later.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_WITHDRAWAL_GROUP_INCOMPLETE"] = 
7015] = "WALLET_WITHDRAWAL_GROUP_INCOMPLETE";
-    /**
-     * The signature on a coin by the exchange's denomination key (obtained 
through the merchant via tipping) is invalid after unblinding it.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_TIPPING_COIN_SIGNATURE_INVALID"] = 
7016] = "WALLET_TIPPING_COIN_SIGNATURE_INVALID";
-    /**
-     * The wallet does not implement a version of the bank integration API 
that is compatible with the version offered by the bank.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE"]
 = 7017] = "WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE";
-    /**
-     * The wallet processed a taler://pay URI, but the merchant base URL in 
the downloaded contract terms does not match the merchant base URL derived from 
the URI.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_CONTRACT_TERMS_BASE_URL_MISMATCH"] = 
7018] = "WALLET_CONTRACT_TERMS_BASE_URL_MISMATCH";
-    /**
-     * The merchant's signature on the contract terms is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_CONTRACT_TERMS_SIGNATURE_INVALID"] = 
7019] = "WALLET_CONTRACT_TERMS_SIGNATURE_INVALID";
-    /**
-     * The contract terms given by the merchant are malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["WALLET_CONTRACT_TERMS_MALFORMED"] = 7020] = 
"WALLET_CONTRACT_TERMS_MALFORMED";
-    /**
-     * We encountered a timeout with our payment backend.
-     * Returned with an HTTP status code of #MHD_HTTP_GATEWAY_TIMEOUT (504).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_GENERIC_BACKEND_TIMEOUT"] = 8000] 
= "ANASTASIS_GENERIC_BACKEND_TIMEOUT";
-    /**
-     * The backend requested payment, but the request is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_GENERIC_INVALID_PAYMENT_REQUEST"] 
= 8001] = "ANASTASIS_GENERIC_INVALID_PAYMENT_REQUEST";
-    /**
-     * The backend got an unexpected reply from the payment processor.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_GENERIC_BACKEND_ERROR"] = 8002] = 
"ANASTASIS_GENERIC_BACKEND_ERROR";
-    /**
-     * The "Content-length" field for the upload is missing.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH"] 
= 8003] = "ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH";
-    /**
-     * The "Content-length" field for the upload is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH"] = 
8004] = "ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH";
-    /**
-     * The backend failed to setup an order with the payment processor.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_GENERIC_ORDER_CREATE_BACKEND_ERROR"] = 
8005] = "ANASTASIS_GENERIC_ORDER_CREATE_BACKEND_ERROR";
-    /**
-     * The backend was not authorized to check for payment with the payment 
processor.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED"] = 
8006] = "ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED";
-    /**
-     * The backend could not check payment status with the payment processor.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED"] = 
8007] = "ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED";
-    /**
-     * The truth public key is unknown to the provider.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_UNKNOWN"] = 8108] = 
"ANASTASIS_TRUTH_UNKNOWN";
-    /**
-     * The authorization method used by the truth is no longer supported by 
the provider.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED"]
 = 8109] = "ANASTASIS_TRUTH_AUTHORIZATION_METHOD_NO_LONGER_SUPPORTED";
-    /**
-     * The client needs to respond to the challenge.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED"] = 
8110] = "ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED";
-    /**
-     * The client's response to the challenge was invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_CHALLENGE_FAILED"] = 8111] 
= "ANASTASIS_TRUTH_CHALLENGE_FAILED";
-    /**
-     * The service is unaware of having issued a challenge.
-     * Returned with an HTTP status code of #MHD_HTTP_GONE (410).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_CHALLENGE_UNKNOWN"] = 8112] 
= "ANASTASIS_TRUTH_CHALLENGE_UNKNOWN";
-    /**
-     * A challenge is already active, the service is thus not issuing a new 
one.
-     * Returned with an HTTP status code of #MHD_HTTP_ALREADY_REPORTED (208).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_CHALLENGE_ACTIVE"] = 8113] 
= "ANASTASIS_TRUTH_CHALLENGE_ACTIVE";
-    /**
-     * The backend failed to initiate the authorization process.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED"] = 
8114] = "ANASTASIS_TRUTH_AUTHORIZATION_START_FAILED";
-    /**
-     * The authorization succeeded, but the key share is no longer available.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_KEY_SHARE_GONE"] = 8115] = 
"ANASTASIS_TRUTH_KEY_SHARE_GONE";
-    /**
-     * The backend forgot the order we asked the client to pay for
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_ORDER_DISAPPEARED"] = 8116] 
= "ANASTASIS_TRUTH_ORDER_DISAPPEARED";
-    /**
-     * The backend itself reported a bad exchange interaction.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_BACKEND_EXCHANGE_BAD"] = 
8117] = "ANASTASIS_TRUTH_BACKEND_EXCHANGE_BAD";
-    /**
-     * The backend reported a payment status we did not expect.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_UNEXPECTED_PAYMENT_STATUS"] 
= 8118] = "ANASTASIS_TRUTH_UNEXPECTED_PAYMENT_STATUS";
-    /**
-     * The backend failed to setup the order for payment.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_PAYMENT_CREATE_BACKEND_ERROR"] = 
8119] = "ANASTASIS_TRUTH_PAYMENT_CREATE_BACKEND_ERROR";
-    /**
-     * The decryption of the truth object failed with the provided key.
-     * Returned with an HTTP status code of #MHD_HTTP_EXPECTATION_FAILED (417).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_DECRYPTION_FAILED"] = 8120] 
= "ANASTASIS_TRUTH_DECRYPTION_FAILED";
-    /**
-     * The request rate is too high. The server is refusing requests to guard 
against brute-force attacks.
-     * Returned with an HTTP status code of #MHD_HTTP_TOO_MANY_REQUESTS (429).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_RATE_LIMITED"] = 8121] = 
"ANASTASIS_TRUTH_RATE_LIMITED";
-    /**
-     * The backend failed to store the truth because the UUID is already in 
use.
-     * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS"] = 
8150] = "ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS";
-    /**
-     * The backend failed to store the truth because the authorization method 
is not supported.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED"] = 
8151] = "ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED";
-    /**
-     * The provided phone number is not an acceptable number.
-     * Returned with an HTTP status code of #MHD_HTTP_EXPECTATION_FAILED (417).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_SMS_PHONE_INVALID"] = 8200] = 
"ANASTASIS_SMS_PHONE_INVALID";
-    /**
-     * Failed to run the SMS transmission helper process.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_SMS_HELPER_EXEC_FAILED"] = 8201] 
= "ANASTASIS_SMS_HELPER_EXEC_FAILED";
-    /**
-     * Helper terminated with a non-successful result.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_SMS_HELPER_COMMAND_FAILED"] = 
8202] = "ANASTASIS_SMS_HELPER_COMMAND_FAILED";
-    /**
-     * The provided email address is not an acceptable address.
-     * Returned with an HTTP status code of #MHD_HTTP_EXPECTATION_FAILED (417).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_EMAIL_INVALID"] = 8210] = 
"ANASTASIS_EMAIL_INVALID";
-    /**
-     * Failed to run the E-mail transmission helper process.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_EMAIL_HELPER_EXEC_FAILED"] = 
8211] = "ANASTASIS_EMAIL_HELPER_EXEC_FAILED";
-    /**
-     * Helper terminated with a non-successful result.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_EMAIL_HELPER_COMMAND_FAILED"] = 
8212] = "ANASTASIS_EMAIL_HELPER_COMMAND_FAILED";
-    /**
-     * The provided postal address is not an acceptable address.
-     * Returned with an HTTP status code of #MHD_HTTP_EXPECTATION_FAILED (417).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_POST_INVALID"] = 8220] = 
"ANASTASIS_POST_INVALID";
-    /**
-     * Failed to run the mail transmission helper process.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_POST_HELPER_EXEC_FAILED"] = 8221] 
= "ANASTASIS_POST_HELPER_EXEC_FAILED";
-    /**
-     * Helper terminated with a non-successful result.
-     * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_POST_HELPER_COMMAND_FAILED"] = 
8222] = "ANASTASIS_POST_HELPER_COMMAND_FAILED";
-    /**
-     * The given if-none-match header is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_POLICY_BAD_IF_NONE_MATCH"] = 
8301] = "ANASTASIS_POLICY_BAD_IF_NONE_MATCH";
-    /**
-     * The server is out of memory to handle the upload. Trying again later 
may succeed.
-     * Returned with an HTTP status code of #MHD_HTTP_PAYLOAD_TOO_LARGE (413).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH"]
 = 8304] = "ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH";
-    /**
-     * The signature provided in the "Anastasis-Policy-Signature" header is 
malformed or missing.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_POLICY_BAD_SIGNATURE"] = 8305] = 
"ANASTASIS_POLICY_BAD_SIGNATURE";
-    /**
-     * The given if-match header is malformed.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_POLICY_BAD_IF_MATCH"] = 8306] = 
"ANASTASIS_POLICY_BAD_IF_MATCH";
-    /**
-     * The uploaded data does not match the Etag.
-     * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_POLICY_INVALID_UPLOAD"] = 8307] = 
"ANASTASIS_POLICY_INVALID_UPLOAD";
-    /**
-     * The provider is unaware of the requested policy.
-     * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_POLICY_NOT_FOUND"] = 8350] = 
"ANASTASIS_POLICY_NOT_FOUND";
-    /**
-     * The given action is invalid for the current state of the reducer.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_ACTION_INVALID"] = 8400] 
= "ANASTASIS_REDUCER_ACTION_INVALID";
-    /**
-     * The given state of the reducer is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_STATE_INVALID"] = 8401] = 
"ANASTASIS_REDUCER_STATE_INVALID";
-    /**
-     * The given input to the reducer is invalid.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_INPUT_INVALID"] = 8402] = 
"ANASTASIS_REDUCER_INPUT_INVALID";
-    /**
-     * The selected authentication method does ot work for the Anastasis 
provider.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    
TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_AUTHENTICATION_METHOD_NOT_SUPPORTED"]
 = 8403] = "ANASTASIS_REDUCER_AUTHENTICATION_METHOD_NOT_SUPPORTED";
-    /**
-     * The given input and action do not work for the current state.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE"] 
= 8404] = "ANASTASIS_REDUCER_INPUT_INVALID_FOR_STATE";
-    /**
-     * We experienced an unexpected failure interacting with the backend.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_BACKEND_FAILURE"] = 8405] 
= "ANASTASIS_REDUCER_BACKEND_FAILURE";
-    /**
-     * The contents of a resource file did not match our expectations.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_RESOURCE_MALFORMED"] = 
8406] = "ANASTASIS_REDUCER_RESOURCE_MALFORMED";
-    /**
-     * A required resource file is missing.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_RESOURCE_MISSING"] = 
8407] = "ANASTASIS_REDUCER_RESOURCE_MISSING";
-    /**
-     * An input did not match the regular expression.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_INPUT_REGEX_FAILED"] = 
8408] = "ANASTASIS_REDUCER_INPUT_REGEX_FAILED";
-    /**
-     * An input did not match the custom validation logic.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_INPUT_VALIDATION_FAILED"] 
= 8409] = "ANASTASIS_REDUCER_INPUT_VALIDATION_FAILED";
-    /**
-     * Our attempts to download the recovery document failed with all 
providers.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED"] = 
8410] = "ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED";
-    /**
-     * Anastasis provider reported a fatal failure.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_BACKUP_PROVIDER_FAILED"] 
= 8411] = "ANASTASIS_REDUCER_BACKUP_PROVIDER_FAILED";
-    /**
-     * Anastasis provider failed to respond to the configuration request.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED"] 
= 8412] = "ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED";
-    /**
-     * The policy we downloaded is malformed. Must have been a client error 
while creating the backup.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_POLICY_MALFORMED"] = 
8413] = "ANASTASIS_REDUCER_POLICY_MALFORMED";
-    /**
-     * We failed to obtain the policy, likely due to a network issue.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_NETWORK_FAILED"] = 8414] 
= "ANASTASIS_REDUCER_NETWORK_FAILED";
-    /**
-     * The recovered secret did not match the required syntax.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_SECRET_MALFORMED"] = 
8415] = "ANASTASIS_REDUCER_SECRET_MALFORMED";
-    /**
-     * The challenge data provided is too large for the available providers.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_CHALLENGE_DATA_TOO_BIG"] 
= 8416] = "ANASTASIS_REDUCER_CHALLENGE_DATA_TOO_BIG";
-    /**
-     * The provided core secret is too large for some of the providers.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_SECRET_TOO_BIG"] = 8417] 
= "ANASTASIS_REDUCER_SECRET_TOO_BIG";
-    /**
-     * The provider returned in invalid configuration.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["ANASTASIS_REDUCER_PROVIDER_INVALID_CONFIG"] 
= 8418] = "ANASTASIS_REDUCER_PROVIDER_INVALID_CONFIG";
-    /**
-     * End of error code range.
-     * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
-     * (A value of 0 indicates that the error is generated client-side).
-     */
-    TalerErrorCode[TalerErrorCode["END"] = 9999] = "END";
-})(TalerErrorCode || (TalerErrorCode = {}));
-
-/*
- This file is part of GNU Taler
- (C) 2018-2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Type-safe codecs for converting from/to JSON.
- */
-/* eslint-disable @typescript-eslint/ban-types */
-/**
- * Error thrown when decoding fails.
- */
-class DecodingError extends Error {
-    constructor(message) {
-        super(message);
-        Object.setPrototypeOf(this, DecodingError.prototype);
-        this.name = "DecodingError";
-    }
-}
-function renderContext(c) {
-    const p = c === null || c === void 0 ? void 0 : c.path;
-    if (p) {
-        return p.join(".");
-    }
-    else {
-        return "(unknown)";
-    }
-}
-function joinContext(c, part) {
-    var _a;
-    const path = (_a = c === null || c === void 0 ? void 0 : c.path) !== null 
&& _a !== void 0 ? _a : [];
-    return {
-        path: path.concat([part]),
-    };
-}
-class ObjectCodecBuilder {
-    constructor() {
-        this.propList = [];
-    }
-    /**
-     * Define a property for the object.
-     */
-    property(x, codec) {
-        if (!codec) {
-            throw Error("inner codec must be defined");
-        }
-        this.propList.push({ name: x, codec: codec });
-        return this;
-    }
-    /**
-     * Return the built codec.
-     *
-     * @param objectDisplayName name of the object that this codec operates on,
-     *   used in error messages.
-     */
-    build(objectDisplayName) {
-        const propList = this.propList;
-        return {
-            decode(x, c) {
-                if (!c) {
-                    c = {
-                        path: [`(${objectDisplayName})`],
-                    };
-                }
-                if (typeof x !== "object") {
-                    throw new DecodingError(`expected object for 
${objectDisplayName} at ${renderContext(c)} but got ${typeof x}`);
-                }
-                const obj = {};
-                for (const prop of propList) {
-                    const propRawVal = x[prop.name];
-                    const propVal = prop.codec.decode(propRawVal, 
joinContext(c, prop.name));
-                    obj[prop.name] = propVal;
-                }
-                return obj;
-            },
-        };
-    }
-}
-class UnionCodecBuilder {
-    constructor(discriminator, baseCodec) {
-        this.discriminator = discriminator;
-        this.baseCodec = baseCodec;
-        this.alternatives = new Map();
-    }
-    /**
-     * Define a property for the object.
-     */
-    alternative(tagValue, codec) {
-        if (!codec) {
-            throw Error("inner codec must be defined");
-        }
-        this.alternatives.set(tagValue, { codec, tagValue });
-        return this;
-    }
-    /**
-     * Return the built codec.
-     *
-     * @param objectDisplayName name of the object that this codec operates on,
-     *   used in error messages.
-     */
-    build(objectDisplayName) {
-        const alternatives = this.alternatives;
-        const discriminator = this.discriminator;
-        const baseCodec = this.baseCodec;
-        return {
-            decode(x, c) {
-                if (!c) {
-                    c = {
-                        path: [`(${objectDisplayName})`],
-                    };
-                }
-                const d = x[discriminator];
-                if (d === undefined) {
-                    throw new DecodingError(`expected tag for 
${objectDisplayName} at ${renderContext(c)}.${discriminator}`);
-                }
-                const alt = alternatives.get(d);
-                if (!alt) {
-                    throw new DecodingError(`unknown tag for 
${objectDisplayName} ${d} at ${renderContext(c)}.${discriminator}`);
-                }
-                const altDecoded = alt.codec.decode(x);
-                if (baseCodec) {
-                    const baseDecoded = baseCodec.decode(x, c);
-                    return Object.assign(Object.assign({}, baseDecoded), 
altDecoded);
-                }
-                else {
-                    return altDecoded;
-                }
-            },
-        };
-    }
-}
-class UnionCodecPreBuilder {
-    discriminateOn(discriminator, baseCodec) {
-        return new UnionCodecBuilder(discriminator, baseCodec);
-    }
-}
-/**
- * Return a builder for a codec that decodes an object with properties.
- */
-function buildCodecForObject() {
-    return new ObjectCodecBuilder();
-}
-function buildCodecForUnion() {
-    return new UnionCodecPreBuilder();
-}
-/**
- * Return a codec for a mapping from a string to values described by the inner 
codec.
- */
-function codecForMap(innerCodec) {
-    if (!innerCodec) {
-        throw Error("inner codec must be defined");
-    }
-    return {
-        decode(x, c) {
-            const map = {};
-            if (typeof x !== "object") {
-                throw new DecodingError(`expected object at 
${renderContext(c)}`);
-            }
-            for (const i in x) {
-                map[i] = innerCodec.decode(x[i], joinContext(c, `[${i}]`));
-            }
-            return map;
-        },
-    };
-}
-/**
- * Return a codec for a list, containing values described by the inner codec.
- */
-function codecForList(innerCodec) {
-    if (!innerCodec) {
-        throw Error("inner codec must be defined");
-    }
-    return {
-        decode(x, c) {
-            const arr = [];
-            if (!Array.isArray(x)) {
-                throw new DecodingError(`expected array at 
${renderContext(c)}`);
-            }
-            for (const i in x) {
-                arr.push(innerCodec.decode(x[i], joinContext(c, `[${i}]`)));
-            }
-            return arr;
-        },
-    };
-}
-/**
- * Return a codec for a value that must be a number.
- */
-function codecForNumber() {
-    return {
-        decode(x, c) {
-            if (typeof x === "number") {
-                return x;
-            }
-            throw new DecodingError(`expected number at ${renderContext(c)} 
but got ${typeof x}`);
-        },
-    };
-}
-/**
- * Return a codec for a value that must be a number.
- */
-function codecForBoolean() {
-    return {
-        decode(x, c) {
-            if (typeof x === "boolean") {
-                return x;
-            }
-            throw new DecodingError(`expected boolean at ${renderContext(c)} 
but got ${typeof x}`);
-        },
-    };
-}
-/**
- * Return a codec for a value that must be a string.
- */
-function codecForString() {
-    return {
-        decode(x, c) {
-            if (typeof x === "string") {
-                return x;
-            }
-            throw new DecodingError(`expected string at ${renderContext(c)} 
but got ${typeof x}`);
-        },
-    };
-}
-/**
- * Codec that allows any value.
- */
-function codecForAny() {
-    return {
-        decode(x, c) {
-            return x;
-        },
-    };
-}
-/**
- * Return a codec for a value that must be a string.
- */
-function codecForConstString(s) {
-    return {
-        decode(x, c) {
-            if (x === s) {
-                return x;
-            }
-            if (typeof x !== "string") {
-                throw new DecodingError(`expected string constant "${s}" at 
${renderContext(c)} but got ${typeof x}`);
-            }
-            throw new DecodingError(`expected string constant "${s}" at 
${renderContext(c)} but got string value "${x}"`);
-        },
-    };
-}
-/**
- * Return a codec for a value that must be a constant number.
- */
-function codecForConstNumber(n) {
-    return {
-        decode(x, c) {
-            if (x === n) {
-                return x;
-            }
-            throw new DecodingError(`expected number constant "${n}" at 
${renderContext(c)}  but got ${typeof x}`);
-        },
-    };
-}
-function codecOptional(innerCodec) {
-    return {
-        decode(x, c) {
-            if (x === undefined || x === null) {
-                return undefined;
-            }
-            return innerCodec.decode(x, c);
-        },
-    };
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Number of fractional units that one value unit represents.
- */
-const amountFractionalBase = 1e8;
-/**
- * How many digits behind the comma are required to represent the
- * fractional value in human readable decimal format?  Must match
- * lg(fractionalBase)
- */
-const amountFractionalLength = 8;
-/**
- * Maximum allowed value field of an amount.
- */
-const amountMaxValue = Math.pow(2, 52);
-const codecForAmountString = () => codecForString();
-/**
- * Helper class for dealing with amounts.
- */
-class Amounts {
-    constructor() {
-        throw Error("not instantiable");
-    }
-    /**
-     * Get an amount that represents zero units of a currency.
-     */
-    static getZero(currency) {
-        return {
-            currency,
-            fraction: 0,
-            value: 0,
-        };
-    }
-    static jsonifyAmount(amt) {
-        if (typeof amt === "string") {
-            return Amounts.parseOrThrow(amt);
-        }
-        return amt;
-    }
-    static sum(amounts) {
-        if (amounts.length <= 0) {
-            throw Error("can't sum zero amounts");
-        }
-        const jsonAmounts = amounts.map((x) => Amounts.jsonifyAmount(x));
-        return Amounts.add(jsonAmounts[0], ...jsonAmounts.slice(1));
-    }
-    /**
-     * Add two amounts.  Return the result and whether
-     * the addition overflowed.  The overflow is always handled
-     * by saturating and never by wrapping.
-     *
-     * Throws when currencies don't match.
-     */
-    static add(first, ...rest) {
-        const currency = first.currency;
-        let value = first.value + Math.floor(first.fraction / 
amountFractionalBase);
-        if (value > amountMaxValue) {
-            return {
-                amount: {
-                    currency,
-                    value: amountMaxValue,
-                    fraction: amountFractionalBase - 1,
-                },
-                saturated: true,
-            };
-        }
-        let fraction = first.fraction % amountFractionalBase;
-        for (const x of rest) {
-            if (x.currency !== currency) {
-                throw Error(`Mismatched currency: ${x.currency} and 
${currency}`);
-            }
-            value =
-                value +
-                    x.value +
-                    Math.floor((fraction + x.fraction) / amountFractionalBase);
-            fraction = Math.floor((fraction + x.fraction) % 
amountFractionalBase);
-            if (value > amountMaxValue) {
-                return {
-                    amount: {
-                        currency,
-                        value: amountMaxValue,
-                        fraction: amountFractionalBase - 1,
-                    },
-                    saturated: true,
-                };
-            }
-        }
-        return { amount: { currency, value, fraction }, saturated: false };
-    }
-    /**
-     * Subtract two amounts.  Return the result and whether
-     * the subtraction overflowed.  The overflow is always handled
-     * by saturating and never by wrapping.
-     *
-     * Throws when currencies don't match.
-     */
-    static sub(a, ...rest) {
-        const currency = a.currency;
-        let value = a.value;
-        let fraction = a.fraction;
-        for (const b of rest) {
-            if (b.currency !== currency) {
-                throw Error(`Mismatched currency: ${b.currency} and 
${currency}`);
-            }
-            if (fraction < b.fraction) {
-                if (value < 1) {
-                    return {
-                        amount: { currency, value: 0, fraction: 0 },
-                        saturated: true,
-                    };
-                }
-                value--;
-                fraction += amountFractionalBase;
-            }
-            console.assert(fraction >= b.fraction);
-            fraction -= b.fraction;
-            if (value < b.value) {
-                return { amount: { currency, value: 0, fraction: 0 }, 
saturated: true };
-            }
-            value -= b.value;
-        }
-        return { amount: { currency, value, fraction }, saturated: false };
-    }
-    /**
-     * Compare two amounts.  Returns 0 when equal, -1 when a < b
-     * and +1 when a > b.  Throws when currencies don't match.
-     */
-    static cmp(a, b) {
-        a = Amounts.jsonifyAmount(a);
-        b = Amounts.jsonifyAmount(b);
-        if (a.currency !== b.currency) {
-            throw Error(`Mismatched currency: ${a.currency} and 
${b.currency}`);
-        }
-        const av = a.value + Math.floor(a.fraction / amountFractionalBase);
-        const af = a.fraction % amountFractionalBase;
-        const bv = b.value + Math.floor(b.fraction / amountFractionalBase);
-        const bf = b.fraction % amountFractionalBase;
-        switch (true) {
-            case av < bv:
-                return -1;
-            case av > bv:
-                return 1;
-            case af < bf:
-                return -1;
-            case af > bf:
-                return 1;
-            case af === bf:
-                return 0;
-            default:
-                throw Error("assertion failed");
-        }
-    }
-    /**
-     * Create a copy of an amount.
-     */
-    static copy(a) {
-        return {
-            currency: a.currency,
-            fraction: a.fraction,
-            value: a.value,
-        };
-    }
-    /**
-     * Divide an amount.  Throws on division by zero.
-     */
-    static divide(a, n) {
-        if (n === 0) {
-            throw Error(`Division by 0`);
-        }
-        if (n === 1) {
-            return { value: a.value, fraction: a.fraction, currency: 
a.currency };
-        }
-        const r = a.value % n;
-        return {
-            currency: a.currency,
-            fraction: Math.floor((r * amountFractionalBase + a.fraction) / n),
-            value: Math.floor(a.value / n),
-        };
-    }
-    /**
-     * Check if an amount is non-zero.
-     */
-    static isNonZero(a) {
-        return a.value > 0 || a.fraction > 0;
-    }
-    static isZero(a) {
-        a = Amounts.jsonifyAmount(a);
-        return a.value === 0 && a.fraction === 0;
-    }
-    /**
-     * Parse an amount like 'EUR:20.5' for 20 Euros and 50 ct.
-     */
-    static parse(s) {
-        const res = s.match(/^([a-zA-Z0-9_*-]+):([0-9]+)([.][0-9]+)?$/);
-        if (!res) {
-            return undefined;
-        }
-        const tail = res[3] || ".0";
-        if (tail.length > amountFractionalLength + 1) {
-            return undefined;
-        }
-        const value = Number.parseInt(res[2]);
-        if (value > amountMaxValue) {
-            return undefined;
-        }
-        return {
-            currency: res[1],
-            fraction: Math.round(amountFractionalBase * 
Number.parseFloat(tail)),
-            value,
-        };
-    }
-    /**
-     * Parse amount in standard string form (like 'EUR:20.5'),
-     * throw if the input is not a valid amount.
-     */
-    static parseOrThrow(s) {
-        const res = Amounts.parse(s);
-        if (!res) {
-            throw Error(`Can't parse amount: "${s}"`);
-        }
-        return res;
-    }
-    /**
-     * Convert a float to a Taler amount.
-     * Loss of precision possible.
-     */
-    static fromFloat(floatVal, currency) {
-        return {
-            currency,
-            fraction: Math.floor((floatVal - Math.floor(floatVal)) * 
amountFractionalBase),
-            value: Math.floor(floatVal),
-        };
-    }
-    static min(a, b) {
-        const cr = Amounts.cmp(a, b);
-        if (cr >= 0) {
-            return Amounts.jsonifyAmount(b);
-        }
-        else {
-            return Amounts.jsonifyAmount(a);
-        }
-    }
-    static max(a, b) {
-        const cr = Amounts.cmp(a, b);
-        if (cr >= 0) {
-            return Amounts.jsonifyAmount(a);
-        }
-        else {
-            return Amounts.jsonifyAmount(b);
-        }
-    }
-    static mult(a, n) {
-        if (!Number.isInteger(n)) {
-            throw Error("amount can only be multipied by an integer");
-        }
-        if (n < 0) {
-            throw Error("amount can only be multiplied by a positive integer");
-        }
-        if (n == 0) {
-            return { amount: Amounts.getZero(a.currency), saturated: false };
-        }
-        let x = a;
-        let acc = Amounts.getZero(a.currency);
-        while (n > 1) {
-            if (n % 2 == 0) {
-                n = n / 2;
-            }
-            else {
-                n = (n - 1) / 2;
-                const r2 = Amounts.add(acc, x);
-                if (r2.saturated) {
-                    return r2;
-                }
-                acc = r2.amount;
-            }
-            const r2 = Amounts.add(x, x);
-            if (r2.saturated) {
-                return r2;
-            }
-            x = r2.amount;
-        }
-        return Amounts.add(acc, x);
-    }
-    /**
-     * Check if the argument is a valid amount in string form.
-     */
-    static check(a) {
-        if (typeof a !== "string") {
-            return false;
-        }
-        try {
-            const parsedAmount = Amounts.parse(a);
-            return !!parsedAmount;
-        }
-        catch (_a) {
-            return false;
-        }
-    }
-    /**
-     * Convert to standard human-readable string representation that's
-     * also used in JSON formats.
-     */
-    static stringify(a) {
-        a = Amounts.jsonifyAmount(a);
-        const av = a.value + Math.floor(a.fraction / amountFractionalBase);
-        const af = a.fraction % amountFractionalBase;
-        let s = av.toString();
-        if (af) {
-            s = s + ".";
-            let n = af;
-            for (let i = 0; i < amountFractionalLength; i++) {
-                if (!n) {
-                    break;
-                }
-                s = s + Math.floor((n / amountFractionalBase) * 10).toString();
-                n = (n * 10) % amountFractionalBase;
-            }
-        }
-        return `${a.currency}:${s}`;
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Types of coin sources.
- */
-var BackupCoinSourceType;
-(function (BackupCoinSourceType) {
-    BackupCoinSourceType["Withdraw"] = "withdraw";
-    BackupCoinSourceType["Refresh"] = "refresh";
-    BackupCoinSourceType["Tip"] = "tip";
-})(BackupCoinSourceType || (BackupCoinSourceType = {}));
-/**
- * Reasons for why a coin is being refreshed.
- */
-var BackupRefreshReason;
-(function (BackupRefreshReason) {
-    BackupRefreshReason["Manual"] = "manual";
-    BackupRefreshReason["Pay"] = "pay";
-    BackupRefreshReason["Refund"] = "refund";
-    BackupRefreshReason["AbortPay"] = "abort-pay";
-    BackupRefreshReason["Recoup"] = "recoup";
-    BackupRefreshReason["BackupRestored"] = "backup-restored";
-    BackupRefreshReason["Scheduled"] = "scheduled";
-})(BackupRefreshReason || (BackupRefreshReason = {}));
-var BackupRefundState;
-(function (BackupRefundState) {
-    BackupRefundState["Failed"] = "failed";
-    BackupRefundState["Applied"] = "applied";
-    BackupRefundState["Pending"] = "pending";
-})(BackupRefundState || (BackupRefundState = {}));
-var BackupProposalStatus;
-(function (BackupProposalStatus) {
-    /**
-     * Proposed (and either downloaded or not,
-     * depending on whether contract terms are present),
-     * but the user needs to accept/reject it.
-     */
-    BackupProposalStatus["Proposed"] = "proposed";
-    /**
-     * The user has rejected the proposal.
-     */
-    BackupProposalStatus["Refused"] = "refused";
-    /**
-     * Downloading or processing the proposal has failed permanently.
-     *
-     * FIXME:  Should this be modeled as a "misbehavior report" instead?
-     */
-    BackupProposalStatus["PermanentlyFailed"] = "permanently-failed";
-    /**
-     * Downloaded proposal was detected as a re-purchase.
-     */
-    BackupProposalStatus["Repurchase"] = "repurchase";
-})(BackupProposalStatus || (BackupProposalStatus = {}));
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-// globalThis polyfill, see https://mathiasbynens.be/notes/globalthis
-(function () {
-    if (typeof globalThis === "object")
-        return;
-    Object.defineProperty(Object.prototype, "__magic__", {
-        get: function () {
-            return this;
-        },
-        configurable: true, // This makes it possible to `delete` the getter 
later.
-    });
-    // @ts-ignore: polyfill magic
-    __magic__.globalThis = __magic__; // lolwat
-    // @ts-ignore: polyfill magic
-    delete Object.prototype.__magic__;
-})();
-// @ts-ignore
-const _URL = globalThis.URL;
-if (!_URL) {
-    throw Error("FATAL: URL not available");
-}
-const URL$1 = _URL;
-// @ts-ignore
-const _URLSearchParams = globalThis.URLSearchParams;
-if (!_URLSearchParams) {
-    throw Error("FATAL: URLSearchParams not available");
-}
-const URLSearchParams$1 = _URLSearchParams;
-
-/*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Show an amount in a form suitable for the user.
- * FIXME:  In the future, this should consider currency-specific
- * settings such as significant digits or currency symbols.
- */
-function amountToPretty(amount) {
-    const x = amount.value + amount.fraction / amountFractionalBase;
-    return `${x} ${amount.currency}`;
-}
-/**
- * Canonicalize a base url, typically for the exchange.
- *
- * See http://api.taler.net/wallet.html#general
- */
-function canonicalizeBaseUrl(url) {
-    if (!url.startsWith("http") && !url.startsWith("https")) {
-        url = "https://"; + url;
-    }
-    const x = new URL$1(url);
-    if (!x.pathname.endsWith("/")) {
-        x.pathname = x.pathname + "/";
-    }
-    x.search = "";
-    x.hash = "";
-    return x.href;
-}
-/**
- * Convert object to JSON with canonical ordering of keys
- * and whitespace omitted.
- *
- * See RFC 4885 (https://tools.ietf.org/html/rfc8785).
- */
-function canonicalJson(obj) {
-    // Check for cycles, etc.
-    obj = JSON.parse(JSON.stringify(obj));
-    if (typeof obj === "string") {
-        const s = JSON.stringify(obj);
-        return s.replace(/[\u007F-\uFFFF]/g, function (chr) {
-            return "\\u" + ("0000" + 
chr.charCodeAt(0).toString(16)).substr(-4);
-        });
-    }
-    if (typeof obj === "number" || typeof obj === "boolean" || obj === null) {
-        return JSON.stringify(obj);
-    }
-    if (Array.isArray(obj)) {
-        const objs = obj.map((e) => canonicalJson(e));
-        return `[${objs.join(",")}]`;
-    }
-    const keys = [];
-    for (const key in obj) {
-        keys.push(key);
-    }
-    keys.sort();
-    let s = "{";
-    for (let i = 0; i < keys.length; i++) {
-        const key = keys[i];
-        s += JSON.stringify(key) + ":" + canonicalJson(obj[key]);
-        if (i !== keys.length - 1) {
-            s += ",";
-        }
-    }
-    return s + "}";
-}
-/**
- * Lexically compare two strings.
- */
-function strcmp(s1, s2) {
-    if (s1 < s2) {
-        return -1;
-    }
-    if (s1 > s2) {
-        return 1;
-    }
-    return 0;
-}
-/**
- * Shorthand function for formatted JSON stringification.
- */
-function j2s(x) {
-    return JSON.stringify(x, undefined, 2);
-}
-
-/*
- This file is part of TALER
- (C) 2017 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Compare two libtool-style version strings.
- */
-function compare(me, other) {
-    const meVer = parseVersion(me);
-    const otherVer = parseVersion(other);
-    if (!(meVer && otherVer)) {
-        return undefined;
-    }
-    const compatible = meVer.current - meVer.age <= otherVer.current &&
-        meVer.current >= otherVer.current - otherVer.age;
-    const currentCmp = Math.sign(meVer.current - otherVer.current);
-    return { compatible, currentCmp };
-}
-function parseVersion(v) {
-    const [currentStr, revisionStr, ageStr, ...rest] = v.split(":");
-    if (rest.length !== 0) {
-        return undefined;
-    }
-    const current = Number.parseInt(currentStr);
-    const revision = Number.parseInt(revisionStr);
-    const age = Number.parseInt(ageStr);
-    if (Number.isNaN(current)) {
-        return undefined;
-    }
-    if (Number.isNaN(revision)) {
-        return undefined;
-    }
-    if (Number.isNaN(age)) {
-        return undefined;
-    }
-    return { current, revision, age };
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var NotificationType;
-(function (NotificationType) {
-    NotificationType["CoinWithdrawn"] = "coin-withdrawn";
-    NotificationType["ProposalAccepted"] = "proposal-accepted";
-    NotificationType["ProposalDownloaded"] = "proposal-downloaded";
-    NotificationType["RefundsSubmitted"] = "refunds-submitted";
-    NotificationType["RecoupStarted"] = "recoup-started";
-    NotificationType["RecoupFinished"] = "recoup-finished";
-    NotificationType["RefreshRevealed"] = "refresh-revealed";
-    NotificationType["RefreshMelted"] = "refresh-melted";
-    NotificationType["RefreshStarted"] = "refresh-started";
-    NotificationType["RefreshUnwarranted"] = "refresh-unwarranted";
-    NotificationType["ReserveUpdated"] = "reserve-updated";
-    NotificationType["ReserveConfirmed"] = "reserve-confirmed";
-    NotificationType["ReserveCreated"] = "reserve-created";
-    NotificationType["WithdrawGroupCreated"] = "withdraw-group-created";
-    NotificationType["WithdrawGroupFinished"] = "withdraw-group-finished";
-    NotificationType["WaitingForRetry"] = "waiting-for-retry";
-    NotificationType["RefundStarted"] = "refund-started";
-    NotificationType["RefundQueried"] = "refund-queried";
-    NotificationType["RefundFinished"] = "refund-finished";
-    NotificationType["ExchangeOperationError"] = "exchange-operation-error";
-    NotificationType["RefreshOperationError"] = "refresh-operation-error";
-    NotificationType["RecoupOperationError"] = "recoup-operation-error";
-    NotificationType["RefundApplyOperationError"] = "refund-apply-error";
-    NotificationType["RefundStatusOperationError"] = "refund-status-error";
-    NotificationType["ProposalOperationError"] = "proposal-error";
-    NotificationType["TipOperationError"] = "tip-error";
-    NotificationType["PayOperationError"] = "pay-error";
-    NotificationType["PayOperationSuccess"] = "pay-operation-success";
-    NotificationType["WithdrawOperationError"] = "withdraw-error";
-    NotificationType["ReserveNotYetFound"] = "reserve-not-yet-found";
-    NotificationType["ReserveOperationError"] = "reserve-error";
-    NotificationType["InternalError"] = "internal-error";
-    NotificationType["PendingOperationProcessed"] = 
"pending-operation-processed";
-    NotificationType["ProposalRefused"] = "proposal-refused";
-    NotificationType["ReserveRegisteredWithBank"] = 
"reserve-registered-with-bank";
-    NotificationType["DepositOperationError"] = "deposit-operation-error";
-})(NotificationType || (NotificationType = {}));
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const paytoPfx = "payto://";
-/**
- * Add query parameters to a payto URI
- */
-function addPaytoQueryParams(s, params) {
-    const [acct, search] = s.slice(paytoPfx.length).split("?");
-    const searchParams = new URLSearchParams$1(search || "");
-    for (const k of Object.keys(params)) {
-        searchParams.set(k, params[k]);
-    }
-    return paytoPfx + acct + "?" + searchParams.toString();
-}
-function parsePaytoUri(s) {
-    if (!s.startsWith(paytoPfx)) {
-        return undefined;
-    }
-    const [acct, search] = s.slice(paytoPfx.length).split("?");
-    const firstSlashPos = acct.indexOf("/");
-    if (firstSlashPos === -1) {
-        return undefined;
-    }
-    const targetType = acct.slice(0, firstSlashPos);
-    const targetPath = acct.slice(firstSlashPos + 1);
-    const params = {};
-    const searchParams = new URLSearchParams$1(search || "");
-    searchParams.forEach((v, k) => {
-        params[v] = k;
-    });
-    return {
-        targetPath,
-        targetType,
-        params,
-    };
-}
-
-/*
- This file is part of GNU Taler
- (C) 2017-2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-let timeshift = 0;
-function getTimestampNow() {
-    return {
-        t_ms: new Date().getTime() + timeshift,
-    };
-}
-function isTimestampExpired(t) {
-    return timestampCmp(t, getTimestampNow()) <= 0;
-}
-function getDurationRemaining(deadline, now = getTimestampNow()) {
-    if (deadline.t_ms === "never") {
-        return { d_ms: "forever" };
-    }
-    if (now.t_ms === "never") {
-        throw Error("invalid argument for 'now'");
-    }
-    if (deadline.t_ms < now.t_ms) {
-        return { d_ms: 0 };
-    }
-    return { d_ms: deadline.t_ms - now.t_ms };
-}
-function timestampMin(t1, t2) {
-    if (t1.t_ms === "never") {
-        return { t_ms: t2.t_ms };
-    }
-    if (t2.t_ms === "never") {
-        return { t_ms: t2.t_ms };
-    }
-    return { t_ms: Math.min(t1.t_ms, t2.t_ms) };
-}
-function timestampMax(t1, t2) {
-    if (t1.t_ms === "never") {
-        return { t_ms: "never" };
-    }
-    if (t2.t_ms === "never") {
-        return { t_ms: "never" };
-    }
-    return { t_ms: Math.max(t1.t_ms, t2.t_ms) };
-}
-const SECONDS = 1000;
-const MINUTES = SECONDS * 60;
-const HOURS = MINUTES * 60;
-const DAYS = HOURS * 24;
-const MONTHS = DAYS * 30;
-const YEARS = DAYS * 365;
-function durationFromSpec(spec) {
-    var _a, _b, _c, _d, _e, _f;
-    let d_ms = 0;
-    d_ms += ((_a = spec.seconds) !== null && _a !== void 0 ? _a : 0) * SECONDS;
-    d_ms += ((_b = spec.minutes) !== null && _b !== void 0 ? _b : 0) * MINUTES;
-    d_ms += ((_c = spec.hours) !== null && _c !== void 0 ? _c : 0) * HOURS;
-    d_ms += ((_d = spec.days) !== null && _d !== void 0 ? _d : 0) * DAYS;
-    d_ms += ((_e = spec.months) !== null && _e !== void 0 ? _e : 0) * MONTHS;
-    d_ms += ((_f = spec.years) !== null && _f !== void 0 ? _f : 0) * YEARS;
-    return { d_ms };
-}
-/**
- * Truncate a timestamp so that that it represents a multiple
- * of seconds.  The timestamp is always rounded down.
- */
-function timestampTruncateToSecond(t1) {
-    if (t1.t_ms === "never") {
-        return { t_ms: "never" };
-    }
-    return {
-        t_ms: Math.floor(t1.t_ms / 1000) * 1000,
-    };
-}
-function durationMin(d1, d2) {
-    if (d1.d_ms === "forever") {
-        return { d_ms: d2.d_ms };
-    }
-    if (d2.d_ms === "forever") {
-        return { d_ms: d2.d_ms };
-    }
-    return { d_ms: Math.min(d1.d_ms, d2.d_ms) };
-}
-function durationMax(d1, d2) {
-    if (d1.d_ms === "forever") {
-        return { d_ms: "forever" };
-    }
-    if (d2.d_ms === "forever") {
-        return { d_ms: "forever" };
-    }
-    return { d_ms: Math.max(d1.d_ms, d2.d_ms) };
-}
-function durationMul(d, n) {
-    if (d.d_ms === "forever") {
-        return { d_ms: "forever" };
-    }
-    return { d_ms: Math.round(d.d_ms * n) };
-}
-function timestampCmp(t1, t2) {
-    if (t1.t_ms === "never") {
-        if (t2.t_ms === "never") {
-            return 0;
-        }
-        return 1;
-    }
-    if (t2.t_ms === "never") {
-        return -1;
-    }
-    if (t1.t_ms == t2.t_ms) {
-        return 0;
-    }
-    if (t1.t_ms > t2.t_ms) {
-        return 1;
-    }
-    return -1;
-}
-function timestampAddDuration(t1, d) {
-    if (t1.t_ms === "never" || d.d_ms === "forever") {
-        return { t_ms: "never" };
-    }
-    return { t_ms: t1.t_ms + d.d_ms };
-}
-function timestampSubtractDuraction(t1, d) {
-    if (t1.t_ms === "never") {
-        return { t_ms: "never" };
-    }
-    if (d.d_ms === "forever") {
-        return { t_ms: 0 };
-    }
-    return { t_ms: Math.max(0, t1.t_ms - d.d_ms) };
-}
-function timestampDifference(t1, t2) {
-    if (t1.t_ms === "never") {
-        return { d_ms: "forever" };
-    }
-    if (t2.t_ms === "never") {
-        return { d_ms: "forever" };
-    }
-    return { d_ms: Math.abs(t1.t_ms - t2.t_ms) };
-}
-function timestampToIsoString(t) {
-    if (t.t_ms === "never") {
-        return "<never>";
-    }
-    else {
-        return new Date(t.t_ms).toISOString();
-    }
-}
-function timestampIsBetween(t, start, end) {
-    if (timestampCmp(t, start) < 0) {
-        return false;
-    }
-    if (timestampCmp(t, end) > 0) {
-        return false;
-    }
-    return true;
-}
-const codecForTimestamp = {
-    decode(x, c) {
-        const t_ms = x.t_ms;
-        if (typeof t_ms === "string") {
-            if (t_ms === "never") {
-                return { t_ms: "never" };
-            }
-            throw Error(`expected timestamp at ${renderContext(c)}`);
-        }
-        if (typeof t_ms === "number") {
-            return { t_ms };
-        }
-        throw Error(`expected timestamp at ${renderContext(c)}`);
-    },
-};
-const codecForDuration = {
-    decode(x, c) {
-        const d_ms = x.d_ms;
-        if (typeof d_ms === "string") {
-            if (d_ms === "forever") {
-                return { d_ms: "forever" };
-            }
-            throw Error(`expected duration at ${renderContext(c)}`);
-        }
-        if (typeof d_ms === "number") {
-            return { d_ms };
-        }
-        throw Error(`expected duration at ${renderContext(c)}`);
-    },
-};
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var ReserveTransactionType;
-(function (ReserveTransactionType) {
-    ReserveTransactionType["Withdraw"] = "WITHDRAW";
-    ReserveTransactionType["Credit"] = "CREDIT";
-    ReserveTransactionType["Recoup"] = "RECOUP";
-    ReserveTransactionType["Closing"] = "CLOSING";
-})(ReserveTransactionType || (ReserveTransactionType = {}));
-const codecForReserveWithdrawTransaction = () => buildCodecForObject()
-    .property("amount", codecForString())
-    .property("h_coin_envelope", codecForString())
-    .property("h_denom_pub", codecForString())
-    .property("reserve_sig", codecForString())
-    .property("type", codecForConstString(ReserveTransactionType.Withdraw))
-    .property("withdraw_fee", codecForString())
-    .build("ReserveWithdrawTransaction");
-const codecForReserveCreditTransaction = () => buildCodecForObject()
-    .property("amount", codecForString())
-    .property("sender_account_url", codecForString())
-    .property("timestamp", codecForTimestamp)
-    .property("wire_reference", codecForNumber())
-    .property("type", codecForConstString(ReserveTransactionType.Credit))
-    .build("ReserveCreditTransaction");
-const codecForReserveClosingTransaction = () => buildCodecForObject()
-    .property("amount", codecForString())
-    .property("closing_fee", codecForString())
-    .property("exchange_pub", codecForString())
-    .property("exchange_sig", codecForString())
-    .property("h_wire", codecForString())
-    .property("timestamp", codecForTimestamp)
-    .property("type", codecForConstString(ReserveTransactionType.Closing))
-    .property("wtid", codecForString())
-    .build("ReserveClosingTransaction");
-const codecForReserveRecoupTransaction = () => buildCodecForObject()
-    .property("amount", codecForString())
-    .property("coin_pub", codecForString())
-    .property("exchange_pub", codecForString())
-    .property("exchange_sig", codecForString())
-    .property("timestamp", codecForTimestamp)
-    .property("type", codecForConstString(ReserveTransactionType.Recoup))
-    .build("ReserveRecoupTransaction");
-const codecForReserveTransaction = () => buildCodecForUnion()
-    .discriminateOn("type")
-    .alternative(ReserveTransactionType.Withdraw, 
codecForReserveWithdrawTransaction())
-    .alternative(ReserveTransactionType.Closing, 
codecForReserveClosingTransaction())
-    .alternative(ReserveTransactionType.Recoup, 
codecForReserveRecoupTransaction())
-    .alternative(ReserveTransactionType.Credit, 
codecForReserveCreditTransaction())
-    .build("ReserveTransaction");
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const codecForReserveStatus = () => buildCodecForObject()
-    .property("balance", codecForString())
-    .property("history", codecForList(codecForReserveTransaction()))
-    .build("ReserveStatus");
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const codecForBankWithdrawalOperationPostResponse = () => buildCodecForObject()
-    .property("transfer_done", codecForBoolean())
-    .build("BankWithdrawalOperationPostResponse");
-const codecForDenomination = () => buildCodecForObject()
-    .property("value", codecForString())
-    .property("denom_pub", codecForString())
-    .property("fee_withdraw", codecForString())
-    .property("fee_deposit", codecForString())
-    .property("fee_refresh", codecForString())
-    .property("fee_refund", codecForString())
-    .property("stamp_start", codecForTimestamp)
-    .property("stamp_expire_withdraw", codecForTimestamp)
-    .property("stamp_expire_legal", codecForTimestamp)
-    .property("stamp_expire_deposit", codecForTimestamp)
-    .property("master_sig", codecForString())
-    .build("Denomination");
-const codecForAuditorDenomSig = () => buildCodecForObject()
-    .property("denom_pub_h", codecForString())
-    .property("auditor_sig", codecForString())
-    .build("AuditorDenomSig");
-const codecForAuditor = () => buildCodecForObject()
-    .property("auditor_pub", codecForString())
-    .property("auditor_url", codecForString())
-    .property("denomination_keys", codecForList(codecForAuditorDenomSig()))
-    .build("Auditor");
-const codecForExchangeHandle = () => buildCodecForObject()
-    .property("master_pub", codecForString())
-    .property("url", codecForString())
-    .build("ExchangeHandle");
-const codecForAuditorHandle = () => buildCodecForObject()
-    .property("name", codecForString())
-    .property("auditor_pub", codecForString())
-    .property("url", codecForString())
-    .build("AuditorHandle");
-const codecForLocation = () => buildCodecForObject()
-    .property("country", codecOptional(codecForString()))
-    .property("country_subdivision", codecOptional(codecForString()))
-    .property("building_name", codecOptional(codecForString()))
-    .property("building_number", codecOptional(codecForString()))
-    .property("district", codecOptional(codecForString()))
-    .property("street", codecOptional(codecForString()))
-    .property("post_code", codecOptional(codecForString()))
-    .property("town", codecOptional(codecForString()))
-    .property("town_location", codecOptional(codecForString()))
-    .property("address_lines", codecOptional(codecForList(codecForString())))
-    .build("Location");
-const codecForMerchantInfo = () => buildCodecForObject()
-    .property("name", codecForString())
-    .property("address", codecOptional(codecForLocation()))
-    .property("jurisdiction", codecOptional(codecForLocation()))
-    .build("MerchantInfo");
-const codecForInternationalizedString = () => codecForMap(codecForString());
-const codecForProduct = () => buildCodecForObject()
-    .property("product_id", codecOptional(codecForString()))
-    .property("description", codecForString())
-    .property("description_i18n", 
codecOptional(codecForInternationalizedString()))
-    .property("quantity", codecOptional(codecForNumber()))
-    .property("unit", codecOptional(codecForString()))
-    .property("price", codecOptional(codecForString()))
-    .build("Tax");
-const codecForContractTerms = () => buildCodecForObject()
-    .property("order_id", codecForString())
-    .property("fulfillment_url", codecOptional(codecForString()))
-    .property("fulfillment_message", codecOptional(codecForString()))
-    .property("fulfillment_message_i18n", 
codecOptional(codecForInternationalizedString()))
-    .property("merchant_base_url", codecForString())
-    .property("h_wire", codecForString())
-    .property("auto_refund", codecOptional(codecForDuration))
-    .property("wire_method", codecForString())
-    .property("summary", codecForString())
-    .property("summary_i18n", codecOptional(codecForInternationalizedString()))
-    .property("nonce", codecForString())
-    .property("amount", codecForString())
-    .property("auditors", codecForList(codecForAuditorHandle()))
-    .property("pay_deadline", codecForTimestamp)
-    .property("refund_deadline", codecForTimestamp)
-    .property("wire_transfer_deadline", codecForTimestamp)
-    .property("timestamp", codecForTimestamp)
-    .property("delivery_location", codecOptional(codecForLocation()))
-    .property("delivery_date", codecOptional(codecForTimestamp))
-    .property("max_fee", codecForString())
-    .property("max_wire_fee", codecOptional(codecForString()))
-    .property("merchant", codecForMerchantInfo())
-    .property("merchant_pub", codecForString())
-    .property("exchanges", codecForList(codecForExchangeHandle()))
-    .property("products", codecOptional(codecForList(codecForProduct())))
-    .property("extra", codecForAny())
-    .build("ContractTerms");
-const codecForBlindSigWrapper = () => buildCodecForObject()
-    .property("blind_sig", codecForString())
-    .build("BlindSigWrapper");
-const codecForTipResponse = () => buildCodecForObject()
-    .property("blind_sigs", codecForList(codecForBlindSigWrapper()))
-    .build("TipResponse");
-const codecForRecoup = () => buildCodecForObject()
-    .property("h_denom_pub", codecForString())
-    .build("Recoup");
-const codecForExchangeSigningKey = () => buildCodecForObject()
-    .property("key", codecForString())
-    .property("master_sig", codecForString())
-    .property("stamp_end", codecForTimestamp)
-    .property("stamp_start", codecForTimestamp)
-    .property("stamp_expire", codecForTimestamp)
-    .build("ExchangeSignKeyJson");
-const codecForExchangeKeysJson = () => buildCodecForObject()
-    .property("denoms", codecForList(codecForDenomination()))
-    .property("master_public_key", codecForString())
-    .property("auditors", codecForList(codecForAuditor()))
-    .property("list_issue_date", codecForTimestamp)
-    .property("recoup", codecOptional(codecForList(codecForRecoup())))
-    .property("signkeys", codecForList(codecForExchangeSigningKey()))
-    .property("version", codecForString())
-    .property("reserve_closing_delay", codecForDuration)
-    .build("KeysJson");
-const codecForWireFeesJson = () => buildCodecForObject()
-    .property("wire_fee", codecForString())
-    .property("closing_fee", codecForString())
-    .property("sig", codecForString())
-    .property("start_date", codecForTimestamp)
-    .property("end_date", codecForTimestamp)
-    .build("WireFeesJson");
-const codecForAccountInfo = () => buildCodecForObject()
-    .property("payto_uri", codecForString())
-    .property("master_sig", codecForString())
-    .build("AccountInfo");
-const codecForExchangeWireJson = () => buildCodecForObject()
-    .property("accounts", codecForList(codecForAccountInfo()))
-    .property("fees", codecForMap(codecForList(codecForWireFeesJson())))
-    .build("ExchangeWireJson");
-const codecForProposal = () => buildCodecForObject()
-    .property("contract_terms", codecForAny())
-    .property("sig", codecForString())
-    .build("Proposal");
-const codecForCheckPaymentResponse = () => buildCodecForObject()
-    .property("order_status", codecForString())
-    .property("refunded", codecOptional(codecForBoolean()))
-    .property("refunded_amount", codecOptional(codecForString()))
-    .property("contract_terms", codecOptional(codecForAny()))
-    .property("taler_pay_uri", codecOptional(codecForString()))
-    .property("contract_url", codecOptional(codecForString()))
-    .build("CheckPaymentResponse");
-const codecForWithdrawOperationStatusResponse = () => buildCodecForObject()
-    .property("selection_done", codecForBoolean())
-    .property("transfer_done", codecForBoolean())
-    .property("aborted", codecForBoolean())
-    .property("amount", codecForString())
-    .property("sender_wire", codecOptional(codecForString()))
-    .property("suggested_exchange", codecOptional(codecForString()))
-    .property("confirm_transfer_url", codecOptional(codecForString()))
-    .property("wire_types", codecForList(codecForString()))
-    .build("WithdrawOperationStatusResponse");
-const codecForTipPickupGetResponse = () => buildCodecForObject()
-    .property("tip_amount", codecForString())
-    .property("exchange_url", codecForString())
-    .property("expiration", codecForTimestamp)
-    .build("TipPickupGetResponse");
-const codecForRecoupConfirmation = () => buildCodecForObject()
-    .property("reserve_pub", codecOptional(codecForString()))
-    .property("old_coin_pub", codecOptional(codecForString()))
-    .build("RecoupConfirmation");
-const codecForWithdrawResponse = () => buildCodecForObject()
-    .property("ev_sig", codecForString())
-    .build("WithdrawResponse");
-const codecForMerchantPayResponse = () => buildCodecForObject()
-    .property("sig", codecForString())
-    .build("MerchantPayResponse");
-const codecForExchangeMeltResponse = () => buildCodecForObject()
-    .property("exchange_pub", codecForString())
-    .property("exchange_sig", codecForString())
-    .property("noreveal_index", codecForNumber())
-    .property("refresh_base_url", codecOptional(codecForString()))
-    .build("ExchangeMeltResponse");
-const codecForExchangeRevealItem = () => buildCodecForObject()
-    .property("ev_sig", codecForString())
-    .build("ExchangeRevealItem");
-const codecForExchangeRevealResponse = () => buildCodecForObject()
-    .property("ev_sigs", codecForList(codecForExchangeRevealItem()))
-    .build("ExchangeRevealResponse");
-const codecForMerchantCoinRefundSuccessStatus = () => buildCodecForObject()
-    .property("type", codecForConstString("success"))
-    .property("coin_pub", codecForString())
-    .property("exchange_status", codecForConstNumber(200))
-    .property("exchange_sig", codecForString())
-    .property("rtransaction_id", codecForNumber())
-    .property("refund_amount", codecForString())
-    .property("exchange_pub", codecForString())
-    .property("execution_time", codecForTimestamp)
-    .build("MerchantCoinRefundSuccessStatus");
-const codecForMerchantCoinRefundFailureStatus = () => buildCodecForObject()
-    .property("type", codecForConstString("failure"))
-    .property("coin_pub", codecForString())
-    .property("exchange_status", codecForNumber())
-    .property("rtransaction_id", codecForNumber())
-    .property("refund_amount", codecForString())
-    .property("exchange_code", codecOptional(codecForNumber()))
-    .property("exchange_reply", codecOptional(codecForAny()))
-    .property("execution_time", codecForTimestamp)
-    .build("MerchantCoinRefundFailureStatus");
-const codecForMerchantCoinRefundStatus = () => buildCodecForUnion()
-    .discriminateOn("type")
-    .alternative("success", codecForMerchantCoinRefundSuccessStatus())
-    .alternative("failure", codecForMerchantCoinRefundFailureStatus())
-    .build("MerchantCoinRefundStatus");
-const codecForMerchantOrderRefundPickupResponse = () => buildCodecForObject()
-    .property("merchant_pub", codecForString())
-    .property("refund_amount", codecForString())
-    .property("refunds", codecForList(codecForMerchantCoinRefundStatus()))
-    .build("MerchantOrderRefundPickupResponse");
-const codecForAbortResponse = () => buildCodecForObject()
-    .property("refunds", codecForList(codecForMerchantAbortPayRefundStatus()))
-    .build("AbortResponse");
-const codecForMerchantAbortPayRefundSuccessStatus = () => buildCodecForObject()
-    .property("exchange_pub", codecForString())
-    .property("exchange_sig", codecForString())
-    .property("exchange_status", codecForConstNumber(200))
-    .property("type", codecForConstString("success"))
-    .build("MerchantAbortPayRefundSuccessStatus");
-const codecForMerchantAbortPayRefundFailureStatus = () => buildCodecForObject()
-    .property("exchange_code", codecForNumber())
-    .property("exchange_reply", codecForAny())
-    .property("exchange_status", codecForNumber())
-    .property("type", codecForConstString("failure"))
-    .build("MerchantAbortPayRefundFailureStatus");
-const codecForMerchantAbortPayRefundStatus = () => buildCodecForUnion()
-    .discriminateOn("type")
-    .alternative("success", codecForMerchantAbortPayRefundSuccessStatus())
-    .alternative("failure", codecForMerchantAbortPayRefundFailureStatus())
-    .build("MerchantAbortPayRefundStatus");
-const codecForTalerConfigResponse = () => buildCodecForObject()
-    .property("name", codecForString())
-    .property("version", codecForString())
-    .property("currency", codecOptional(codecForString()))
-    .build("TalerConfigResponse");
-
-/*
- This file is part of GNU Taler
- (C) 2019-2020 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Parse a taler[+http]://withdraw URI.
- * Return undefined if not passed a valid URI.
- */
-function parseWithdrawUri(s) {
-    const pi = parseProtoInfo(s, "withdraw");
-    if (!pi) {
-        return undefined;
-    }
-    const parts = pi.rest.split("/");
-    if (parts.length < 2) {
-        return undefined;
-    }
-    const host = parts[0].toLowerCase();
-    const pathSegments = parts.slice(1, parts.length - 1);
-    const withdrawId = parts[parts.length - 1];
-    const p = [host, ...pathSegments].join("/");
-    return {
-        bankIntegrationApiBaseUrl: 
canonicalizeBaseUrl(`${pi.innerProto}://${p}/`),
-        withdrawalOperationId: withdrawId,
-    };
-}
-var TalerUriType;
-(function (TalerUriType) {
-    TalerUriType["TalerPay"] = "taler-pay";
-    TalerUriType["TalerWithdraw"] = "taler-withdraw";
-    TalerUriType["TalerTip"] = "taler-tip";
-    TalerUriType["TalerRefund"] = "taler-refund";
-    TalerUriType["TalerNotifyReserve"] = "taler-notify-reserve";
-    TalerUriType["Unknown"] = "unknown";
-})(TalerUriType || (TalerUriType = {}));
-function parseProtoInfo(s, action) {
-    const pfxPlain = `taler://${action}/`;
-    const pfxHttp = `taler+http://${action}/`;
-    if (s.toLowerCase().startsWith(pfxPlain)) {
-        return {
-            innerProto: "https",
-            rest: s.substring(pfxPlain.length),
-        };
-    }
-    else if (s.toLowerCase().startsWith(pfxHttp)) {
-        return {
-            innerProto: "http",
-            rest: s.substring(pfxHttp.length),
-        };
-    }
-    else {
-        return undefined;
-    }
-}
-/**
- * Parse a taler[+http]://pay URI.
- * Return undefined if not passed a valid URI.
- */
-function parsePayUri(s) {
-    var _a, _b;
-    const pi = parseProtoInfo(s, "pay");
-    if (!pi) {
-        return undefined;
-    }
-    const c = pi === null || pi === void 0 ? void 0 : pi.rest.split("?");
-    const q = new URLSearchParams$1((_a = c[1]) !== null && _a !== void 0 ? _a 
: "");
-    const claimToken = (_b = q.get("c")) !== null && _b !== void 0 ? _b : 
undefined;
-    const parts = c[0].split("/");
-    if (parts.length < 3) {
-        return undefined;
-    }
-    const host = parts[0].toLowerCase();
-    const sessionId = parts[parts.length - 1];
-    const orderId = parts[parts.length - 2];
-    const pathSegments = parts.slice(1, parts.length - 2);
-    const p = [host, ...pathSegments].join("/");
-    const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
-    return {
-        merchantBaseUrl,
-        orderId,
-        sessionId: sessionId,
-        claimToken,
-    };
-}
-/**
- * Parse a taler[+http]://tip URI.
- * Return undefined if not passed a valid URI.
- */
-function parseTipUri(s) {
-    const pi = parseProtoInfo(s, "tip");
-    if (!pi) {
-        return undefined;
-    }
-    const c = pi === null || pi === void 0 ? void 0 : pi.rest.split("?");
-    const parts = c[0].split("/");
-    if (parts.length < 2) {
-        return undefined;
-    }
-    const host = parts[0].toLowerCase();
-    const tipId = parts[parts.length - 1];
-    const pathSegments = parts.slice(1, parts.length - 1);
-    const p = [host, ...pathSegments].join("/");
-    const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
-    return {
-        merchantBaseUrl,
-        merchantTipId: tipId,
-    };
-}
-/**
- * Parse a taler[+http]://refund URI.
- * Return undefined if not passed a valid URI.
- */
-function parseRefundUri(s) {
-    const pi = parseProtoInfo(s, "refund");
-    if (!pi) {
-        return undefined;
-    }
-    const c = pi === null || pi === void 0 ? void 0 : pi.rest.split("?");
-    const parts = c[0].split("/");
-    if (parts.length < 3) {
-        return undefined;
-    }
-    const host = parts[0].toLowerCase();
-    parts[parts.length - 1];
-    const orderId = parts[parts.length - 2];
-    const pathSegments = parts.slice(1, parts.length - 2);
-    const p = [host, ...pathSegments].join("/");
-    const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
-    return {
-        merchantBaseUrl,
-        orderId,
-    };
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var TransactionType;
-(function (TransactionType) {
-    TransactionType["Withdrawal"] = "withdrawal";
-    TransactionType["Payment"] = "payment";
-    TransactionType["Refund"] = "refund";
-    TransactionType["Refresh"] = "refresh";
-    TransactionType["Tip"] = "tip";
-    TransactionType["Deposit"] = "deposit";
-})(TransactionType || (TransactionType = {}));
-var WithdrawalType;
-(function (WithdrawalType) {
-    WithdrawalType["TalerBankIntegrationApi"] = "taler-bank-integration-api";
-    WithdrawalType["ManualTransfer"] = "manual-transfer";
-})(WithdrawalType || (WithdrawalType = {}));
-var PaymentStatus;
-(function (PaymentStatus) {
-    /**
-     * Explicitly aborted after timeout / failure
-     */
-    PaymentStatus["Aborted"] = "aborted";
-    /**
-     * Payment failed, wallet will auto-retry.
-     * User should be given the option to retry now / abort.
-     */
-    PaymentStatus["Failed"] = "failed";
-    /**
-     * Paid successfully
-     */
-    PaymentStatus["Paid"] = "paid";
-    /**
-     * User accepted, payment is processing.
-     */
-    PaymentStatus["Accepted"] = "accepted";
-})(PaymentStatus || (PaymentStatus = {}));
-const codecForTransactionsRequest = () => buildCodecForObject()
-    .property("currency", codecOptional(codecForString()))
-    .property("search", codecOptional(codecForString()))
-    .build("TransactionsRequest");
-
-/*
- This file is part of GNU Taler
- (C) 2015-2020 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var ConfirmPayResultType;
-(function (ConfirmPayResultType) {
-    ConfirmPayResultType["Done"] = "done";
-    ConfirmPayResultType["Pending"] = "pending";
-})(ConfirmPayResultType || (ConfirmPayResultType = {}));
-var PreparePayResultType;
-(function (PreparePayResultType) {
-    PreparePayResultType["PaymentPossible"] = "payment-possible";
-    PreparePayResultType["InsufficientBalance"] = "insufficient-balance";
-    PreparePayResultType["AlreadyConfirmed"] = "already-confirmed";
-})(PreparePayResultType || (PreparePayResultType = {}));
-/**
- * Reasons for why a coin is being refreshed.
- */
-var RefreshReason;
-(function (RefreshReason) {
-    RefreshReason["Manual"] = "manual";
-    RefreshReason["Pay"] = "pay";
-    RefreshReason["Refund"] = "refund";
-    RefreshReason["AbortPay"] = "abort-pay";
-    RefreshReason["Recoup"] = "recoup";
-    RefreshReason["BackupRestored"] = "backup-restored";
-    RefreshReason["Scheduled"] = "scheduled";
-})(RefreshReason || (RefreshReason = {}));
-const codecForTestPayArgs = () => buildCodecForObject()
-    .property("merchantBaseUrl", codecForString())
-    .property("merchantAuthToken", codecOptional(codecForString()))
-    .property("amount", codecForString())
-    .property("summary", codecForString())
-    .build("TestPayArgs");
-const codecForIntegrationTestArgs = () => buildCodecForObject()
-    .property("exchangeBaseUrl", codecForString())
-    .property("bankBaseUrl", codecForString())
-    .property("merchantBaseUrl", codecForString())
-    .property("merchantAuthToken", codecOptional(codecForString()))
-    .property("amountToSpend", codecForAmountString())
-    .property("amountToWithdraw", codecForAmountString())
-    .build("IntegrationTestArgs");
-const codecForAddExchangeRequest = () => buildCodecForObject()
-    .property("exchangeBaseUrl", codecForString())
-    .property("forceUpdate", codecOptional(codecForBoolean()))
-    .build("AddExchangeRequest");
-const codecForGetExchangeTosRequest = () => buildCodecForObject()
-    .property("exchangeBaseUrl", codecForString())
-    .build("GetExchangeTosRequest");
-const codecForAcceptManualWithdrawalRequet = () => buildCodecForObject()
-    .property("exchangeBaseUrl", codecForString())
-    .property("amount", codecForString())
-    .build("AcceptManualWithdrawalRequest");
-const codecForAcceptBankIntegratedWithdrawalRequest = () => 
buildCodecForObject()
-    .property("exchangeBaseUrl", codecForString())
-    .property("talerWithdrawUri", codecForString())
-    .build("AcceptBankIntegratedWithdrawalRequest");
-const codecForGetWithdrawalDetailsForAmountRequest = () => 
buildCodecForObject()
-    .property("exchangeBaseUrl", codecForString())
-    .property("amount", codecForString())
-    .build("GetWithdrawalDetailsForAmountRequest");
-const codecForAcceptExchangeTosRequest = () => buildCodecForObject()
-    .property("exchangeBaseUrl", codecForString())
-    .property("etag", codecForString())
-    .build("AcceptExchangeTosRequest");
-const codecForApplyRefundRequest = () => buildCodecForObject()
-    .property("talerRefundUri", codecForString())
-    .build("ApplyRefundRequest");
-const codecForGetWithdrawalDetailsForUri = () => buildCodecForObject()
-    .property("talerWithdrawUri", codecForString())
-    .build("GetWithdrawalDetailsForUriRequest");
-const codecForPreparePayRequest = () => buildCodecForObject()
-    .property("talerPayUri", codecForString())
-    .build("PreparePay");
-const codecForConfirmPayRequest = () => buildCodecForObject()
-    .property("proposalId", codecForString())
-    .property("sessionId", codecOptional(codecForString()))
-    .build("ConfirmPay");
-/**
- * Strategy for loading recovery information.
- */
-var RecoveryMergeStrategy;
-(function (RecoveryMergeStrategy) {
-    /**
-     * Keep the local wallet root key, import and take over providers.
-     */
-    RecoveryMergeStrategy["Ours"] = "ours";
-    /**
-     * Migrate to the wallet root key from the recovery information.
-     */
-    RecoveryMergeStrategy["Theirs"] = "theirs";
-})(RecoveryMergeStrategy || (RecoveryMergeStrategy = {}));
-const codecForWithdrawTestBalance = () => buildCodecForObject()
-    .property("amount", codecForString())
-    .property("bankBaseUrl", codecForString())
-    .property("exchangeBaseUrl", codecForString())
-    .build("WithdrawTestBalanceRequest");
-const codecForSetCoinSuspendedRequest = () => buildCodecForObject()
-    .property("coinPub", codecForString())
-    .property("suspended", codecForBoolean())
-    .build("SetCoinSuspendedRequest");
-const codecForForceRefreshRequest = () => buildCodecForObject()
-    .property("coinPubList", codecForList(codecForString()))
-    .build("ForceRefreshRequest");
-const codecForPrepareTipRequest = () => buildCodecForObject()
-    .property("talerTipUri", codecForString())
-    .build("PrepareTipRequest");
-const codecForAcceptTipRequest = () => buildCodecForObject()
-    .property("walletTipId", codecForString())
-    .build("AcceptTipRequest");
-const codecForAbortPayWithRefundRequest = () => buildCodecForObject()
-    .property("proposalId", codecForString())
-    .build("AbortPayWithRefundRequest");
-const codecForCreateDepositGroupRequest = () => buildCodecForObject()
-    .property("amount", codecForAmountString())
-    .property("depositPaytoUri", codecForString())
-    .build("CreateDepositGroupRequest");
-const codecForTrackDepositGroupRequest = () => buildCodecForObject()
-    .property("depositGroupId", codecForAmountString())
-    .build("TrackDepositGroupRequest");
-const codecForDeleteTransactionRequest = () => buildCodecForObject()
-    .property("transactionId", codecForString())
-    .build("DeleteTransactionRequest");
-const codecForRetryTransactionRequest = () => buildCodecForObject()
-    .property("transactionId", codecForString())
-    .build("RetryTransactionRequest");
-const codecForSetWalletDeviceIdRequest = () => buildCodecForObject()
-    .property("walletDeviceId", codecForString())
-    .build("SetWalletDeviceIdRequest");
-
-/*! 
*****************************************************************************
-Copyright (c) Microsoft Corporation.
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-PERFORMANCE OF THIS SOFTWARE.
-***************************************************************************** 
*/
-
-function __awaiter(thisArg, _arguments, P, generator) {
-    function adopt(value) { return value instanceof P ? value : new P(function 
(resolve) { resolve(value); }); }
-    return new (P || (P = Promise))(function (resolve, reject) {
-        function fulfilled(value) { try { step(generator.next(value)); } catch 
(e) { reject(e); } }
-        function rejected(value) { try { step(generator["throw"](value)); } 
catch (e) { reject(e); } }
-        function step(result) { result.done ? resolve(result.value) : 
adopt(result.value).then(fulfilled, rejected); }
-        step((generator = generator.apply(thisArg, _arguments || [])).next());
-    });
-}
-
-function createCommonjsModule(fn) {
-  var module = { exports: {} };
-       return fn(module, module.exports), module.exports;
-}
-
-/**
- * @preserve jed.js https://github.com/SlexAxton/Jed
- */
-
-createCommonjsModule(function (module, exports) {
-/*
------------
-A gettext compatible i18n library for modern JavaScript Applications
-
-by Alex Sexton - AlexSexton [at] gmail - @SlexAxton
-
-MIT License
-
-A jQuery Foundation project - requires CLA to contribute -
-https://contribute.jquery.org/CLA/
-
-
-
-Jed offers the entire applicable GNU gettext spec'd set of
-functions, but also offers some nicer wrappers around them.
-The api for gettext was written for a language with no function
-overloading, so Jed allows a little more of that.
-
-Many thanks to Joshua I. Miller - unrtst@cpan.org - who wrote
-gettext.js back in 2008. I was able to vet a lot of my ideas
-against his. I also made sure Jed passed against his tests
-in order to offer easy upgrades -- jsgettext.berlios.de
-*/
-(function (root, undef) {
-
-  // Set up some underscore-style functions, if you already have
-  // underscore, feel free to delete this section, and use it
-  // directly, however, the amount of functions used doesn't
-  // warrant having underscore as a full dependency.
-  // Underscore 1.3.0 was used to port and is licensed
-  // under the MIT License by Jeremy Ashkenas.
-  var ArrayProto    = Array.prototype,
-      ObjProto      = Object.prototype,
-      slice         = ArrayProto.slice,
-      hasOwnProp    = ObjProto.hasOwnProperty,
-      nativeForEach = ArrayProto.forEach,
-      breaker       = {};
-
-  // We're not using the OOP style _ so we don't need the
-  // extra level of indirection. This still means that you
-  // sub out for real `_` though.
-  var _ = {
-    forEach : function( obj, iterator, context ) {
-      var i, l, key;
-      if ( obj === null ) {
-        return;
-      }
-
-      if ( nativeForEach && obj.forEach === nativeForEach ) {
-        obj.forEach( iterator, context );
-      }
-      else if ( obj.length === +obj.length ) {
-        for ( i = 0, l = obj.length; i < l; i++ ) {
-          if ( i in obj && iterator.call( context, obj[i], i, obj ) === 
breaker ) {
-            return;
-          }
-        }
-      }
-      else {
-        for ( key in obj) {
-          if ( hasOwnProp.call( obj, key ) ) {
-            if ( iterator.call (context, obj[key], key, obj ) === breaker ) {
-              return;
-            }
-          }
-        }
-      }
-    },
-    extend : function( obj ) {
-      this.forEach( slice.call( arguments, 1 ), function ( source ) {
-        for ( var prop in source ) {
-          obj[prop] = source[prop];
-        }
-      });
-      return obj;
-    }
-  };
-  // END Miniature underscore impl
-
-  // Jed is a constructor function
-  var Jed = function ( options ) {
-    // Some minimal defaults
-    this.defaults = {
-      "locale_data" : {
-        "messages" : {
-          "" : {
-            "domain"       : "messages",
-            "lang"         : "en",
-            "plural_forms" : "nplurals=2; plural=(n != 1);"
-          }
-          // There are no default keys, though
-        }
-      },
-      // The default domain if one is missing
-      "domain" : "messages",
-      // enable debug mode to log untranslated strings to the console
-      "debug" : false
-    };
-
-    // Mix in the sent options with the default options
-    this.options = _.extend( {}, this.defaults, options );
-    this.textdomain( this.options.domain );
-
-    if ( options.domain && ! this.options.locale_data[ this.options.domain ] ) 
{
-      throw new Error('Text domain set to non-existent domain: `' + 
options.domain + '`');
-    }
-  };
-
-  // The gettext spec sets this character as the default
-  // delimiter for context lookups.
-  // e.g.: context\u0004key
-  // If your translation company uses something different,
-  // just change this at any time and it will use that instead.
-  Jed.context_delimiter = String.fromCharCode( 4 );
-
-  function getPluralFormFunc ( plural_form_string ) {
-    return Jed.PF.compile( plural_form_string || "nplurals=2; plural=(n != 
1);");
-  }
-
-  function Chain( key, i18n ){
-    this._key = key;
-    this._i18n = i18n;
-  }
-
-  // Create a chainable api for adding args prettily
-  _.extend( Chain.prototype, {
-    onDomain : function ( domain ) {
-      this._domain = domain;
-      return this;
-    },
-    withContext : function ( context ) {
-      this._context = context;
-      return this;
-    },
-    ifPlural : function ( num, pkey ) {
-      this._val = num;
-      this._pkey = pkey;
-      return this;
-    },
-    fetch : function ( sArr ) {
-      if ( {}.toString.call( sArr ) != '[object Array]' ) {
-        sArr = [].slice.call(arguments, 0);
-      }
-      return ( sArr && sArr.length ? Jed.sprintf : function(x){ return x; } )(
-        this._i18n.dcnpgettext(this._domain, this._context, this._key, 
this._pkey, this._val),
-        sArr
-      );
-    }
-  });
-
-  // Add functions to the Jed prototype.
-  // These will be the functions on the object that's returned
-  // from creating a `new Jed()`
-  // These seem redundant, but they gzip pretty well.
-  _.extend( Jed.prototype, {
-    // The sexier api start point
-    translate : function ( key ) {
-      return new Chain( key, this );
-    },
-
-    textdomain : function ( domain ) {
-      if ( ! domain ) {
-        return this._textdomain;
-      }
-      this._textdomain = domain;
-    },
-
-    gettext : function ( key ) {
-      return this.dcnpgettext.call( this, undef, undef, key );
-    },
-
-    dgettext : function ( domain, key ) {
-     return this.dcnpgettext.call( this, domain, undef, key );
-    },
-
-    dcgettext : function ( domain , key /*, category */ ) {
-      // Ignores the category anyways
-      return this.dcnpgettext.call( this, domain, undef, key );
-    },
-
-    ngettext : function ( skey, pkey, val ) {
-      return this.dcnpgettext.call( this, undef, undef, skey, pkey, val );
-    },
-
-    dngettext : function ( domain, skey, pkey, val ) {
-      return this.dcnpgettext.call( this, domain, undef, skey, pkey, val );
-    },
-
-    dcngettext : function ( domain, skey, pkey, val/*, category */) {
-      return this.dcnpgettext.call( this, domain, undef, skey, pkey, val );
-    },
-
-    pgettext : function ( context, key ) {
-      return this.dcnpgettext.call( this, undef, context, key );
-    },
-
-    dpgettext : function ( domain, context, key ) {
-      return this.dcnpgettext.call( this, domain, context, key );
-    },
-
-    dcpgettext : function ( domain, context, key/*, category */) {
-      return this.dcnpgettext.call( this, domain, context, key );
-    },
-
-    npgettext : function ( context, skey, pkey, val ) {
-      return this.dcnpgettext.call( this, undef, context, skey, pkey, val );
-    },
-
-    dnpgettext : function ( domain, context, skey, pkey, val ) {
-      return this.dcnpgettext.call( this, domain, context, skey, pkey, val );
-    },
-
-    // The most fully qualified gettext function. It has every option.
-    // Since it has every option, we can use it from every other method.
-    // This is the bread and butter.
-    // Technically there should be one more argument in this function for 
'Category',
-    // but since we never use it, we might as well not waste the bytes to 
define it.
-    dcnpgettext : function ( domain, context, singular_key, plural_key, val ) {
-      // Set some defaults
-
-      plural_key = plural_key || singular_key;
-
-      // Use the global domain default if one
-      // isn't explicitly passed in
-      domain = domain || this._textdomain;
-
-      var fallback;
-
-      // Handle special cases
-
-      // No options found
-      if ( ! this.options ) {
-        // There's likely something wrong, but we'll return the correct key 
for english
-        // We do this by instantiating a brand new Jed instance with the 
default set
-        // for everything that could be broken.
-        fallback = new Jed();
-        return fallback.dcnpgettext.call( fallback, undefined, undefined, 
singular_key, plural_key, val );
-      }
-
-      // No translation data provided
-      if ( ! this.options.locale_data ) {
-        throw new Error('No locale data provided.');
-      }
-
-      if ( ! this.options.locale_data[ domain ] ) {
-        throw new Error('Domain `' + domain + '` was not found.');
-      }
-
-      if ( ! this.options.locale_data[ domain ][ "" ] ) {
-        throw new Error('No locale meta information provided.');
-      }
-
-      // Make sure we have a truthy key. Otherwise we might start looking
-      // into the empty string key, which is the options for the locale
-      // data.
-      if ( ! singular_key ) {
-        throw new Error('No translation key found.');
-      }
-
-      var key  = context ? context + Jed.context_delimiter + singular_key : 
singular_key,
-          locale_data = this.options.locale_data,
-          dict = locale_data[ domain ],
-          defaultConf = (locale_data.messages || 
this.defaults.locale_data.messages)[""],
-          pluralForms = dict[""].plural_forms || dict[""]["Plural-Forms"] || 
dict[""]["plural-forms"] || defaultConf.plural_forms || 
defaultConf["Plural-Forms"] || defaultConf["plural-forms"],
-          val_list,
-          res;
-
-      var val_idx;
-      if (val === undefined) {
-        // No value passed in; assume singular key lookup.
-        val_idx = 0;
-
-      } else {
-        // Value has been passed in; use plural-forms calculations.
-
-        // Handle invalid numbers, but try casting strings for good measure
-        if ( typeof val != 'number' ) {
-          val = parseInt( val, 10 );
-
-          if ( isNaN( val ) ) {
-            throw new Error('The number that was passed in is not a number.');
-          }
-        }
-
-        val_idx = getPluralFormFunc(pluralForms)(val);
-      }
-
-      // Throw an error if a domain isn't found
-      if ( ! dict ) {
-        throw new Error('No domain named `' + domain + '` could be found.');
-      }
-
-      val_list = dict[ key ];
-
-      // If there is no match, then revert back to
-      // english style singular/plural with the keys passed in.
-      if ( ! val_list || val_idx > val_list.length ) {
-        if (this.options.missing_key_callback) {
-          this.options.missing_key_callback(key, domain);
-        }
-        res = [ singular_key, plural_key ];
-
-        // collect untranslated strings
-        if (this.options.debug===true) {
-          console.log(res[ getPluralFormFunc(pluralForms)( val ) ]);
-        }
-        return res[ getPluralFormFunc()( val ) ];
-      }
-
-      res = val_list[ val_idx ];
-
-      // This includes empty strings on purpose
-      if ( ! res  ) {
-        res = [ singular_key, plural_key ];
-        return res[ getPluralFormFunc()( val ) ];
-      }
-      return res;
-    }
-  });
-
-
-  // We add in sprintf capabilities for post translation value interolation
-  // This is not internally used, so you can remove it if you have this
-  // available somewhere else, or want to use a different system.
-
-  // We _slightly_ modify the normal sprintf behavior to more gracefully handle
-  // undefined values.
-
-  /**
-   sprintf() for JavaScript 0.7-beta1
-   http://www.diveintojavascript.com/projects/javascript-sprintf
-
-   Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
-   All rights reserved.
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are met:
-       * Redistributions of source code must retain the above copyright
-         notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above copyright
-         notice, this list of conditions and the following disclaimer in the
-         documentation and/or other materials provided with the distribution.
-       * Neither the name of sprintf() for JavaScript nor the
-         names of its contributors may be used to endorse or promote products
-         derived from this software without specific prior written permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND
-   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED
-   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-   DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
-   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
THIS
-   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-  */
-  var sprintf = (function() {
-    function get_type(variable) {
-      return Object.prototype.toString.call(variable).slice(8, 
-1).toLowerCase();
-    }
-    function str_repeat(input, multiplier) {
-      for (var output = []; multiplier > 0; output[--multiplier] = input) {/* 
do nothing */}
-      return output.join('');
-    }
-
-    var str_format = function() {
-      if (!str_format.cache.hasOwnProperty(arguments[0])) {
-        str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
-      }
-      return str_format.format.call(null, str_format.cache[arguments[0]], 
arguments);
-    };
-
-    str_format.format = function(parse_tree, argv) {
-      var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, 
output = [], i, k, match, pad, pad_character, pad_length;
-      for (i = 0; i < tree_length; i++) {
-        node_type = get_type(parse_tree[i]);
-        if (node_type === 'string') {
-          output.push(parse_tree[i]);
-        }
-        else if (node_type === 'array') {
-          match = parse_tree[i]; // convenience purposes only
-          if (match[2]) { // keyword argument
-            arg = argv[cursor];
-            for (k = 0; k < match[2].length; k++) {
-              if (!arg.hasOwnProperty(match[2][k])) {
-                throw(sprintf('[sprintf] property "%s" does not exist', 
match[2][k]));
-              }
-              arg = arg[match[2][k]];
-            }
-          }
-          else if (match[1]) { // positional argument (explicit)
-            arg = argv[match[1]];
-          }
-          else { // positional argument (implicit)
-            arg = argv[cursor++];
-          }
-
-          if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
-            throw(sprintf('[sprintf] expecting number but found %s', 
get_type(arg)));
-          }
-
-          // Jed EDIT
-          if ( typeof arg == 'undefined' || arg === null ) {
-            arg = '';
-          }
-          // Jed EDIT
-
-          switch (match[8]) {
-            case 'b': arg = arg.toString(2); break;
-            case 'c': arg = String.fromCharCode(arg); break;
-            case 'd': arg = parseInt(arg, 10); break;
-            case 'e': arg = match[7] ? arg.toExponential(match[7]) : 
arg.toExponential(); break;
-            case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : 
parseFloat(arg); break;
-            case 'o': arg = arg.toString(8); break;
-            case 's': arg = ((arg = String(arg)) && match[7] ? 
arg.substring(0, match[7]) : arg); break;
-            case 'u': arg = Math.abs(arg); break;
-            case 'x': arg = arg.toString(16); break;
-            case 'X': arg = arg.toString(16).toUpperCase(); break;
-          }
-          arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : 
arg);
-          pad_character = match[4] ? match[4] == '0' ? '0' : 
match[4].charAt(1) : ' ';
-          pad_length = match[6] - String(arg).length;
-          pad = match[6] ? str_repeat(pad_character, pad_length) : '';
-          output.push(match[5] ? arg + pad : pad + arg);
-        }
-      }
-      return output.join('');
-    };
-
-    str_format.cache = {};
-
-    str_format.parse = function(fmt) {
-      var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
-      while (_fmt) {
-        if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
-          parse_tree.push(match[0]);
-        }
-        else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
-          parse_tree.push('%');
-        }
-        else if ((match = 
/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt))
 !== null) {
-          if (match[2]) {
-            arg_names |= 1;
-            var field_list = [], replacement_field = match[2], field_match = 
[];
-            if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) 
!== null) {
-              field_list.push(field_match[1]);
-              while ((replacement_field = 
replacement_field.substring(field_match[0].length)) !== '') {
-                if ((field_match = 
/^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
-                  field_list.push(field_match[1]);
-                }
-                else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) 
!== null) {
-                  field_list.push(field_match[1]);
-                }
-                else {
-                  throw('[sprintf] huh?');
-                }
-              }
-            }
-            else {
-              throw('[sprintf] huh?');
-            }
-            match[2] = field_list;
-          }
-          else {
-            arg_names |= 2;
-          }
-          if (arg_names === 3) {
-            throw('[sprintf] mixing positional and named placeholders is not 
(yet) supported');
-          }
-          parse_tree.push(match);
-        }
-        else {
-          throw('[sprintf] huh?');
-        }
-        _fmt = _fmt.substring(match[0].length);
-      }
-      return parse_tree;
-    };
-
-    return str_format;
-  })();
-
-  var vsprintf = function(fmt, argv) {
-    argv.unshift(fmt);
-    return sprintf.apply(null, argv);
-  };
-
-  Jed.parse_plural = function ( plural_forms, n ) {
-    plural_forms = plural_forms.replace(/n/g, n);
-    return Jed.parse_expression(plural_forms);
-  };
-
-  Jed.sprintf = function ( fmt, args ) {
-    if ( {}.toString.call( args ) == '[object Array]' ) {
-      return vsprintf( fmt, [].slice.call(args) );
-    }
-    return sprintf.apply(this, [].slice.call(arguments) );
-  };
-
-  Jed.prototype.sprintf = function () {
-    return Jed.sprintf.apply(this, arguments);
-  };
-  // END sprintf Implementation
-
-  // Start the Plural forms section
-  // This is a full plural form expression parser. It is used to avoid
-  // running 'eval' or 'new Function' directly against the plural
-  // forms.
-  //
-  // This can be important if you get translations done through a 3rd
-  // party vendor. I encourage you to use this instead, however, I
-  // also will provide a 'precompiler' that you can use at build time
-  // to output valid/safe function representations of the plural form
-  // expressions. This means you can build this code out for the most
-  // part.
-  Jed.PF = {};
-
-  Jed.PF.parse = function ( p ) {
-    var plural_str = Jed.PF.extractPluralExpr( p );
-    return Jed.PF.parser.parse.call(Jed.PF.parser, plural_str);
-  };
-
-  Jed.PF.compile = function ( p ) {
-    // Handle trues and falses as 0 and 1
-    function imply( val ) {
-      return (val === true ? 1 : val ? val : 0);
-    }
-
-    var ast = Jed.PF.parse( p );
-    return function ( n ) {
-      return imply( Jed.PF.interpreter( ast )( n ) );
-    };
-  };
-
-  Jed.PF.interpreter = function ( ast ) {
-    return function ( n ) {
-      switch ( ast.type ) {
-        case 'GROUP':
-          return Jed.PF.interpreter( ast.expr )( n );
-        case 'TERNARY':
-          if ( Jed.PF.interpreter( ast.expr )( n ) ) {
-            return Jed.PF.interpreter( ast.truthy )( n );
-          }
-          return Jed.PF.interpreter( ast.falsey )( n );
-        case 'OR':
-          return Jed.PF.interpreter( ast.left )( n ) || Jed.PF.interpreter( 
ast.right )( n );
-        case 'AND':
-          return Jed.PF.interpreter( ast.left )( n ) && Jed.PF.interpreter( 
ast.right )( n );
-        case 'LT':
-          return Jed.PF.interpreter( ast.left )( n ) < Jed.PF.interpreter( 
ast.right )( n );
-        case 'GT':
-          return Jed.PF.interpreter( ast.left )( n ) > Jed.PF.interpreter( 
ast.right )( n );
-        case 'LTE':
-          return Jed.PF.interpreter( ast.left )( n ) <= Jed.PF.interpreter( 
ast.right )( n );
-        case 'GTE':
-          return Jed.PF.interpreter( ast.left )( n ) >= Jed.PF.interpreter( 
ast.right )( n );
-        case 'EQ':
-          return Jed.PF.interpreter( ast.left )( n ) == Jed.PF.interpreter( 
ast.right )( n );
-        case 'NEQ':
-          return Jed.PF.interpreter( ast.left )( n ) != Jed.PF.interpreter( 
ast.right )( n );
-        case 'MOD':
-          return Jed.PF.interpreter( ast.left )( n ) % Jed.PF.interpreter( 
ast.right )( n );
-        case 'VAR':
-          return n;
-        case 'NUM':
-          return ast.val;
-        default:
-          throw new Error("Invalid Token found.");
-      }
-    };
-  };
-
-  Jed.PF.extractPluralExpr = function ( p ) {
-    // trim first
-    p = p.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
-
-    if (! /;\s*$/.test(p)) {
-      p = p.concat(';');
-    }
-
-    var nplurals_re = /nplurals\=(\d+);/,
-        plural_re = /plural\=(.*);/,
-        nplurals_matches = p.match( nplurals_re ),
-        plural_matches;
-
-    // Find the nplurals number
-    if ( nplurals_matches.length > 1 ) {
-      nplurals_matches[1];
-    }
-    else {
-      throw new Error('nplurals not found in plural_forms string: ' + p );
-    }
-
-    // remove that data to get to the formula
-    p = p.replace( nplurals_re, "" );
-    plural_matches = p.match( plural_re );
-
-    if (!( plural_matches && plural_matches.length > 1 ) ) {
-      throw new Error('`plural` expression not found: ' + p);
-    }
-    return plural_matches[ 1 ];
-  };
-
-  /* Jison generated parser */
-  Jed.PF.parser = (function(){
-
-var parser = {trace: function trace() { },
-yy: {},
-symbols_: 
{"error":2,"expressions":3,"e":4,"EOF":5,"?":6,":":7,"||":8,"&&":9,"<":10,"<=":11,">":12,">=":13,"!=":14,"==":15,"%":16,"(":17,")":18,"n":19,"NUMBER":20,"$accept":0,"$end":1},
-terminals_: 
{2:"error",5:"EOF",6:"?",7:":",8:"||",9:"&&",10:"<",11:"<=",12:">",13:">=",14:"!=",15:"==",16:"%",17:"(",18:")",19:"n",20:"NUMBER"},
-productions_: 
[0,[3,2],[4,5],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,1],[4,1]],
-performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
-
-var $0 = $$.length - 1;
-switch (yystate) {
-case 1: return { type : 'GROUP', expr: $$[$0-1] };
-case 2:this.$ = { type: 'TERNARY', expr: $$[$0-4], truthy : $$[$0-2], falsey: 
$$[$0] };
-break;
-case 3:this.$ = { type: "OR", left: $$[$0-2], right: $$[$0] };
-break;
-case 4:this.$ = { type: "AND", left: $$[$0-2], right: $$[$0] };
-break;
-case 5:this.$ = { type: 'LT', left: $$[$0-2], right: $$[$0] };
-break;
-case 6:this.$ = { type: 'LTE', left: $$[$0-2], right: $$[$0] };
-break;
-case 7:this.$ = { type: 'GT', left: $$[$0-2], right: $$[$0] };
-break;
-case 8:this.$ = { type: 'GTE', left: $$[$0-2], right: $$[$0] };
-break;
-case 9:this.$ = { type: 'NEQ', left: $$[$0-2], right: $$[$0] };
-break;
-case 10:this.$ = { type: 'EQ', left: $$[$0-2], right: $$[$0] };
-break;
-case 11:this.$ = { type: 'MOD', left: $$[$0-2], right: $$[$0] };
-break;
-case 12:this.$ = { type: 'GROUP', expr: $$[$0-1] };
-break;
-case 13:this.$ = { type: 'VAR' };
-break;
-case 14:this.$ = { type: 'NUM', val: Number(yytext) };
-break;
-}
-},
-table: 
[{3:1,4:2,17:[1,3],19:[1,4],20:[1,5]},{1:[3]},{5:[1,6],6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{4:17,17:[1,3],19:[1,4],20:[1,5]},{5:[2,13],6:[2,13],7:[2,13],8:[2,13],9:[2,13],10:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13],15:[2,13],16:[2,13],18:[2,13]},{5:[2,14],6:[2,14],7:[2,14],8:[2,14],9:[2,14],10:[2,14],11:[2,14],12:[2,14],13:[2,14],14:[2,14],15:[2,14],16:[2,14],18:[2,14]},{1:[2,1]},{4:18,17:[1,3],19:[1,4],20:[1,5]},{4:
 [...]
-defaultActions: {6:[2,1]},
-parseError: function parseError(str, hash) {
-    throw new Error(str);
-},
-parse: function parse(input) {
-    var self = this,
-        stack = [0],
-        vstack = [null], // semantic value stack
-        lstack = [], // location stack
-        table = this.table,
-        yytext = '',
-        yylineno = 0,
-        yyleng = 0,
-        recovering = 0,
-        TERROR = 2,
-        EOF = 1;
-
-    //this.reductionCount = this.shiftCount = 0;
-
-    this.lexer.setInput(input);
-    this.lexer.yy = this.yy;
-    this.yy.lexer = this.lexer;
-    if (typeof this.lexer.yylloc == 'undefined')
-        this.lexer.yylloc = {};
-    var yyloc = this.lexer.yylloc;
-    lstack.push(yyloc);
-
-    if (typeof this.yy.parseError === 'function')
-        this.parseError = this.yy.parseError;
-
-    function popStack (n) {
-        stack.length = stack.length - 2*n;
-        vstack.length = vstack.length - n;
-        lstack.length = lstack.length - n;
-    }
-
-    function lex() {
-        var token;
-        token = self.lexer.lex() || 1; // $end = 1
-        // if token isn't its numeric value, convert
-        if (typeof token !== 'number') {
-            token = self.symbols_[token] || token;
-        }
-        return token;
-    }
-
-    var symbol, preErrorSymbol, state, action, r, yyval={},p,len,newState, 
expected;
-    while (true) {
-        // retreive state number from top of stack
-        state = stack[stack.length-1];
-
-        // use default actions if available
-        if (this.defaultActions[state]) {
-            action = this.defaultActions[state];
-        } else {
-            if (symbol == null)
-                symbol = lex();
-            // read action for current state and first input
-            action = table[state] && table[state][symbol];
-        }
-
-        // handle parse error
-        if (typeof action === 'undefined' || !action.length || !action[0]) {
-
-            if (!recovering) {
-                // Report error
-                expected = [];
-                for (p in table[state]) if (this.terminals_[p] && p > 2) {
-                    expected.push("'"+this.terminals_[p]+"'");
-                }
-                var errStr = '';
-                if (this.lexer.showPosition) {
-                    errStr = 'Parse error on line 
'+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', 
') + ", got '" + this.terminals_[symbol]+ "'";
-                } else {
-                    errStr = 'Parse error on line '+(yylineno+1)+": Unexpected 
" +
-                                  (symbol == 1 /*EOF*/ ? "end of input" :
-                                              ("'"+(this.terminals_[symbol] || 
symbol)+"'"));
-                }
-                this.parseError(errStr,
-                    {text: this.lexer.match, token: this.terminals_[symbol] || 
symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
-            }
-
-            // just recovered from another error
-            if (recovering == 3) {
-                if (symbol == EOF) {
-                    throw new Error(errStr || 'Parsing halted.');
-                }
-
-                // discard current lookahead and grab another
-                yyleng = this.lexer.yyleng;
-                yytext = this.lexer.yytext;
-                yylineno = this.lexer.yylineno;
-                yyloc = this.lexer.yylloc;
-                symbol = lex();
-            }
-
-            // try to recover from error
-            while (1) {
-                // check for error recovery rule in this state
-                if ((TERROR.toString()) in table[state]) {
-                    break;
-                }
-                if (state == 0) {
-                    throw new Error(errStr || 'Parsing halted.');
-                }
-                popStack(1);
-                state = stack[stack.length-1];
-            }
-
-            preErrorSymbol = symbol; // save the lookahead token
-            symbol = TERROR;         // insert generic error symbol as new 
lookahead
-            state = stack[stack.length-1];
-            action = table[state] && table[state][TERROR];
-            recovering = 3; // allow 3 real symbols to be shifted before 
reporting a new error
-        }
-
-        // this shouldn't happen, unless resolve defaults are off
-        if (action[0] instanceof Array && action.length > 1) {
-            throw new Error('Parse Error: multiple actions possible at state: 
'+state+', token: '+symbol);
-        }
-
-        switch (action[0]) {
-
-            case 1: // shift
-                //this.shiftCount++;
-
-                stack.push(symbol);
-                vstack.push(this.lexer.yytext);
-                lstack.push(this.lexer.yylloc);
-                stack.push(action[1]); // push state
-                symbol = null;
-                if (!preErrorSymbol) { // normal execution/no error
-                    yyleng = this.lexer.yyleng;
-                    yytext = this.lexer.yytext;
-                    yylineno = this.lexer.yylineno;
-                    yyloc = this.lexer.yylloc;
-                    if (recovering > 0)
-                        recovering--;
-                } else { // error just occurred, resume old lookahead f/ 
before error
-                    symbol = preErrorSymbol;
-                    preErrorSymbol = null;
-                }
-                break;
-
-            case 2: // reduce
-                //this.reductionCount++;
-
-                len = this.productions_[action[1]][1];
-
-                // perform semantic action
-                yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
-                // default location, uses first token for firsts, last for 
lasts
-                yyval._$ = {
-                    first_line: lstack[lstack.length-(len||1)].first_line,
-                    last_line: lstack[lstack.length-1].last_line,
-                    first_column: lstack[lstack.length-(len||1)].first_column,
-                    last_column: lstack[lstack.length-1].last_column
-                };
-                r = this.performAction.call(yyval, yytext, yyleng, yylineno, 
this.yy, action[1], vstack, lstack);
-
-                if (typeof r !== 'undefined') {
-                    return r;
-                }
-
-                // pop off stack
-                if (len) {
-                    stack = stack.slice(0,-1*len*2);
-                    vstack = vstack.slice(0, -1*len);
-                    lstack = lstack.slice(0, -1*len);
-                }
-
-                stack.push(this.productions_[action[1]][0]);    // push 
nonterminal (reduce)
-                vstack.push(yyval.$);
-                lstack.push(yyval._$);
-                // goto new state = table[STATE][NONTERMINAL]
-                newState = table[stack[stack.length-2]][stack[stack.length-1]];
-                stack.push(newState);
-                break;
-
-            case 3: // accept
-                return true;
-        }
-
-    }
-
-    return true;
-}};/* Jison generated lexer */
-var lexer = (function(){
-
-var lexer = ({EOF:1,
-parseError:function parseError(str, hash) {
-        if (this.yy.parseError) {
-            this.yy.parseError(str, hash);
-        } else {
-            throw new Error(str);
-        }
-    },
-setInput:function (input) {
-        this._input = input;
-        this._more = this._less = this.done = false;
-        this.yylineno = this.yyleng = 0;
-        this.yytext = this.matched = this.match = '';
-        this.conditionStack = ['INITIAL'];
-        this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
-        return this;
-    },
-input:function () {
-        var ch = this._input[0];
-        this.yytext+=ch;
-        this.yyleng++;
-        this.match+=ch;
-        this.matched+=ch;
-        var lines = ch.match(/\n/);
-        if (lines) this.yylineno++;
-        this._input = this._input.slice(1);
-        return ch;
-    },
-unput:function (ch) {
-        this._input = ch + this._input;
-        return this;
-    },
-more:function () {
-        this._more = true;
-        return this;
-    },
-pastInput:function () {
-        var past = this.matched.substr(0, this.matched.length - 
this.match.length);
-        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, 
"");
-    },
-upcomingInput:function () {
-        var next = this.match;
-        if (next.length < 20) {
-            next += this._input.substr(0, 20-next.length);
-        }
-        return (next.substr(0,20)+(next.length > 20 ? 
'...':'')).replace(/\n/g, "");
-    },
-showPosition:function () {
-        var pre = this.pastInput();
-        var c = new Array(pre.length + 1).join("-");
-        return pre + this.upcomingInput() + "\n" + c+"^";
-    },
-next:function () {
-        if (this.done) {
-            return this.EOF;
-        }
-        if (!this._input) this.done = true;
-
-        var token,
-            match,
-            lines;
-        if (!this._more) {
-            this.yytext = '';
-            this.match = '';
-        }
-        var rules = this._currentRules();
-        for (var i=0;i < rules.length; i++) {
-            match = this._input.match(this.rules[rules[i]]);
-            if (match) {
-                lines = match[0].match(/\n.*/g);
-                if (lines) this.yylineno += lines.length;
-                this.yylloc = {first_line: this.yylloc.last_line,
-                               last_line: this.yylineno+1,
-                               first_column: this.yylloc.last_column,
-                               last_column: lines ? 
lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length};
-                this.yytext += match[0];
-                this.match += match[0];
-                this.matches = match;
-                this.yyleng = this.yytext.length;
-                this._more = false;
-                this._input = this._input.slice(match[0].length);
-                this.matched += match[0];
-                token = this.performAction.call(this, this.yy, this, 
rules[i],this.conditionStack[this.conditionStack.length-1]);
-                if (token) return token;
-                else return;
-            }
-        }
-        if (this._input === "") {
-            return this.EOF;
-        } else {
-            this.parseError('Lexical error on line '+(this.yylineno+1)+'. 
Unrecognized text.\n'+this.showPosition(),
-                    {text: "", token: null, line: this.yylineno});
-        }
-    },
-lex:function lex() {
-        var r = this.next();
-        if (typeof r !== 'undefined') {
-            return r;
-        } else {
-            return this.lex();
-        }
-    },
-begin:function begin(condition) {
-        this.conditionStack.push(condition);
-    },
-popState:function popState() {
-        return this.conditionStack.pop();
-    },
-_currentRules:function _currentRules() {
-        return 
this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
-    },
-topState:function () {
-        return this.conditionStack[this.conditionStack.length-2];
-    },
-pushState:function begin(condition) {
-        this.begin(condition);
-    }});
-lexer.performAction = function 
anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
-switch($avoiding_name_collisions) {
-case 0:/* skip whitespace */
-break;
-case 1:return 20
-case 2:return 19
-case 3:return 8
-case 4:return 9
-case 5:return 6
-case 6:return 7
-case 7:return 11
-case 8:return 13
-case 9:return 10
-case 10:return 12
-case 11:return 14
-case 12:return 15
-case 13:return 16
-case 14:return 17
-case 15:return 18
-case 16:return 5
-case 17:return 'INVALID'
-}
-};
-lexer.rules = 
[/^\s+/,/^[0-9]+(\.[0-9]+)?\b/,/^n\b/,/^\|\|/,/^&&/,/^\?/,/^:/,/^<=/,/^>=/,/^</,/^>/,/^!=/,/^==/,/^%/,/^\(/,/^\)/,/^$/,/^./];
-lexer.conditions = 
{"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],"inclusive":true}};return
 lexer;})();
-parser.lexer = lexer;
-return parser;
-})();
-// End parser
-
-  // Handle node, amd, and global systems
-  {
-    if (module.exports) {
-      exports = module.exports = Jed;
-    }
-    exports.Jed = Jed;
-  }
-
-})();
-});
-
-/*
- This file is part of TALER
- (C) 2019 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Check if we are running under nodejs.
- */
-const isNode = typeof process !== "undefined" &&
-    typeof process.release !== "undefined" &&
-    process.release.name === "node";
-function writeNodeLog(message, tag, level, args) {
-    try {
-        process.stderr.write(`${new Date().toISOString()} ${tag} ${level} `);
-        process.stderr.write(`${message}`);
-        if (args.length != 0) {
-            process.stderr.write(" ");
-            process.stderr.write(JSON.stringify(args, undefined, 2));
-        }
-        process.stderr.write("\n");
-    }
-    catch (e) {
-        // This can happen when we're trying to log something that doesn't 
want to be
-        // converted to a string.
-        process.stderr.write(`${new Date().toISOString()} (logger) FATAL `);
-        if (e instanceof Error) {
-            process.stderr.write("failed to write log: ");
-            process.stderr.write(e.message);
-        }
-        process.stderr.write("\n");
-    }
-}
-/**
- * Logger that writes to stderr when running under node,
- * and uses the corresponding console.* method to log in the browser.
- */
-class Logger {
-    constructor(tag) {
-        this.tag = tag;
-    }
-    info(message, ...args) {
-        if (isNode) {
-            writeNodeLog(message, this.tag, "INFO", args);
-        }
-        else {
-            console.info(`${new Date().toISOString()} ${this.tag} INFO ` + 
message, ...args);
-        }
-    }
-    warn(message, ...args) {
-        if (isNode) {
-            writeNodeLog(message, this.tag, "WARN", args);
-        }
-        else {
-            console.warn(`${new Date().toISOString()} ${this.tag} INFO ` + 
message, ...args);
-        }
-    }
-    error(message, ...args) {
-        if (isNode) {
-            writeNodeLog(message, this.tag, "ERROR", args);
-        }
-        else {
-            console.info(`${new Date().toISOString()} ${this.tag} ERROR ` + 
message, ...args);
-        }
-    }
-    trace(message, ...args) {
-        if (isNode) {
-            writeNodeLog(message, this.tag, "TRACE", args);
-        }
-        else {
-            console.info(`${new Date().toISOString()} ${this.tag} TRACE ` + 
message, ...args);
-        }
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019-2020 Taler Systems SA
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * This exception is there to let the caller know that an error happened,
- * but the error has already been reported by writing it to the database.
- */
-class OperationFailedAndReportedError extends Error {
-    constructor(operationError) {
-        super(operationError.message);
-        this.operationError = operationError;
-        // Set the prototype explicitly.
-        Object.setPrototypeOf(this, OperationFailedAndReportedError.prototype);
-    }
-    static fromCode(ec, message, details) {
-        return new OperationFailedAndReportedError(makeErrorDetails(ec, 
message, details));
-    }
-}
-/**
- * This exception is thrown when an error occurred and the caller is
- * responsible for recording the failure in the database.
- */
-class OperationFailedError extends Error {
-    constructor(operationError) {
-        super(operationError.message);
-        this.operationError = operationError;
-        // Set the prototype explicitly.
-        Object.setPrototypeOf(this, OperationFailedError.prototype);
-    }
-    static fromCode(ec, message, details) {
-        return new OperationFailedError(makeErrorDetails(ec, message, 
details));
-    }
-}
-function makeErrorDetails(ec, message, details) {
-    return {
-        code: ec,
-        hint: `Error: ${TalerErrorCode[ec]}`,
-        details: details,
-        message,
-    };
-}
-/**
- * Run an operation and call the onOpError callback
- * when there was an exception or operation error that must be reported.
- * The cause will be re-thrown to the caller.
- */
-async function guardOperationException(op, onOpError) {
-    try {
-        return await op();
-    }
-    catch (e) {
-        if (e instanceof OperationFailedAndReportedError) {
-            throw e;
-        }
-        if (e instanceof OperationFailedError) {
-            await onOpError(e.operationError);
-            throw new OperationFailedAndReportedError(e.operationError);
-        }
-        if (e instanceof Error) {
-            const opErr = 
makeErrorDetails(TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, `unexpected 
exception (message: ${e.message})`, {
-                stack: e.stack,
-            });
-            await onOpError(opErr);
-            throw new OperationFailedAndReportedError(opErr);
-        }
-        // Something was thrown that is not even an exception!
-        // Try to stringify it.
-        let excString;
-        try {
-            excString = e.toString();
-        }
-        catch (e) {
-            // Something went horribly wrong.
-            excString = "can't stringify exception";
-        }
-        const opErr = 
makeErrorDetails(TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, `unexpected 
exception (not an exception, ${excString})`, {});
-        await onOpError(opErr);
-        throw new OperationFailedAndReportedError(opErr);
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Get an unresolved promise together with its extracted resolve / reject
- * function.
- */
-function openPromise$1() {
-    let resolve = null;
-    let reject = null;
-    const promise = new Promise((res, rej) => {
-        resolve = res;
-        reject = rej;
-    });
-    if (!(resolve && reject)) {
-        // Never happens, unless JS implementation is broken
-        throw Error();
-    }
-    return { resolve, reject, promise };
-}
-class AsyncCondition$1 {
-    constructor() {
-        const op = openPromise$1();
-        this._waitPromise = op.promise;
-        this._resolveWaitPromise = op.resolve;
-    }
-    wait() {
-        return this._waitPromise;
-    }
-    trigger() {
-        this._resolveWaitPromise();
-        const op = openPromise$1();
-        this._waitPromise = op.promise;
-        this._resolveWaitPromise = op.resolve;
-    }
-}
-
-/*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$l = new Logger("query.ts");
-/**
- * Exception that should be thrown by client code to abort a transaction.
- */
-const TransactionAbort = Symbol("transaction_abort");
-function requestToPromise(req) {
-    const stack = Error("Failed request was started here.");
-    return new Promise((resolve, reject) => {
-        req.onsuccess = () => {
-            resolve(req.result);
-        };
-        req.onerror = () => {
-            console.error("error in DB request", req.error);
-            reject(req.error);
-            console.error("Request failed:", stack);
-        };
-    });
-}
-class ResultStream {
-    constructor(req) {
-        this.req = req;
-        this.gotCursorEnd = false;
-        this.awaitingResult = false;
-        this.awaitingResult = true;
-        let p = openPromise$1();
-        this.currentPromise = p.promise;
-        req.onsuccess = () => {
-            if (!this.awaitingResult) {
-                throw Error("BUG: invariant violated");
-            }
-            const cursor = req.result;
-            if (cursor) {
-                this.awaitingResult = false;
-                p.resolve();
-                p = openPromise$1();
-                this.currentPromise = p.promise;
-            }
-            else {
-                this.gotCursorEnd = true;
-                p.resolve();
-            }
-        };
-        req.onerror = () => {
-            p.reject(req.error);
-        };
-    }
-    async toArray() {
-        const arr = [];
-        while (true) {
-            const x = await this.next();
-            if (x.hasValue) {
-                arr.push(x.value);
-            }
-            else {
-                break;
-            }
-        }
-        return arr;
-    }
-    async map(f) {
-        const arr = [];
-        while (true) {
-            const x = await this.next();
-            if (x.hasValue) {
-                arr.push(f(x.value));
-            }
-            else {
-                break;
-            }
-        }
-        return arr;
-    }
-    async forEachAsync(f) {
-        while (true) {
-            const x = await this.next();
-            if (x.hasValue) {
-                await f(x.value);
-            }
-            else {
-                break;
-            }
-        }
-    }
-    async forEach(f) {
-        while (true) {
-            const x = await this.next();
-            if (x.hasValue) {
-                f(x.value);
-            }
-            else {
-                break;
-            }
-        }
-    }
-    async filter(f) {
-        const arr = [];
-        while (true) {
-            const x = await this.next();
-            if (x.hasValue) {
-                if (f(x.value)) {
-                    arr.push(x.value);
-                }
-            }
-            else {
-                break;
-            }
-        }
-        return arr;
-    }
-    async next() {
-        if (this.gotCursorEnd) {
-            return { hasValue: false };
-        }
-        if (!this.awaitingResult) {
-            const cursor = this.req.result;
-            if (!cursor) {
-                throw Error("assertion failed");
-            }
-            this.awaitingResult = true;
-            cursor.continue();
-        }
-        await this.currentPromise;
-        if (this.gotCursorEnd) {
-            return { hasValue: false };
-        }
-        const cursor = this.req.result;
-        if (!cursor) {
-            throw Error("assertion failed");
-        }
-        return { hasValue: true, value: cursor.value };
-    }
-}
-/**
- * Return a promise that resolves to the opened IndexedDB database.
- */
-function openDatabase(idbFactory, databaseName, databaseVersion, 
onVersionChange, onUpgradeNeeded) {
-    return new Promise((resolve, reject) => {
-        const req = idbFactory.open(databaseName, databaseVersion);
-        req.onerror = (e) => {
-            logger$l.error("database error", e);
-            reject(new Error("database error"));
-        };
-        req.onsuccess = (e) => {
-            req.result.onversionchange = (evt) => {
-                logger$l.info(`handling live db version change from 
${evt.oldVersion} to ${evt.newVersion}`);
-                req.result.close();
-                onVersionChange();
-            };
-            resolve(req.result);
-        };
-        req.onupgradeneeded = (e) => {
-            const db = req.result;
-            const newVersion = e.newVersion;
-            if (!newVersion) {
-                throw Error("upgrade needed, but new version unknown");
-            }
-            const transaction = req.transaction;
-            if (!transaction) {
-                throw Error("no transaction handle available in upgrade 
handler");
-            }
-            onUpgradeNeeded(db, e.oldVersion, newVersion, transaction);
-        };
-    });
-}
-function describeContents(name, options) {
-    return { name, keyPath: options.keyPath, _dummy: undefined };
-}
-function describeIndex(name, keyPath, options = {}) {
-    return {
-        keyPath,
-        name,
-        multiEntry: options.multiEntry,
-    };
-}
-const storeWithIndexesSymbol = Symbol("StoreWithIndexesMark");
-function describeStore(s, m) {
-    return {
-        store: s,
-        indexMap: m,
-        mark: storeWithIndexesSymbol,
-    };
-}
-function runTx(tx, arg, f) {
-    const stack = Error("Failed transaction was started here.");
-    return new Promise((resolve, reject) => {
-        let funResult = undefined;
-        let gotFunResult = false;
-        tx.oncomplete = () => {
-            // This is a fatal error: The transaction completed *before*
-            // the transaction function returned.  Likely, the transaction
-            // function waited on a promise that is *not* resolved in the
-            // microtask queue, thus triggering the auto-commit behavior.
-            // Unfortunately, the auto-commit behavior of IDB can't be switched
-            // of.  There are some proposals to add this functionality in the 
future.
-            if (!gotFunResult) {
-                const msg = "BUG: transaction closed before transaction 
function returned";
-                console.error(msg);
-                reject(Error(msg));
-            }
-            resolve(funResult);
-        };
-        tx.onerror = () => {
-            logger$l.error("error in transaction");
-            logger$l.error(`${stack}`);
-        };
-        tx.onabort = () => {
-            if (tx.error) {
-                logger$l.error("Transaction aborted with error:", tx.error);
-            }
-            else {
-                logger$l.error("Transaction aborted (no error)");
-            }
-            reject(TransactionAbort);
-        };
-        const resP = Promise.resolve().then(() => f(arg));
-        resP
-            .then((result) => {
-            gotFunResult = true;
-            funResult = result;
-        })
-            .catch((e) => {
-            if (e == TransactionAbort) {
-                logger$l.trace("aborting transaction");
-            }
-            else {
-                console.error("Transaction failed:", e);
-                console.error(stack);
-                tx.abort();
-            }
-        })
-            .catch((e) => {
-            console.error("fatal: aborting transaction failed", e);
-        });
-    });
-}
-function makeReadContext(tx, storePick) {
-    const ctx = {};
-    for (const storeAlias in storePick) {
-        const indexes = {};
-        const swi = storePick[storeAlias];
-        const storeName = swi.store.name;
-        for (const indexAlias in storePick[storeAlias].indexMap) {
-            const indexDescriptor = storePick[storeAlias].indexMap[indexAlias];
-            const indexName = indexDescriptor.name;
-            indexes[indexAlias] = {
-                get(key) {
-                    const req = 
tx.objectStore(storeName).index(indexName).get(key);
-                    return requestToPromise(req);
-                },
-                iter(query) {
-                    const req = tx
-                        .objectStore(storeName)
-                        .index(indexName)
-                        .openCursor(query);
-                    return new ResultStream(req);
-                },
-            };
-        }
-        ctx[storeAlias] = {
-            indexes,
-            get(key) {
-                const req = tx.objectStore(storeName).get(key);
-                return requestToPromise(req);
-            },
-            iter(query) {
-                const req = tx.objectStore(storeName).openCursor(query);
-                return new ResultStream(req);
-            },
-        };
-    }
-    return ctx;
-}
-function makeWriteContext(tx, storePick) {
-    const ctx = {};
-    for (const storeAlias in storePick) {
-        const indexes = {};
-        const swi = storePick[storeAlias];
-        const storeName = swi.store.name;
-        for (const indexAlias in storePick[storeAlias].indexMap) {
-            const indexDescriptor = storePick[storeAlias].indexMap[indexAlias];
-            const indexName = indexDescriptor.name;
-            indexes[indexAlias] = {
-                get(key) {
-                    const req = 
tx.objectStore(storeName).index(indexName).get(key);
-                    return requestToPromise(req);
-                },
-                iter(query) {
-                    const req = tx
-                        .objectStore(storeName)
-                        .index(indexName)
-                        .openCursor(query);
-                    return new ResultStream(req);
-                },
-            };
-        }
-        ctx[storeAlias] = {
-            indexes,
-            get(key) {
-                const req = tx.objectStore(storeName).get(key);
-                return requestToPromise(req);
-            },
-            iter(query) {
-                const req = tx.objectStore(storeName).openCursor(query);
-                return new ResultStream(req);
-            },
-            add(r) {
-                const req = tx.objectStore(storeName).add(r);
-                return requestToPromise(req);
-            },
-            put(r) {
-                const req = tx.objectStore(storeName).put(r);
-                return requestToPromise(req);
-            },
-            delete(k) {
-                const req = tx.objectStore(storeName).delete(k);
-                return requestToPromise(req);
-            },
-        };
-    }
-    return ctx;
-}
-/**
- * Type-safe access to a database with a particular store map.
- *
- * A store map is the metadata that describes the store.
- */
-class DbAccess {
-    constructor(db, stores) {
-        this.db = db;
-        this.stores = stores;
-    }
-    mktx(f) {
-        const storePick = f(this.stores);
-        if (typeof storePick !== "object" || storePick === null) {
-            throw Error();
-        }
-        const storeNames = [];
-        for (const storeAlias of Object.keys(storePick)) {
-            const swi = storePick[storeAlias];
-            if (swi.mark !== storeWithIndexesSymbol) {
-                throw Error("invalid store descriptor returned from selector 
function");
-            }
-            storeNames.push(swi.store.name);
-        }
-        const runReadOnly = (txf) => {
-            const tx = this.db.transaction(storeNames, "readonly");
-            const readContext = makeReadContext(tx, storePick);
-            return runTx(tx, readContext, txf);
-        };
-        const runReadWrite = (txf) => {
-            const tx = this.db.transaction(storeNames, "readwrite");
-            const writeContext = makeWriteContext(tx, storePick);
-            return runTx(tx, writeContext, txf);
-        };
-        return {
-            runReadOnly,
-            runReadWrite,
-        };
-    }
-}
-
-/*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var HttpResponseStatus;
-(function (HttpResponseStatus) {
-    HttpResponseStatus[HttpResponseStatus["Ok"] = 200] = "Ok";
-    HttpResponseStatus[HttpResponseStatus["NoContent"] = 204] = "NoContent";
-    HttpResponseStatus[HttpResponseStatus["Gone"] = 210] = "Gone";
-    HttpResponseStatus[HttpResponseStatus["NotModified"] = 304] = 
"NotModified";
-    HttpResponseStatus[HttpResponseStatus["PaymentRequired"] = 402] = 
"PaymentRequired";
-    HttpResponseStatus[HttpResponseStatus["Conflict"] = 409] = "Conflict";
-})(HttpResponseStatus || (HttpResponseStatus = {}));
-/**
- * Headers, roughly modeled after the fetch API's headers object.
- */
-class Headers {
-    constructor() {
-        this.headerMap = new Map();
-    }
-    get(name) {
-        const r = this.headerMap.get(name.toLowerCase());
-        if (r) {
-            return r;
-        }
-        return null;
-    }
-    set(name, value) {
-        const normalizedName = name.toLowerCase();
-        const existing = this.headerMap.get(normalizedName);
-        if (existing !== undefined) {
-            this.headerMap.set(normalizedName, existing + "," + value);
-        }
-        else {
-            this.headerMap.set(normalizedName, value);
-        }
-    }
-    toJSON() {
-        const m = {};
-        this.headerMap.forEach((v, k) => (m[k] = v));
-        return m;
-    }
-}
-async function readTalerErrorResponse(httpResponse) {
-    const errJson = await httpResponse.json();
-    const talerErrorCode = errJson.code;
-    if (typeof talerErrorCode !== "number") {
-        throw new 
OperationFailedError(makeErrorDetails(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
 "Error response did not contain error code", {
-            requestUrl: httpResponse.requestUrl,
-            requestMethod: httpResponse.requestMethod,
-            httpStatusCode: httpResponse.status,
-        }));
-    }
-    return errJson;
-}
-async function readSuccessResponseJsonOrErrorCode(httpResponse, codec) {
-    if (!(httpResponse.status >= 200 && httpResponse.status < 300)) {
-        return {
-            isError: true,
-            talerErrorResponse: await readTalerErrorResponse(httpResponse),
-        };
-    }
-    const respJson = await httpResponse.json();
-    let parsedResponse;
-    try {
-        parsedResponse = codec.decode(respJson);
-    }
-    catch (e) {
-        throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
 "Response invalid", {
-            requestUrl: httpResponse.requestUrl,
-            httpStatusCode: httpResponse.status,
-            validationError: e.toString(),
-        });
-    }
-    return {
-        isError: false,
-        response: parsedResponse,
-    };
-}
-function getHttpResponseErrorDetails(httpResponse) {
-    return {
-        requestUrl: httpResponse.requestUrl,
-        httpStatusCode: httpResponse.status,
-    };
-}
-function throwUnexpectedRequestError(httpResponse, talerErrorResponse) {
-    throw new 
OperationFailedError(makeErrorDetails(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
 "Unexpected error code in response", {
-        requestUrl: httpResponse.requestUrl,
-        httpStatusCode: httpResponse.status,
-        errorResponse: talerErrorResponse,
-    }));
-}
-async function readSuccessResponseJsonOrThrow(httpResponse, codec) {
-    const r = await readSuccessResponseJsonOrErrorCode(httpResponse, codec);
-    if (!r.isError) {
-        return r.response;
-    }
-    throwUnexpectedRequestError(httpResponse, r.talerErrorResponse);
-}
-async function readSuccessResponseTextOrErrorCode(httpResponse) {
-    if (!(httpResponse.status >= 200 && httpResponse.status < 300)) {
-        const errJson = await httpResponse.json();
-        const talerErrorCode = errJson.code;
-        if (typeof talerErrorCode !== "number") {
-            throw new 
OperationFailedError(makeErrorDetails(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
 "Error response did not contain error code", {
-                httpStatusCode: httpResponse.status,
-                requestUrl: httpResponse.requestUrl,
-                requestMethod: httpResponse.requestMethod,
-            }));
-        }
-        return {
-            isError: true,
-            talerErrorResponse: errJson,
-        };
-    }
-    const respJson = await httpResponse.text();
-    return {
-        isError: false,
-        response: respJson,
-    };
-}
-async function checkSuccessResponseOrThrow(httpResponse) {
-    if (!(httpResponse.status >= 200 && httpResponse.status < 300)) {
-        const errJson = await httpResponse.json();
-        const talerErrorCode = errJson.code;
-        if (typeof talerErrorCode !== "number") {
-            throw new 
OperationFailedError(makeErrorDetails(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
 "Error response did not contain error code", {
-                httpStatusCode: httpResponse.status,
-                requestUrl: httpResponse.requestUrl,
-                requestMethod: httpResponse.requestMethod,
-            }));
-        }
-        throwUnexpectedRequestError(httpResponse, errJson);
-    }
-}
-async function readSuccessResponseTextOrThrow(httpResponse) {
-    const r = await readSuccessResponseTextOrErrorCode(httpResponse);
-    if (!r.isError) {
-        return r.response;
-    }
-    throwUnexpectedRequestError(httpResponse, r.talerErrorResponse);
-}
-/**
- * Get the timestamp at which the response's content is considered expired.
- */
-function getExpiryTimestamp(httpResponse, opt) {
-    var _a;
-    const expiryDateMs = new Date((_a = httpResponse.headers.get("expiry")) 
!== null && _a !== void 0 ? _a : "").getTime();
-    let t;
-    if (Number.isNaN(expiryDateMs)) {
-        t = getTimestampNow();
-    }
-    else {
-        t = {
-            t_ms: expiryDateMs,
-        };
-    }
-    if (opt.minDuration) {
-        const t2 = timestampAddDuration(getTimestampNow(), opt.minDuration);
-        return timestampMax(t, t2);
-    }
-    return t;
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Protocol version spoken with the exchange.
- *
- * Uses libtool's current:revision:age versioning.
- */
-const WALLET_EXCHANGE_PROTOCOL_VERSION = "9:0:0";
-/**
- * Protocol version spoken with the merchant.
- *
- * Uses libtool's current:revision:age versioning.
- */
-const WALLET_MERCHANT_PROTOCOL_VERSION = "1:0:0";
-/**
- * Protocol version spoken with the merchant.
- *
- * Uses libtool's current:revision:age versioning.
- */
-const WALLET_BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0";
-/**
- * Cache breaker that is appended to queries such as /keys and /wire
- * to break through caching, if it has been accidentally/badly configured
- * by the exchange.
- *
- * This is only a temporary measure.
- */
-const WALLET_CACHE_BREAKER_CLIENT_VERSION = "3";
-
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Name of the Taler database.  This is effectively the major
- * version of the DB schema. Whenever it changes, custom import logic
- * for all previous versions must be written, which should be
- * avoided.
- */
-const TALER_DB_NAME = "taler-wallet-main-v2";
-const TALER_META_DB_NAME = "taler-wallet-meta";
-const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
-/**
- * Current database minor version, should be incremented
- * each time we do minor schema changes on the database.
- * A change is considered minor when fields are added in a
- * backwards-compatible way or object stores and indices
- * are added.
- */
-const WALLET_DB_MINOR_VERSION = 1;
-const logger$k = new Logger("db.ts");
-function upgradeFromStoreMap(storeMap, db, oldVersion, newVersion, 
upgradeTransaction) {
-    if (oldVersion === 0) {
-        for (const n in storeMap) {
-            const swi = storeMap[n];
-            const storeDesc = swi.store;
-            const s = db.createObjectStore(storeDesc.name, {
-                autoIncrement: storeDesc.autoIncrement,
-                keyPath: storeDesc.keyPath,
-            });
-            for (const indexName in swi.indexMap) {
-                const indexDesc = swi.indexMap[indexName];
-                s.createIndex(indexDesc.name, indexDesc.keyPath, {
-                    multiEntry: indexDesc.multiEntry,
-                });
-            }
-        }
-        return;
-    }
-    if (oldVersion === newVersion) {
-        return;
-    }
-    logger$k.info(`upgrading database from ${oldVersion} to ${newVersion}`);
-    throw Error("upgrade not supported");
-}
-function onTalerDbUpgradeNeeded(db, oldVersion, newVersion, 
upgradeTransaction) {
-    upgradeFromStoreMap(WalletStoresV1, db, oldVersion, newVersion);
-}
-function onMetaDbUpgradeNeeded(db, oldVersion, newVersion, upgradeTransaction) 
{
-    upgradeFromStoreMap(walletMetadataStore, db, oldVersion, newVersion);
-}
-/**
- * Return a promise that resolves
- * to the taler wallet db.
- */
-async function openTalerDatabase(idbFactory, onVersionChange) {
-    const metaDbHandle = await openDatabase(idbFactory, TALER_META_DB_NAME, 1, 
() => { }, onMetaDbUpgradeNeeded);
-    const metaDb = new DbAccess(metaDbHandle, walletMetadataStore);
-    let currentMainVersion;
-    await metaDb
-        .mktx((x) => ({
-        metaConfig: x.metaConfig,
-    }))
-        .runReadWrite(async (tx) => {
-        const dbVersionRecord = await tx.metaConfig.get(CURRENT_DB_CONFIG_KEY);
-        if (!dbVersionRecord) {
-            currentMainVersion = TALER_DB_NAME;
-            await tx.metaConfig.put({
-                key: CURRENT_DB_CONFIG_KEY,
-                value: TALER_DB_NAME,
-            });
-        }
-        else {
-            currentMainVersion = dbVersionRecord.value;
-        }
-    });
-    if (currentMainVersion !== TALER_DB_NAME) {
-        // In the future, the migration logic will be implemented here.
-        throw Error(`migration from database ${currentMainVersion} not 
supported`);
-    }
-    const mainDbHandle = await openDatabase(idbFactory, TALER_DB_NAME, 
WALLET_DB_MINOR_VERSION, onVersionChange, onTalerDbUpgradeNeeded);
-    return new DbAccess(mainDbHandle, WalletStoresV1);
-}
-var ReserveRecordStatus;
-(function (ReserveRecordStatus) {
-    /**
-     * Reserve must be registered with the bank.
-     */
-    ReserveRecordStatus["REGISTERING_BANK"] = "registering-bank";
-    /**
-     * We've registered reserve's information with the bank
-     * and are now waiting for the user to confirm the withdraw
-     * with the bank (typically 2nd factor auth).
-     */
-    ReserveRecordStatus["WAIT_CONFIRM_BANK"] = "wait-confirm-bank";
-    /**
-     * Querying reserve status with the exchange.
-     */
-    ReserveRecordStatus["QUERYING_STATUS"] = "querying-status";
-    /**
-     * The corresponding withdraw record has been created.
-     * No further processing is done, unless explicitly requested
-     * by the user.
-     */
-    ReserveRecordStatus["DORMANT"] = "dormant";
-    /**
-     * The bank aborted the withdrawal.
-     */
-    ReserveRecordStatus["BANK_ABORTED"] = "bank-aborted";
-})(ReserveRecordStatus || (ReserveRecordStatus = {}));
-/**
- * Status of a denomination.
- */
-var DenominationStatus;
-(function (DenominationStatus) {
-    /**
-     * Verification was delayed.
-     */
-    DenominationStatus["Unverified"] = "unverified";
-    /**
-     * Verified as valid.
-     */
-    DenominationStatus["VerifiedGood"] = "verified-good";
-    /**
-     * Verified as invalid.
-     */
-    DenominationStatus["VerifiedBad"] = "verified-bad";
-})(DenominationStatus || (DenominationStatus = {}));
-/**
- * Status of a coin.
- */
-var CoinStatus;
-(function (CoinStatus) {
-    /**
-     * Withdrawn and never shown to anybody.
-     */
-    CoinStatus["Fresh"] = "fresh";
-    /**
-     * A coin that has been spent and refreshed.
-     */
-    CoinStatus["Dormant"] = "dormant";
-})(CoinStatus || (CoinStatus = {}));
-var CoinSourceType;
-(function (CoinSourceType) {
-    CoinSourceType["Withdraw"] = "withdraw";
-    CoinSourceType["Refresh"] = "refresh";
-    CoinSourceType["Tip"] = "tip";
-})(CoinSourceType || (CoinSourceType = {}));
-var ProposalStatus;
-(function (ProposalStatus) {
-    /**
-     * Not downloaded yet.
-     */
-    ProposalStatus["DOWNLOADING"] = "downloading";
-    /**
-     * Proposal downloaded, but the user needs to accept/reject it.
-     */
-    ProposalStatus["PROPOSED"] = "proposed";
-    /**
-     * The user has accepted the proposal.
-     */
-    ProposalStatus["ACCEPTED"] = "accepted";
-    /**
-     * The user has rejected the proposal.
-     */
-    ProposalStatus["REFUSED"] = "refused";
-    /**
-     * Downloading or processing the proposal has failed permanently.
-     */
-    ProposalStatus["PERMANENTLY_FAILED"] = "permanently-failed";
-    /**
-     * Downloaded proposal was detected as a re-purchase.
-     */
-    ProposalStatus["REPURCHASE"] = "repurchase";
-})(ProposalStatus || (ProposalStatus = {}));
-var RefundState;
-(function (RefundState) {
-    RefundState["Failed"] = "failed";
-    RefundState["Applied"] = "applied";
-    RefundState["Pending"] = "pending";
-})(RefundState || (RefundState = {}));
-var RefundReason;
-(function (RefundReason) {
-    /**
-     * Normal refund given by the merchant.
-     */
-    RefundReason["NormalRefund"] = "normal-refund";
-    /**
-     * Refund from an aborted payment.
-     */
-    RefundReason["AbortRefund"] = "abort-pay-refund";
-})(RefundReason || (RefundReason = {}));
-var AbortStatus;
-(function (AbortStatus) {
-    AbortStatus["None"] = "none";
-    AbortStatus["AbortRefund"] = "abort-refund";
-    AbortStatus["AbortFinished"] = "abort-finished";
-})(AbortStatus || (AbortStatus = {}));
-const WALLET_BACKUP_STATE_KEY = "walletBackupState";
-var BackupProviderStatus;
-(function (BackupProviderStatus) {
-    BackupProviderStatus["PaymentRequired"] = "payment-required";
-    BackupProviderStatus["Ready"] = "ready";
-})(BackupProviderStatus || (BackupProviderStatus = {}));
-const WalletStoresV1 = {
-    coins: describeStore(describeContents("coins", {
-        keyPath: "coinPub",
-    }), {
-        byBaseUrl: describeIndex("byBaseUrl", "exchangeBaseUrl"),
-        byDenomPubHash: describeIndex("byDenomPubHash", "denomPubHash"),
-        byCoinEvHash: describeIndex("byCoinEvHash", "coinEvHash"),
-    }),
-    config: describeStore(describeContents("config", { keyPath: "key" }), {}),
-    auditorTrust: describeStore(describeContents("auditorTrust", {
-        keyPath: ["currency", "auditorBaseUrl"],
-    }), {
-        byAuditorPub: describeIndex("byAuditorPub", "auditorPub"),
-        byUid: describeIndex("byUid", "uids", {
-            multiEntry: true,
-        }),
-    }),
-    exchangeTrust: describeStore(describeContents("exchangeTrust", {
-        keyPath: ["currency", "exchangeBaseUrl"],
-    }), {
-        byExchangeMasterPub: describeIndex("byExchangeMasterPub", 
"exchangeMasterPub"),
-    }),
-    denominations: describeStore(describeContents("denominations", {
-        keyPath: ["exchangeBaseUrl", "denomPubHash"],
-    }), {
-        byExchangeBaseUrl: describeIndex("byExchangeBaseUrl", 
"exchangeBaseUrl"),
-    }),
-    exchanges: describeStore(describeContents("exchanges", {
-        keyPath: "baseUrl",
-    }), {}),
-    exchangeDetails: describeStore(describeContents("exchangeDetails", {
-        keyPath: ["exchangeBaseUrl", "currency", "masterPublicKey"],
-    }), {}),
-    proposals: describeStore(describeContents("proposals", { keyPath: 
"proposalId" }), {
-        byUrlAndOrderId: describeIndex("byUrlAndOrderId", [
-            "merchantBaseUrl",
-            "orderId",
-        ]),
-    }),
-    refreshGroups: describeStore(describeContents("refreshGroups", {
-        keyPath: "refreshGroupId",
-    }), {}),
-    recoupGroups: describeStore(describeContents("recoupGroups", {
-        keyPath: "recoupGroupId",
-    }), {}),
-    reserves: describeStore(describeContents("reserves", { keyPath: 
"reservePub" }), {
-        byInitialWithdrawalGroupId: 
describeIndex("byInitialWithdrawalGroupId", "initialWithdrawalGroupId"),
-    }),
-    purchases: describeStore(describeContents("purchases", { keyPath: 
"proposalId" }), {
-        byFulfillmentUrl: describeIndex("byFulfillmentUrl", 
"download.contractData.fulfillmentUrl"),
-        byMerchantUrlAndOrderId: describeIndex("byMerchantUrlAndOrderId", [
-            "download.contractData.merchantBaseUrl",
-            "download.contractData.orderId",
-        ]),
-    }),
-    tips: describeStore(describeContents("tips", { keyPath: "walletTipId" }), {
-        byMerchantTipIdAndBaseUrl: describeIndex("byMerchantTipIdAndBaseUrl", [
-            "merchantTipId",
-            "merchantBaseUrl",
-        ]),
-    }),
-    withdrawalGroups: describeStore(describeContents("withdrawalGroups", {
-        keyPath: "withdrawalGroupId",
-    }), {
-        byReservePub: describeIndex("byReservePub", "reservePub"),
-    }),
-    planchets: describeStore(describeContents("planchets", { keyPath: 
"coinPub" }), {
-        byGroupAndIndex: describeIndex("byGroupAndIndex", [
-            "withdrawalGroupId",
-            "coinIdx",
-        ]),
-        byGroup: describeIndex("byGroup", "withdrawalGroupId"),
-        byCoinEvHash: describeIndex("byCoinEv", "coinEvHash"),
-    }),
-    bankWithdrawUris: describeStore(describeContents("bankWithdrawUris", {
-        keyPath: "talerWithdrawUri",
-    }), {}),
-    backupProviders: describeStore(describeContents("backupProviders", {
-        keyPath: "baseUrl",
-    }), {}),
-    depositGroups: describeStore(describeContents("depositGroups", {
-        keyPath: "depositGroupId",
-    }), {}),
-    tombstones: describeStore(describeContents("tombstones", { keyPath: "id" 
}), {}),
-    ghostDepositGroups: describeStore(describeContents("ghostDepositGroups", {
-        keyPath: "contractTermsHash",
-    }), {}),
-};
-const walletMetadataStore = {
-    metaConfig: describeStore(describeContents("metaConfig", { keyPath: "key" 
}), {}),
-};
-
-/*
- This file is part of GNU Taler
- (C) 2017-2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$j = new Logger("timer.ts");
-class IntervalHandle {
-    constructor(h) {
-        this.h = h;
-    }
-    clear() {
-        clearInterval(this.h);
-    }
-    /**
-     * Make sure the event loop exits when the timer is the
-     * only event left.  Has no effect in the browser.
-     */
-    unref() {
-        if (typeof this.h === "object") {
-            this.h.unref();
-        }
-    }
-}
-class TimeoutHandle {
-    constructor(h) {
-        this.h = h;
-    }
-    clear() {
-        clearTimeout(this.h);
-    }
-    /**
-     * Make sure the event loop exits when the timer is the
-     * only event left.  Has no effect in the browser.
-     */
-    unref() {
-        if (typeof this.h === "object") {
-            this.h.unref();
-        }
-    }
-}
-/**
- * Get a performance counter in milliseconds.
- */
-const performanceNow = (() => {
-    // @ts-ignore
-    if (typeof process !== "undefined" && process.hrtime) {
-        return () => {
-            const t = process.hrtime();
-            return t[0] * 1e9 + t[1];
-        };
-    }
-    // @ts-ignore
-    if (typeof performance !== "undefined") {
-        // @ts-ignore
-        return () => performance.now();
-    }
-    return () => 0;
-})();
-/**
- * Call a function every time the delay given in milliseconds passes.
- */
-function every(delayMs, callback) {
-    return new IntervalHandle(setInterval(callback, delayMs));
-}
-/**
- * Call a function after the delay given in milliseconds passes.
- */
-function after(delayMs, callback) {
-    return new TimeoutHandle(setTimeout(callback, delayMs));
-}
-const nullTimerHandle = {
-    clear() {
-        // do nothing
-        return;
-    },
-    unref() {
-        // do nothing
-        return;
-    },
-};
-/**
- * Group of timers that can be destroyed at once.
- */
-class TimerGroup {
-    constructor() {
-        this.stopped = false;
-        this.timerMap = {};
-        this.idGen = 1;
-    }
-    stopCurrentAndFutureTimers() {
-        this.stopped = true;
-        for (const x in this.timerMap) {
-            if (!this.timerMap.hasOwnProperty(x)) {
-                continue;
-            }
-            this.timerMap[x].clear();
-            delete this.timerMap[x];
-        }
-    }
-    resolveAfter(delayMs) {
-        return new Promise((resolve, reject) => {
-            if (delayMs.d_ms !== "forever") {
-                this.after(delayMs.d_ms, () => {
-                    resolve();
-                });
-            }
-        });
-    }
-    after(delayMs, callback) {
-        if (this.stopped) {
-            logger$j.warn("dropping timer since timer group is stopped");
-            return nullTimerHandle;
-        }
-        const h = after(delayMs, callback);
-        const myId = this.idGen++;
-        this.timerMap[myId] = h;
-        const tm = this.timerMap;
-        return {
-            clear() {
-                h.clear();
-                delete tm[myId];
-            },
-            unref() {
-                h.unref();
-            },
-        };
-    }
-    every(delayMs, callback) {
-        if (this.stopped) {
-            logger$j.warn("dropping timer since timer group is stopped");
-            return nullTimerHandle;
-        }
-        const h = every(delayMs, callback);
-        const myId = this.idGen++;
-        this.timerMap[myId] = h;
-        const tm = this.timerMap;
-        return {
-            clear() {
-                h.clear();
-                delete tm[myId];
-            },
-            unref() {
-                h.unref();
-            },
-        };
-    }
-}
-
-// Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.
-// TypeScript port in 2019 by Florian Dold.
-// Public domain.
-//
-// Implementation derived from TweetNaCl version 20140427.
-// See for details: http://tweetnacl.cr.yp.to/
-const gf = function (init = []) {
-    const r = new Float64Array(16);
-    if (init)
-        for (let i = 0; i < init.length; i++)
-            r[i] = init[i];
-    return r;
-};
-//  Pluggable, initialized in high-level API below.
-let randombytes = function (x, n) {
-    throw new Error("no PRNG");
-};
-const _9 = new Uint8Array(32);
-_9[0] = 9;
-// prettier-ignore
-const gf0 = gf();
-const gf1 = gf([1]);
-const _121665 = gf([0xdb41, 1]);
-const D = gf([
-    0x78a3,
-    0x1359,
-    0x4dca,
-    0x75eb,
-    0xd8ab,
-    0x4141,
-    0x0a4d,
-    0x0070,
-    0xe898,
-    0x7779,
-    0x4079,
-    0x8cc7,
-    0xfe73,
-    0x2b6f,
-    0x6cee,
-    0x5203,
-]);
-const D2 = gf([
-    0xf159,
-    0x26b2,
-    0x9b94,
-    0xebd6,
-    0xb156,
-    0x8283,
-    0x149a,
-    0x00e0,
-    0xd130,
-    0xeef3,
-    0x80f2,
-    0x198e,
-    0xfce7,
-    0x56df,
-    0xd9dc,
-    0x2406,
-]);
-const X = gf([
-    0xd51a,
-    0x8f25,
-    0x2d60,
-    0xc956,
-    0xa7b2,
-    0x9525,
-    0xc760,
-    0x692c,
-    0xdc5c,
-    0xfdd6,
-    0xe231,
-    0xc0a4,
-    0x53fe,
-    0xcd6e,
-    0x36d3,
-    0x2169,
-]);
-const Y = gf([
-    0x6658,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-    0x6666,
-]);
-const I = gf([
-    0xa0b0,
-    0x4a0e,
-    0x1b27,
-    0xc4ee,
-    0xe478,
-    0xad2f,
-    0x1806,
-    0x2f43,
-    0xd7a7,
-    0x3dfb,
-    0x0099,
-    0x2b4d,
-    0xdf0b,
-    0x4fc1,
-    0x2480,
-    0x2b83,
-]);
-function ts64(x, i, h, l) {
-    x[i] = (h >> 24) & 0xff;
-    x[i + 1] = (h >> 16) & 0xff;
-    x[i + 2] = (h >> 8) & 0xff;
-    x[i + 3] = h & 0xff;
-    x[i + 4] = (l >> 24) & 0xff;
-    x[i + 5] = (l >> 16) & 0xff;
-    x[i + 6] = (l >> 8) & 0xff;
-    x[i + 7] = l & 0xff;
-}
-function vn(x, xi, y, yi, n) {
-    let i, d = 0;
-    for (i = 0; i < n; i++)
-        d |= x[xi + i] ^ y[yi + i];
-    return (1 & ((d - 1) >>> 8)) - 1;
-}
-function crypto_verify_16(x, xi, y, yi) {
-    return vn(x, xi, y, yi, 16);
-}
-function crypto_verify_32(x, xi, y, yi) {
-    return vn(x, xi, y, yi, 32);
-}
-function core_salsa20(o, p, k, c) {
-    var j0 = (c[0] & 0xff) |
-        ((c[1] & 0xff) << 8) |
-        ((c[2] & 0xff) << 16) |
-        ((c[3] & 0xff) << 24), j1 = (k[0] & 0xff) |
-        ((k[1] & 0xff) << 8) |
-        ((k[2] & 0xff) << 16) |
-        ((k[3] & 0xff) << 24), j2 = (k[4] & 0xff) |
-        ((k[5] & 0xff) << 8) |
-        ((k[6] & 0xff) << 16) |
-        ((k[7] & 0xff) << 24), j3 = (k[8] & 0xff) |
-        ((k[9] & 0xff) << 8) |
-        ((k[10] & 0xff) << 16) |
-        ((k[11] & 0xff) << 24), j4 = (k[12] & 0xff) |
-        ((k[13] & 0xff) << 8) |
-        ((k[14] & 0xff) << 16) |
-        ((k[15] & 0xff) << 24), j5 = (c[4] & 0xff) |
-        ((c[5] & 0xff) << 8) |
-        ((c[6] & 0xff) << 16) |
-        ((c[7] & 0xff) << 24), j6 = (p[0] & 0xff) |
-        ((p[1] & 0xff) << 8) |
-        ((p[2] & 0xff) << 16) |
-        ((p[3] & 0xff) << 24), j7 = (p[4] & 0xff) |
-        ((p[5] & 0xff) << 8) |
-        ((p[6] & 0xff) << 16) |
-        ((p[7] & 0xff) << 24), j8 = (p[8] & 0xff) |
-        ((p[9] & 0xff) << 8) |
-        ((p[10] & 0xff) << 16) |
-        ((p[11] & 0xff) << 24), j9 = (p[12] & 0xff) |
-        ((p[13] & 0xff) << 8) |
-        ((p[14] & 0xff) << 16) |
-        ((p[15] & 0xff) << 24), j10 = (c[8] & 0xff) |
-        ((c[9] & 0xff) << 8) |
-        ((c[10] & 0xff) << 16) |
-        ((c[11] & 0xff) << 24), j11 = (k[16] & 0xff) |
-        ((k[17] & 0xff) << 8) |
-        ((k[18] & 0xff) << 16) |
-        ((k[19] & 0xff) << 24), j12 = (k[20] & 0xff) |
-        ((k[21] & 0xff) << 8) |
-        ((k[22] & 0xff) << 16) |
-        ((k[23] & 0xff) << 24), j13 = (k[24] & 0xff) |
-        ((k[25] & 0xff) << 8) |
-        ((k[26] & 0xff) << 16) |
-        ((k[27] & 0xff) << 24), j14 = (k[28] & 0xff) |
-        ((k[29] & 0xff) << 8) |
-        ((k[30] & 0xff) << 16) |
-        ((k[31] & 0xff) << 24), j15 = (c[12] & 0xff) |
-        ((c[13] & 0xff) << 8) |
-        ((c[14] & 0xff) << 16) |
-        ((c[15] & 0xff) << 24);
-    var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = 
j7, x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, 
x15 = j15, u;
-    for (var i = 0; i < 20; i += 2) {
-        u = (x0 + x12) | 0;
-        x4 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x4 + x0) | 0;
-        x8 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x8 + x4) | 0;
-        x12 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x12 + x8) | 0;
-        x0 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x5 + x1) | 0;
-        x9 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x9 + x5) | 0;
-        x13 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x13 + x9) | 0;
-        x1 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x1 + x13) | 0;
-        x5 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x10 + x6) | 0;
-        x14 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x14 + x10) | 0;
-        x2 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x2 + x14) | 0;
-        x6 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x6 + x2) | 0;
-        x10 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x15 + x11) | 0;
-        x3 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x3 + x15) | 0;
-        x7 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x7 + x3) | 0;
-        x11 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x11 + x7) | 0;
-        x15 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x0 + x3) | 0;
-        x1 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x1 + x0) | 0;
-        x2 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x2 + x1) | 0;
-        x3 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x3 + x2) | 0;
-        x0 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x5 + x4) | 0;
-        x6 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x6 + x5) | 0;
-        x7 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x7 + x6) | 0;
-        x4 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x4 + x7) | 0;
-        x5 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x10 + x9) | 0;
-        x11 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x11 + x10) | 0;
-        x8 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x8 + x11) | 0;
-        x9 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x9 + x8) | 0;
-        x10 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x15 + x14) | 0;
-        x12 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x12 + x15) | 0;
-        x13 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x13 + x12) | 0;
-        x14 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x14 + x13) | 0;
-        x15 ^= (u << 18) | (u >>> (32 - 18));
-    }
-    x0 = (x0 + j0) | 0;
-    x1 = (x1 + j1) | 0;
-    x2 = (x2 + j2) | 0;
-    x3 = (x3 + j3) | 0;
-    x4 = (x4 + j4) | 0;
-    x5 = (x5 + j5) | 0;
-    x6 = (x6 + j6) | 0;
-    x7 = (x7 + j7) | 0;
-    x8 = (x8 + j8) | 0;
-    x9 = (x9 + j9) | 0;
-    x10 = (x10 + j10) | 0;
-    x11 = (x11 + j11) | 0;
-    x12 = (x12 + j12) | 0;
-    x13 = (x13 + j13) | 0;
-    x14 = (x14 + j14) | 0;
-    x15 = (x15 + j15) | 0;
-    o[0] = (x0 >>> 0) & 0xff;
-    o[1] = (x0 >>> 8) & 0xff;
-    o[2] = (x0 >>> 16) & 0xff;
-    o[3] = (x0 >>> 24) & 0xff;
-    o[4] = (x1 >>> 0) & 0xff;
-    o[5] = (x1 >>> 8) & 0xff;
-    o[6] = (x1 >>> 16) & 0xff;
-    o[7] = (x1 >>> 24) & 0xff;
-    o[8] = (x2 >>> 0) & 0xff;
-    o[9] = (x2 >>> 8) & 0xff;
-    o[10] = (x2 >>> 16) & 0xff;
-    o[11] = (x2 >>> 24) & 0xff;
-    o[12] = (x3 >>> 0) & 0xff;
-    o[13] = (x3 >>> 8) & 0xff;
-    o[14] = (x3 >>> 16) & 0xff;
-    o[15] = (x3 >>> 24) & 0xff;
-    o[16] = (x4 >>> 0) & 0xff;
-    o[17] = (x4 >>> 8) & 0xff;
-    o[18] = (x4 >>> 16) & 0xff;
-    o[19] = (x4 >>> 24) & 0xff;
-    o[20] = (x5 >>> 0) & 0xff;
-    o[21] = (x5 >>> 8) & 0xff;
-    o[22] = (x5 >>> 16) & 0xff;
-    o[23] = (x5 >>> 24) & 0xff;
-    o[24] = (x6 >>> 0) & 0xff;
-    o[25] = (x6 >>> 8) & 0xff;
-    o[26] = (x6 >>> 16) & 0xff;
-    o[27] = (x6 >>> 24) & 0xff;
-    o[28] = (x7 >>> 0) & 0xff;
-    o[29] = (x7 >>> 8) & 0xff;
-    o[30] = (x7 >>> 16) & 0xff;
-    o[31] = (x7 >>> 24) & 0xff;
-    o[32] = (x8 >>> 0) & 0xff;
-    o[33] = (x8 >>> 8) & 0xff;
-    o[34] = (x8 >>> 16) & 0xff;
-    o[35] = (x8 >>> 24) & 0xff;
-    o[36] = (x9 >>> 0) & 0xff;
-    o[37] = (x9 >>> 8) & 0xff;
-    o[38] = (x9 >>> 16) & 0xff;
-    o[39] = (x9 >>> 24) & 0xff;
-    o[40] = (x10 >>> 0) & 0xff;
-    o[41] = (x10 >>> 8) & 0xff;
-    o[42] = (x10 >>> 16) & 0xff;
-    o[43] = (x10 >>> 24) & 0xff;
-    o[44] = (x11 >>> 0) & 0xff;
-    o[45] = (x11 >>> 8) & 0xff;
-    o[46] = (x11 >>> 16) & 0xff;
-    o[47] = (x11 >>> 24) & 0xff;
-    o[48] = (x12 >>> 0) & 0xff;
-    o[49] = (x12 >>> 8) & 0xff;
-    o[50] = (x12 >>> 16) & 0xff;
-    o[51] = (x12 >>> 24) & 0xff;
-    o[52] = (x13 >>> 0) & 0xff;
-    o[53] = (x13 >>> 8) & 0xff;
-    o[54] = (x13 >>> 16) & 0xff;
-    o[55] = (x13 >>> 24) & 0xff;
-    o[56] = (x14 >>> 0) & 0xff;
-    o[57] = (x14 >>> 8) & 0xff;
-    o[58] = (x14 >>> 16) & 0xff;
-    o[59] = (x14 >>> 24) & 0xff;
-    o[60] = (x15 >>> 0) & 0xff;
-    o[61] = (x15 >>> 8) & 0xff;
-    o[62] = (x15 >>> 16) & 0xff;
-    o[63] = (x15 >>> 24) & 0xff;
-}
-function core_hsalsa20(o, p, k, c) {
-    var j0 = (c[0] & 0xff) |
-        ((c[1] & 0xff) << 8) |
-        ((c[2] & 0xff) << 16) |
-        ((c[3] & 0xff) << 24), j1 = (k[0] & 0xff) |
-        ((k[1] & 0xff) << 8) |
-        ((k[2] & 0xff) << 16) |
-        ((k[3] & 0xff) << 24), j2 = (k[4] & 0xff) |
-        ((k[5] & 0xff) << 8) |
-        ((k[6] & 0xff) << 16) |
-        ((k[7] & 0xff) << 24), j3 = (k[8] & 0xff) |
-        ((k[9] & 0xff) << 8) |
-        ((k[10] & 0xff) << 16) |
-        ((k[11] & 0xff) << 24), j4 = (k[12] & 0xff) |
-        ((k[13] & 0xff) << 8) |
-        ((k[14] & 0xff) << 16) |
-        ((k[15] & 0xff) << 24), j5 = (c[4] & 0xff) |
-        ((c[5] & 0xff) << 8) |
-        ((c[6] & 0xff) << 16) |
-        ((c[7] & 0xff) << 24), j6 = (p[0] & 0xff) |
-        ((p[1] & 0xff) << 8) |
-        ((p[2] & 0xff) << 16) |
-        ((p[3] & 0xff) << 24), j7 = (p[4] & 0xff) |
-        ((p[5] & 0xff) << 8) |
-        ((p[6] & 0xff) << 16) |
-        ((p[7] & 0xff) << 24), j8 = (p[8] & 0xff) |
-        ((p[9] & 0xff) << 8) |
-        ((p[10] & 0xff) << 16) |
-        ((p[11] & 0xff) << 24), j9 = (p[12] & 0xff) |
-        ((p[13] & 0xff) << 8) |
-        ((p[14] & 0xff) << 16) |
-        ((p[15] & 0xff) << 24), j10 = (c[8] & 0xff) |
-        ((c[9] & 0xff) << 8) |
-        ((c[10] & 0xff) << 16) |
-        ((c[11] & 0xff) << 24), j11 = (k[16] & 0xff) |
-        ((k[17] & 0xff) << 8) |
-        ((k[18] & 0xff) << 16) |
-        ((k[19] & 0xff) << 24), j12 = (k[20] & 0xff) |
-        ((k[21] & 0xff) << 8) |
-        ((k[22] & 0xff) << 16) |
-        ((k[23] & 0xff) << 24), j13 = (k[24] & 0xff) |
-        ((k[25] & 0xff) << 8) |
-        ((k[26] & 0xff) << 16) |
-        ((k[27] & 0xff) << 24), j14 = (k[28] & 0xff) |
-        ((k[29] & 0xff) << 8) |
-        ((k[30] & 0xff) << 16) |
-        ((k[31] & 0xff) << 24), j15 = (c[12] & 0xff) |
-        ((c[13] & 0xff) << 8) |
-        ((c[14] & 0xff) << 16) |
-        ((c[15] & 0xff) << 24);
-    var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = 
j7, x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, 
x15 = j15, u;
-    for (var i = 0; i < 20; i += 2) {
-        u = (x0 + x12) | 0;
-        x4 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x4 + x0) | 0;
-        x8 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x8 + x4) | 0;
-        x12 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x12 + x8) | 0;
-        x0 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x5 + x1) | 0;
-        x9 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x9 + x5) | 0;
-        x13 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x13 + x9) | 0;
-        x1 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x1 + x13) | 0;
-        x5 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x10 + x6) | 0;
-        x14 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x14 + x10) | 0;
-        x2 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x2 + x14) | 0;
-        x6 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x6 + x2) | 0;
-        x10 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x15 + x11) | 0;
-        x3 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x3 + x15) | 0;
-        x7 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x7 + x3) | 0;
-        x11 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x11 + x7) | 0;
-        x15 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x0 + x3) | 0;
-        x1 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x1 + x0) | 0;
-        x2 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x2 + x1) | 0;
-        x3 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x3 + x2) | 0;
-        x0 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x5 + x4) | 0;
-        x6 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x6 + x5) | 0;
-        x7 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x7 + x6) | 0;
-        x4 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x4 + x7) | 0;
-        x5 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x10 + x9) | 0;
-        x11 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x11 + x10) | 0;
-        x8 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x8 + x11) | 0;
-        x9 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x9 + x8) | 0;
-        x10 ^= (u << 18) | (u >>> (32 - 18));
-        u = (x15 + x14) | 0;
-        x12 ^= (u << 7) | (u >>> (32 - 7));
-        u = (x12 + x15) | 0;
-        x13 ^= (u << 9) | (u >>> (32 - 9));
-        u = (x13 + x12) | 0;
-        x14 ^= (u << 13) | (u >>> (32 - 13));
-        u = (x14 + x13) | 0;
-        x15 ^= (u << 18) | (u >>> (32 - 18));
-    }
-    o[0] = (x0 >>> 0) & 0xff;
-    o[1] = (x0 >>> 8) & 0xff;
-    o[2] = (x0 >>> 16) & 0xff;
-    o[3] = (x0 >>> 24) & 0xff;
-    o[4] = (x5 >>> 0) & 0xff;
-    o[5] = (x5 >>> 8) & 0xff;
-    o[6] = (x5 >>> 16) & 0xff;
-    o[7] = (x5 >>> 24) & 0xff;
-    o[8] = (x10 >>> 0) & 0xff;
-    o[9] = (x10 >>> 8) & 0xff;
-    o[10] = (x10 >>> 16) & 0xff;
-    o[11] = (x10 >>> 24) & 0xff;
-    o[12] = (x15 >>> 0) & 0xff;
-    o[13] = (x15 >>> 8) & 0xff;
-    o[14] = (x15 >>> 16) & 0xff;
-    o[15] = (x15 >>> 24) & 0xff;
-    o[16] = (x6 >>> 0) & 0xff;
-    o[17] = (x6 >>> 8) & 0xff;
-    o[18] = (x6 >>> 16) & 0xff;
-    o[19] = (x6 >>> 24) & 0xff;
-    o[20] = (x7 >>> 0) & 0xff;
-    o[21] = (x7 >>> 8) & 0xff;
-    o[22] = (x7 >>> 16) & 0xff;
-    o[23] = (x7 >>> 24) & 0xff;
-    o[24] = (x8 >>> 0) & 0xff;
-    o[25] = (x8 >>> 8) & 0xff;
-    o[26] = (x8 >>> 16) & 0xff;
-    o[27] = (x8 >>> 24) & 0xff;
-    o[28] = (x9 >>> 0) & 0xff;
-    o[29] = (x9 >>> 8) & 0xff;
-    o[30] = (x9 >>> 16) & 0xff;
-    o[31] = (x9 >>> 24) & 0xff;
-}
-var sigma = new Uint8Array([
-    101,
-    120,
-    112,
-    97,
-    110,
-    100,
-    32,
-    51,
-    50,
-    45,
-    98,
-    121,
-    116,
-    101,
-    32,
-    107,
-]);
-// "expand 32-byte k"
-function crypto_stream_salsa20_xor(c, cpos, m, mpos, b, n, k) {
-    var z = new Uint8Array(16), x = new Uint8Array(64);
-    var u, i;
-    for (i = 0; i < 16; i++)
-        z[i] = 0;
-    for (i = 0; i < 8; i++)
-        z[i] = n[i];
-    while (b >= 64) {
-        core_salsa20(x, z, k, sigma);
-        for (i = 0; i < 64; i++)
-            c[cpos + i] = m[mpos + i] ^ x[i];
-        u = 1;
-        for (i = 8; i < 16; i++) {
-            u = (u + (z[i] & 0xff)) | 0;
-            z[i] = u & 0xff;
-            u >>>= 8;
-        }
-        b -= 64;
-        cpos += 64;
-        mpos += 64;
-    }
-    if (b > 0) {
-        core_salsa20(x, z, k, sigma);
-        for (i = 0; i < b; i++)
-            c[cpos + i] = m[mpos + i] ^ x[i];
-    }
-    return 0;
-}
-function crypto_stream_salsa20(c, cpos, b, n, k) {
-    var z = new Uint8Array(16), x = new Uint8Array(64);
-    var u, i;
-    for (i = 0; i < 16; i++)
-        z[i] = 0;
-    for (i = 0; i < 8; i++)
-        z[i] = n[i];
-    while (b >= 64) {
-        core_salsa20(x, z, k, sigma);
-        for (i = 0; i < 64; i++)
-            c[cpos + i] = x[i];
-        u = 1;
-        for (i = 8; i < 16; i++) {
-            u = (u + (z[i] & 0xff)) | 0;
-            z[i] = u & 0xff;
-            u >>>= 8;
-        }
-        b -= 64;
-        cpos += 64;
-    }
-    if (b > 0) {
-        core_salsa20(x, z, k, sigma);
-        for (i = 0; i < b; i++)
-            c[cpos + i] = x[i];
-    }
-    return 0;
-}
-function crypto_stream(c, cpos, d, n, k) {
-    var s = new Uint8Array(32);
-    core_hsalsa20(s, n, k, sigma);
-    var sn = new Uint8Array(8);
-    for (var i = 0; i < 8; i++)
-        sn[i] = n[i + 16];
-    return crypto_stream_salsa20(c, cpos, d, sn, s);
-}
-function crypto_stream_xor(c, cpos, m, mpos, d, n, k) {
-    var s = new Uint8Array(32);
-    core_hsalsa20(s, n, k, sigma);
-    var sn = new Uint8Array(8);
-    for (var i = 0; i < 8; i++)
-        sn[i] = n[i + 16];
-    return crypto_stream_salsa20_xor(c, cpos, m, mpos, d, sn, s);
-}
-/*
- * Port of Andrew Moon's Poly1305-donna-16. Public domain.
- * https://github.com/floodyberry/poly1305-donna
- */
-class poly1305 {
-    constructor(key) {
-        this.buffer = new Uint8Array(16);
-        this.r = new Uint16Array(10);
-        this.h = new Uint16Array(10);
-        this.pad = new Uint16Array(8);
-        this.leftover = 0;
-        this.fin = 0;
-        var t0, t1, t2, t3, t4, t5, t6, t7;
-        t0 = (key[0] & 0xff) | ((key[1] & 0xff) << 8);
-        this.r[0] = t0 & 0x1fff;
-        t1 = (key[2] & 0xff) | ((key[3] & 0xff) << 8);
-        this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
-        t2 = (key[4] & 0xff) | ((key[5] & 0xff) << 8);
-        this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
-        t3 = (key[6] & 0xff) | ((key[7] & 0xff) << 8);
-        this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
-        t4 = (key[8] & 0xff) | ((key[9] & 0xff) << 8);
-        this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
-        this.r[5] = (t4 >>> 1) & 0x1ffe;
-        t5 = (key[10] & 0xff) | ((key[11] & 0xff) << 8);
-        this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
-        t6 = (key[12] & 0xff) | ((key[13] & 0xff) << 8);
-        this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
-        t7 = (key[14] & 0xff) | ((key[15] & 0xff) << 8);
-        this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
-        this.r[9] = (t7 >>> 5) & 0x007f;
-        this.pad[0] = (key[16] & 0xff) | ((key[17] & 0xff) << 8);
-        this.pad[1] = (key[18] & 0xff) | ((key[19] & 0xff) << 8);
-        this.pad[2] = (key[20] & 0xff) | ((key[21] & 0xff) << 8);
-        this.pad[3] = (key[22] & 0xff) | ((key[23] & 0xff) << 8);
-        this.pad[4] = (key[24] & 0xff) | ((key[25] & 0xff) << 8);
-        this.pad[5] = (key[26] & 0xff) | ((key[27] & 0xff) << 8);
-        this.pad[6] = (key[28] & 0xff) | ((key[29] & 0xff) << 8);
-        this.pad[7] = (key[30] & 0xff) | ((key[31] & 0xff) << 8);
-    }
-    blocks(m, mpos, bytes) {
-        var hibit = this.fin ? 0 : 1 << 11;
-        var t0, t1, t2, t3, t4, t5, t6, t7, c;
-        var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9;
-        var h0 = this.h[0], h1 = this.h[1], h2 = this.h[2], h3 = this.h[3], h4 
= this.h[4], h5 = this.h[5], h6 = this.h[6], h7 = this.h[7], h8 = this.h[8], h9 
= this.h[9];
-        var r0 = this.r[0], r1 = this.r[1], r2 = this.r[2], r3 = this.r[3], r4 
= this.r[4], r5 = this.r[5], r6 = this.r[6], r7 = this.r[7], r8 = this.r[8], r9 
= this.r[9];
-        while (bytes >= 16) {
-            t0 = (m[mpos + 0] & 0xff) | ((m[mpos + 1] & 0xff) << 8);
-            h0 += t0 & 0x1fff;
-            t1 = (m[mpos + 2] & 0xff) | ((m[mpos + 3] & 0xff) << 8);
-            h1 += ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
-            t2 = (m[mpos + 4] & 0xff) | ((m[mpos + 5] & 0xff) << 8);
-            h2 += ((t1 >>> 10) | (t2 << 6)) & 0x1fff;
-            t3 = (m[mpos + 6] & 0xff) | ((m[mpos + 7] & 0xff) << 8);
-            h3 += ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
-            t4 = (m[mpos + 8] & 0xff) | ((m[mpos + 9] & 0xff) << 8);
-            h4 += ((t3 >>> 4) | (t4 << 12)) & 0x1fff;
-            h5 += (t4 >>> 1) & 0x1fff;
-            t5 = (m[mpos + 10] & 0xff) | ((m[mpos + 11] & 0xff) << 8);
-            h6 += ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
-            t6 = (m[mpos + 12] & 0xff) | ((m[mpos + 13] & 0xff) << 8);
-            h7 += ((t5 >>> 11) | (t6 << 5)) & 0x1fff;
-            t7 = (m[mpos + 14] & 0xff) | ((m[mpos + 15] & 0xff) << 8);
-            h8 += ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
-            h9 += (t7 >>> 5) | hibit;
-            c = 0;
-            d0 = c;
-            d0 += h0 * r0;
-            d0 += h1 * (5 * r9);
-            d0 += h2 * (5 * r8);
-            d0 += h3 * (5 * r7);
-            d0 += h4 * (5 * r6);
-            c = d0 >>> 13;
-            d0 &= 0x1fff;
-            d0 += h5 * (5 * r5);
-            d0 += h6 * (5 * r4);
-            d0 += h7 * (5 * r3);
-            d0 += h8 * (5 * r2);
-            d0 += h9 * (5 * r1);
-            c += d0 >>> 13;
-            d0 &= 0x1fff;
-            d1 = c;
-            d1 += h0 * r1;
-            d1 += h1 * r0;
-            d1 += h2 * (5 * r9);
-            d1 += h3 * (5 * r8);
-            d1 += h4 * (5 * r7);
-            c = d1 >>> 13;
-            d1 &= 0x1fff;
-            d1 += h5 * (5 * r6);
-            d1 += h6 * (5 * r5);
-            d1 += h7 * (5 * r4);
-            d1 += h8 * (5 * r3);
-            d1 += h9 * (5 * r2);
-            c += d1 >>> 13;
-            d1 &= 0x1fff;
-            d2 = c;
-            d2 += h0 * r2;
-            d2 += h1 * r1;
-            d2 += h2 * r0;
-            d2 += h3 * (5 * r9);
-            d2 += h4 * (5 * r8);
-            c = d2 >>> 13;
-            d2 &= 0x1fff;
-            d2 += h5 * (5 * r7);
-            d2 += h6 * (5 * r6);
-            d2 += h7 * (5 * r5);
-            d2 += h8 * (5 * r4);
-            d2 += h9 * (5 * r3);
-            c += d2 >>> 13;
-            d2 &= 0x1fff;
-            d3 = c;
-            d3 += h0 * r3;
-            d3 += h1 * r2;
-            d3 += h2 * r1;
-            d3 += h3 * r0;
-            d3 += h4 * (5 * r9);
-            c = d3 >>> 13;
-            d3 &= 0x1fff;
-            d3 += h5 * (5 * r8);
-            d3 += h6 * (5 * r7);
-            d3 += h7 * (5 * r6);
-            d3 += h8 * (5 * r5);
-            d3 += h9 * (5 * r4);
-            c += d3 >>> 13;
-            d3 &= 0x1fff;
-            d4 = c;
-            d4 += h0 * r4;
-            d4 += h1 * r3;
-            d4 += h2 * r2;
-            d4 += h3 * r1;
-            d4 += h4 * r0;
-            c = d4 >>> 13;
-            d4 &= 0x1fff;
-            d4 += h5 * (5 * r9);
-            d4 += h6 * (5 * r8);
-            d4 += h7 * (5 * r7);
-            d4 += h8 * (5 * r6);
-            d4 += h9 * (5 * r5);
-            c += d4 >>> 13;
-            d4 &= 0x1fff;
-            d5 = c;
-            d5 += h0 * r5;
-            d5 += h1 * r4;
-            d5 += h2 * r3;
-            d5 += h3 * r2;
-            d5 += h4 * r1;
-            c = d5 >>> 13;
-            d5 &= 0x1fff;
-            d5 += h5 * r0;
-            d5 += h6 * (5 * r9);
-            d5 += h7 * (5 * r8);
-            d5 += h8 * (5 * r7);
-            d5 += h9 * (5 * r6);
-            c += d5 >>> 13;
-            d5 &= 0x1fff;
-            d6 = c;
-            d6 += h0 * r6;
-            d6 += h1 * r5;
-            d6 += h2 * r4;
-            d6 += h3 * r3;
-            d6 += h4 * r2;
-            c = d6 >>> 13;
-            d6 &= 0x1fff;
-            d6 += h5 * r1;
-            d6 += h6 * r0;
-            d6 += h7 * (5 * r9);
-            d6 += h8 * (5 * r8);
-            d6 += h9 * (5 * r7);
-            c += d6 >>> 13;
-            d6 &= 0x1fff;
-            d7 = c;
-            d7 += h0 * r7;
-            d7 += h1 * r6;
-            d7 += h2 * r5;
-            d7 += h3 * r4;
-            d7 += h4 * r3;
-            c = d7 >>> 13;
-            d7 &= 0x1fff;
-            d7 += h5 * r2;
-            d7 += h6 * r1;
-            d7 += h7 * r0;
-            d7 += h8 * (5 * r9);
-            d7 += h9 * (5 * r8);
-            c += d7 >>> 13;
-            d7 &= 0x1fff;
-            d8 = c;
-            d8 += h0 * r8;
-            d8 += h1 * r7;
-            d8 += h2 * r6;
-            d8 += h3 * r5;
-            d8 += h4 * r4;
-            c = d8 >>> 13;
-            d8 &= 0x1fff;
-            d8 += h5 * r3;
-            d8 += h6 * r2;
-            d8 += h7 * r1;
-            d8 += h8 * r0;
-            d8 += h9 * (5 * r9);
-            c += d8 >>> 13;
-            d8 &= 0x1fff;
-            d9 = c;
-            d9 += h0 * r9;
-            d9 += h1 * r8;
-            d9 += h2 * r7;
-            d9 += h3 * r6;
-            d9 += h4 * r5;
-            c = d9 >>> 13;
-            d9 &= 0x1fff;
-            d9 += h5 * r4;
-            d9 += h6 * r3;
-            d9 += h7 * r2;
-            d9 += h8 * r1;
-            d9 += h9 * r0;
-            c += d9 >>> 13;
-            d9 &= 0x1fff;
-            c = ((c << 2) + c) | 0;
-            c = (c + d0) | 0;
-            d0 = c & 0x1fff;
-            c = c >>> 13;
-            d1 += c;
-            h0 = d0;
-            h1 = d1;
-            h2 = d2;
-            h3 = d3;
-            h4 = d4;
-            h5 = d5;
-            h6 = d6;
-            h7 = d7;
-            h8 = d8;
-            h9 = d9;
-            mpos += 16;
-            bytes -= 16;
-        }
-        this.h[0] = h0;
-        this.h[1] = h1;
-        this.h[2] = h2;
-        this.h[3] = h3;
-        this.h[4] = h4;
-        this.h[5] = h5;
-        this.h[6] = h6;
-        this.h[7] = h7;
-        this.h[8] = h8;
-        this.h[9] = h9;
-    }
-    finish(mac, macpos) {
-        var g = new Uint16Array(10);
-        var c, mask, f, i;
-        if (this.leftover) {
-            i = this.leftover;
-            this.buffer[i++] = 1;
-            for (; i < 16; i++)
-                this.buffer[i] = 0;
-            this.fin = 1;
-            this.blocks(this.buffer, 0, 16);
-        }
-        c = this.h[1] >>> 13;
-        this.h[1] &= 0x1fff;
-        for (i = 2; i < 10; i++) {
-            this.h[i] += c;
-            c = this.h[i] >>> 13;
-            this.h[i] &= 0x1fff;
-        }
-        this.h[0] += c * 5;
-        c = this.h[0] >>> 13;
-        this.h[0] &= 0x1fff;
-        this.h[1] += c;
-        c = this.h[1] >>> 13;
-        this.h[1] &= 0x1fff;
-        this.h[2] += c;
-        g[0] = this.h[0] + 5;
-        c = g[0] >>> 13;
-        g[0] &= 0x1fff;
-        for (i = 1; i < 10; i++) {
-            g[i] = this.h[i] + c;
-            c = g[i] >>> 13;
-            g[i] &= 0x1fff;
-        }
-        g[9] -= 1 << 13;
-        mask = (c ^ 1) - 1;
-        for (i = 0; i < 10; i++)
-            g[i] &= mask;
-        mask = ~mask;
-        for (i = 0; i < 10; i++)
-            this.h[i] = (this.h[i] & mask) | g[i];
-        this.h[0] = (this.h[0] | (this.h[1] << 13)) & 0xffff;
-        this.h[1] = ((this.h[1] >>> 3) | (this.h[2] << 10)) & 0xffff;
-        this.h[2] = ((this.h[2] >>> 6) | (this.h[3] << 7)) & 0xffff;
-        this.h[3] = ((this.h[3] >>> 9) | (this.h[4] << 4)) & 0xffff;
-        this.h[4] =
-            ((this.h[4] >>> 12) | (this.h[5] << 1) | (this.h[6] << 14)) & 
0xffff;
-        this.h[5] = ((this.h[6] >>> 2) | (this.h[7] << 11)) & 0xffff;
-        this.h[6] = ((this.h[7] >>> 5) | (this.h[8] << 8)) & 0xffff;
-        this.h[7] = ((this.h[8] >>> 8) | (this.h[9] << 5)) & 0xffff;
-        f = this.h[0] + this.pad[0];
-        this.h[0] = f & 0xffff;
-        for (i = 1; i < 8; i++) {
-            f = (((this.h[i] + this.pad[i]) | 0) + (f >>> 16)) | 0;
-            this.h[i] = f & 0xffff;
-        }
-        mac[macpos + 0] = (this.h[0] >>> 0) & 0xff;
-        mac[macpos + 1] = (this.h[0] >>> 8) & 0xff;
-        mac[macpos + 2] = (this.h[1] >>> 0) & 0xff;
-        mac[macpos + 3] = (this.h[1] >>> 8) & 0xff;
-        mac[macpos + 4] = (this.h[2] >>> 0) & 0xff;
-        mac[macpos + 5] = (this.h[2] >>> 8) & 0xff;
-        mac[macpos + 6] = (this.h[3] >>> 0) & 0xff;
-        mac[macpos + 7] = (this.h[3] >>> 8) & 0xff;
-        mac[macpos + 8] = (this.h[4] >>> 0) & 0xff;
-        mac[macpos + 9] = (this.h[4] >>> 8) & 0xff;
-        mac[macpos + 10] = (this.h[5] >>> 0) & 0xff;
-        mac[macpos + 11] = (this.h[5] >>> 8) & 0xff;
-        mac[macpos + 12] = (this.h[6] >>> 0) & 0xff;
-        mac[macpos + 13] = (this.h[6] >>> 8) & 0xff;
-        mac[macpos + 14] = (this.h[7] >>> 0) & 0xff;
-        mac[macpos + 15] = (this.h[7] >>> 8) & 0xff;
-    }
-    update(m, mpos, bytes) {
-        let i;
-        let want;
-        if (this.leftover) {
-            want = 16 - this.leftover;
-            if (want > bytes)
-                want = bytes;
-            for (i = 0; i < want; i++)
-                this.buffer[this.leftover + i] = m[mpos + i];
-            bytes -= want;
-            mpos += want;
-            this.leftover += want;
-            if (this.leftover < 16)
-                return;
-            this.blocks(this.buffer, 0, 16);
-            this.leftover = 0;
-        }
-        if (bytes >= 16) {
-            want = bytes - (bytes % 16);
-            this.blocks(m, mpos, want);
-            mpos += want;
-            bytes -= want;
-        }
-        if (bytes) {
-            for (i = 0; i < bytes; i++)
-                this.buffer[this.leftover + i] = m[mpos + i];
-            this.leftover += bytes;
-        }
-    }
-}
-function crypto_onetimeauth(out, outpos, m, mpos, n, k) {
-    var s = new poly1305(k);
-    s.update(m, mpos, n);
-    s.finish(out, outpos);
-    return 0;
-}
-function crypto_onetimeauth_verify(h, hpos, m, mpos, n, k) {
-    var x = new Uint8Array(16);
-    crypto_onetimeauth(x, 0, m, mpos, n, k);
-    return crypto_verify_16(h, hpos, x, 0);
-}
-function crypto_secretbox(c, m, d, n, k) {
-    var i;
-    if (d < 32)
-        return -1;
-    crypto_stream_xor(c, 0, m, 0, d, n, k);
-    crypto_onetimeauth(c, 16, c, 32, d - 32, c);
-    for (i = 0; i < 16; i++)
-        c[i] = 0;
-    return 0;
-}
-function crypto_secretbox_open(m, c, d, n, k) {
-    var i;
-    var x = new Uint8Array(32);
-    if (d < 32)
-        return -1;
-    crypto_stream(x, 0, 32, n, k);
-    if (crypto_onetimeauth_verify(c, 16, c, 32, d - 32, x) !== 0)
-        return -1;
-    crypto_stream_xor(m, 0, c, 0, d, n, k);
-    for (i = 0; i < 32; i++)
-        m[i] = 0;
-    return 0;
-}
-function set25519(r, a) {
-    let i;
-    for (i = 0; i < 16; i++)
-        r[i] = a[i] | 0;
-}
-function car25519(o) {
-    let i, v, c = 1;
-    for (i = 0; i < 16; i++) {
-        v = o[i] + c + 65535;
-        c = Math.floor(v / 65536);
-        o[i] = v - c * 65536;
-    }
-    o[0] += c - 1 + 37 * (c - 1);
-}
-function sel25519(p, q, b) {
-    let t;
-    const c = ~(b - 1);
-    for (let i = 0; i < 16; i++) {
-        t = c & (p[i] ^ q[i]);
-        p[i] ^= t;
-        q[i] ^= t;
-    }
-}
-function pack25519(o, n) {
-    let i, j, b;
-    const m = gf(), t = gf();
-    for (i = 0; i < 16; i++)
-        t[i] = n[i];
-    car25519(t);
-    car25519(t);
-    car25519(t);
-    for (j = 0; j < 2; j++) {
-        m[0] = t[0] - 0xffed;
-        for (i = 1; i < 15; i++) {
-            m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
-            m[i - 1] &= 0xffff;
-        }
-        m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
-        b = (m[15] >> 16) & 1;
-        m[14] &= 0xffff;
-        sel25519(t, m, 1 - b);
-    }
-    for (i = 0; i < 16; i++) {
-        o[2 * i] = t[i] & 0xff;
-        o[2 * i + 1] = t[i] >> 8;
-    }
-}
-function neq25519(a, b) {
-    const c = new Uint8Array(32), d = new Uint8Array(32);
-    pack25519(c, a);
-    pack25519(d, b);
-    return crypto_verify_32(c, 0, d, 0);
-}
-function par25519(a) {
-    const d = new Uint8Array(32);
-    pack25519(d, a);
-    return d[0] & 1;
-}
-function unpack25519(o, n) {
-    let i;
-    for (i = 0; i < 16; i++)
-        o[i] = n[2 * i] + (n[2 * i + 1] << 8);
-    o[15] &= 0x7fff;
-}
-function A(o, a, b) {
-    for (let i = 0; i < 16; i++)
-        o[i] = a[i] + b[i];
-}
-function Z(o, a, b) {
-    for (let i = 0; i < 16; i++)
-        o[i] = a[i] - b[i];
-}
-function M(o, a, b) {
-    let v, c, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, 
t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, t16 = 0, 
t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, t24 = 0, t25 = 
0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0;
-    const b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 
= b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11], b12 = b[12], 
b13 = b[13], b14 = b[14], b15 = b[15];
-    v = a[0];
-    t0 += v * b0;
-    t1 += v * b1;
-    t2 += v * b2;
-    t3 += v * b3;
-    t4 += v * b4;
-    t5 += v * b5;
-    t6 += v * b6;
-    t7 += v * b7;
-    t8 += v * b8;
-    t9 += v * b9;
-    t10 += v * b10;
-    t11 += v * b11;
-    t12 += v * b12;
-    t13 += v * b13;
-    t14 += v * b14;
-    t15 += v * b15;
-    v = a[1];
-    t1 += v * b0;
-    t2 += v * b1;
-    t3 += v * b2;
-    t4 += v * b3;
-    t5 += v * b4;
-    t6 += v * b5;
-    t7 += v * b6;
-    t8 += v * b7;
-    t9 += v * b8;
-    t10 += v * b9;
-    t11 += v * b10;
-    t12 += v * b11;
-    t13 += v * b12;
-    t14 += v * b13;
-    t15 += v * b14;
-    t16 += v * b15;
-    v = a[2];
-    t2 += v * b0;
-    t3 += v * b1;
-    t4 += v * b2;
-    t5 += v * b3;
-    t6 += v * b4;
-    t7 += v * b5;
-    t8 += v * b6;
-    t9 += v * b7;
-    t10 += v * b8;
-    t11 += v * b9;
-    t12 += v * b10;
-    t13 += v * b11;
-    t14 += v * b12;
-    t15 += v * b13;
-    t16 += v * b14;
-    t17 += v * b15;
-    v = a[3];
-    t3 += v * b0;
-    t4 += v * b1;
-    t5 += v * b2;
-    t6 += v * b3;
-    t7 += v * b4;
-    t8 += v * b5;
-    t9 += v * b6;
-    t10 += v * b7;
-    t11 += v * b8;
-    t12 += v * b9;
-    t13 += v * b10;
-    t14 += v * b11;
-    t15 += v * b12;
-    t16 += v * b13;
-    t17 += v * b14;
-    t18 += v * b15;
-    v = a[4];
-    t4 += v * b0;
-    t5 += v * b1;
-    t6 += v * b2;
-    t7 += v * b3;
-    t8 += v * b4;
-    t9 += v * b5;
-    t10 += v * b6;
-    t11 += v * b7;
-    t12 += v * b8;
-    t13 += v * b9;
-    t14 += v * b10;
-    t15 += v * b11;
-    t16 += v * b12;
-    t17 += v * b13;
-    t18 += v * b14;
-    t19 += v * b15;
-    v = a[5];
-    t5 += v * b0;
-    t6 += v * b1;
-    t7 += v * b2;
-    t8 += v * b3;
-    t9 += v * b4;
-    t10 += v * b5;
-    t11 += v * b6;
-    t12 += v * b7;
-    t13 += v * b8;
-    t14 += v * b9;
-    t15 += v * b10;
-    t16 += v * b11;
-    t17 += v * b12;
-    t18 += v * b13;
-    t19 += v * b14;
-    t20 += v * b15;
-    v = a[6];
-    t6 += v * b0;
-    t7 += v * b1;
-    t8 += v * b2;
-    t9 += v * b3;
-    t10 += v * b4;
-    t11 += v * b5;
-    t12 += v * b6;
-    t13 += v * b7;
-    t14 += v * b8;
-    t15 += v * b9;
-    t16 += v * b10;
-    t17 += v * b11;
-    t18 += v * b12;
-    t19 += v * b13;
-    t20 += v * b14;
-    t21 += v * b15;
-    v = a[7];
-    t7 += v * b0;
-    t8 += v * b1;
-    t9 += v * b2;
-    t10 += v * b3;
-    t11 += v * b4;
-    t12 += v * b5;
-    t13 += v * b6;
-    t14 += v * b7;
-    t15 += v * b8;
-    t16 += v * b9;
-    t17 += v * b10;
-    t18 += v * b11;
-    t19 += v * b12;
-    t20 += v * b13;
-    t21 += v * b14;
-    t22 += v * b15;
-    v = a[8];
-    t8 += v * b0;
-    t9 += v * b1;
-    t10 += v * b2;
-    t11 += v * b3;
-    t12 += v * b4;
-    t13 += v * b5;
-    t14 += v * b6;
-    t15 += v * b7;
-    t16 += v * b8;
-    t17 += v * b9;
-    t18 += v * b10;
-    t19 += v * b11;
-    t20 += v * b12;
-    t21 += v * b13;
-    t22 += v * b14;
-    t23 += v * b15;
-    v = a[9];
-    t9 += v * b0;
-    t10 += v * b1;
-    t11 += v * b2;
-    t12 += v * b3;
-    t13 += v * b4;
-    t14 += v * b5;
-    t15 += v * b6;
-    t16 += v * b7;
-    t17 += v * b8;
-    t18 += v * b9;
-    t19 += v * b10;
-    t20 += v * b11;
-    t21 += v * b12;
-    t22 += v * b13;
-    t23 += v * b14;
-    t24 += v * b15;
-    v = a[10];
-    t10 += v * b0;
-    t11 += v * b1;
-    t12 += v * b2;
-    t13 += v * b3;
-    t14 += v * b4;
-    t15 += v * b5;
-    t16 += v * b6;
-    t17 += v * b7;
-    t18 += v * b8;
-    t19 += v * b9;
-    t20 += v * b10;
-    t21 += v * b11;
-    t22 += v * b12;
-    t23 += v * b13;
-    t24 += v * b14;
-    t25 += v * b15;
-    v = a[11];
-    t11 += v * b0;
-    t12 += v * b1;
-    t13 += v * b2;
-    t14 += v * b3;
-    t15 += v * b4;
-    t16 += v * b5;
-    t17 += v * b6;
-    t18 += v * b7;
-    t19 += v * b8;
-    t20 += v * b9;
-    t21 += v * b10;
-    t22 += v * b11;
-    t23 += v * b12;
-    t24 += v * b13;
-    t25 += v * b14;
-    t26 += v * b15;
-    v = a[12];
-    t12 += v * b0;
-    t13 += v * b1;
-    t14 += v * b2;
-    t15 += v * b3;
-    t16 += v * b4;
-    t17 += v * b5;
-    t18 += v * b6;
-    t19 += v * b7;
-    t20 += v * b8;
-    t21 += v * b9;
-    t22 += v * b10;
-    t23 += v * b11;
-    t24 += v * b12;
-    t25 += v * b13;
-    t26 += v * b14;
-    t27 += v * b15;
-    v = a[13];
-    t13 += v * b0;
-    t14 += v * b1;
-    t15 += v * b2;
-    t16 += v * b3;
-    t17 += v * b4;
-    t18 += v * b5;
-    t19 += v * b6;
-    t20 += v * b7;
-    t21 += v * b8;
-    t22 += v * b9;
-    t23 += v * b10;
-    t24 += v * b11;
-    t25 += v * b12;
-    t26 += v * b13;
-    t27 += v * b14;
-    t28 += v * b15;
-    v = a[14];
-    t14 += v * b0;
-    t15 += v * b1;
-    t16 += v * b2;
-    t17 += v * b3;
-    t18 += v * b4;
-    t19 += v * b5;
-    t20 += v * b6;
-    t21 += v * b7;
-    t22 += v * b8;
-    t23 += v * b9;
-    t24 += v * b10;
-    t25 += v * b11;
-    t26 += v * b12;
-    t27 += v * b13;
-    t28 += v * b14;
-    t29 += v * b15;
-    v = a[15];
-    t15 += v * b0;
-    t16 += v * b1;
-    t17 += v * b2;
-    t18 += v * b3;
-    t19 += v * b4;
-    t20 += v * b5;
-    t21 += v * b6;
-    t22 += v * b7;
-    t23 += v * b8;
-    t24 += v * b9;
-    t25 += v * b10;
-    t26 += v * b11;
-    t27 += v * b12;
-    t28 += v * b13;
-    t29 += v * b14;
-    t30 += v * b15;
-    t0 += 38 * t16;
-    t1 += 38 * t17;
-    t2 += 38 * t18;
-    t3 += 38 * t19;
-    t4 += 38 * t20;
-    t5 += 38 * t21;
-    t6 += 38 * t22;
-    t7 += 38 * t23;
-    t8 += 38 * t24;
-    t9 += 38 * t25;
-    t10 += 38 * t26;
-    t11 += 38 * t27;
-    t12 += 38 * t28;
-    t13 += 38 * t29;
-    t14 += 38 * t30;
-    // t15 left as is
-    // first car
-    c = 1;
-    v = t0 + c + 65535;
-    c = Math.floor(v / 65536);
-    t0 = v - c * 65536;
-    v = t1 + c + 65535;
-    c = Math.floor(v / 65536);
-    t1 = v - c * 65536;
-    v = t2 + c + 65535;
-    c = Math.floor(v / 65536);
-    t2 = v - c * 65536;
-    v = t3 + c + 65535;
-    c = Math.floor(v / 65536);
-    t3 = v - c * 65536;
-    v = t4 + c + 65535;
-    c = Math.floor(v / 65536);
-    t4 = v - c * 65536;
-    v = t5 + c + 65535;
-    c = Math.floor(v / 65536);
-    t5 = v - c * 65536;
-    v = t6 + c + 65535;
-    c = Math.floor(v / 65536);
-    t6 = v - c * 65536;
-    v = t7 + c + 65535;
-    c = Math.floor(v / 65536);
-    t7 = v - c * 65536;
-    v = t8 + c + 65535;
-    c = Math.floor(v / 65536);
-    t8 = v - c * 65536;
-    v = t9 + c + 65535;
-    c = Math.floor(v / 65536);
-    t9 = v - c * 65536;
-    v = t10 + c + 65535;
-    c = Math.floor(v / 65536);
-    t10 = v - c * 65536;
-    v = t11 + c + 65535;
-    c = Math.floor(v / 65536);
-    t11 = v - c * 65536;
-    v = t12 + c + 65535;
-    c = Math.floor(v / 65536);
-    t12 = v - c * 65536;
-    v = t13 + c + 65535;
-    c = Math.floor(v / 65536);
-    t13 = v - c * 65536;
-    v = t14 + c + 65535;
-    c = Math.floor(v / 65536);
-    t14 = v - c * 65536;
-    v = t15 + c + 65535;
-    c = Math.floor(v / 65536);
-    t15 = v - c * 65536;
-    t0 += c - 1 + 37 * (c - 1);
-    // second car
-    c = 1;
-    v = t0 + c + 65535;
-    c = Math.floor(v / 65536);
-    t0 = v - c * 65536;
-    v = t1 + c + 65535;
-    c = Math.floor(v / 65536);
-    t1 = v - c * 65536;
-    v = t2 + c + 65535;
-    c = Math.floor(v / 65536);
-    t2 = v - c * 65536;
-    v = t3 + c + 65535;
-    c = Math.floor(v / 65536);
-    t3 = v - c * 65536;
-    v = t4 + c + 65535;
-    c = Math.floor(v / 65536);
-    t4 = v - c * 65536;
-    v = t5 + c + 65535;
-    c = Math.floor(v / 65536);
-    t5 = v - c * 65536;
-    v = t6 + c + 65535;
-    c = Math.floor(v / 65536);
-    t6 = v - c * 65536;
-    v = t7 + c + 65535;
-    c = Math.floor(v / 65536);
-    t7 = v - c * 65536;
-    v = t8 + c + 65535;
-    c = Math.floor(v / 65536);
-    t8 = v - c * 65536;
-    v = t9 + c + 65535;
-    c = Math.floor(v / 65536);
-    t9 = v - c * 65536;
-    v = t10 + c + 65535;
-    c = Math.floor(v / 65536);
-    t10 = v - c * 65536;
-    v = t11 + c + 65535;
-    c = Math.floor(v / 65536);
-    t11 = v - c * 65536;
-    v = t12 + c + 65535;
-    c = Math.floor(v / 65536);
-    t12 = v - c * 65536;
-    v = t13 + c + 65535;
-    c = Math.floor(v / 65536);
-    t13 = v - c * 65536;
-    v = t14 + c + 65535;
-    c = Math.floor(v / 65536);
-    t14 = v - c * 65536;
-    v = t15 + c + 65535;
-    c = Math.floor(v / 65536);
-    t15 = v - c * 65536;
-    t0 += c - 1 + 37 * (c - 1);
-    o[0] = t0;
-    o[1] = t1;
-    o[2] = t2;
-    o[3] = t3;
-    o[4] = t4;
-    o[5] = t5;
-    o[6] = t6;
-    o[7] = t7;
-    o[8] = t8;
-    o[9] = t9;
-    o[10] = t10;
-    o[11] = t11;
-    o[12] = t12;
-    o[13] = t13;
-    o[14] = t14;
-    o[15] = t15;
-}
-function S(o, a) {
-    M(o, a, a);
-}
-function inv25519(o, i) {
-    const c = gf();
-    let a;
-    for (a = 0; a < 16; a++)
-        c[a] = i[a];
-    for (a = 253; a >= 0; a--) {
-        S(c, c);
-        if (a !== 2 && a !== 4)
-            M(c, c, i);
-    }
-    for (a = 0; a < 16; a++)
-        o[a] = c[a];
-}
-function pow2523(o, i) {
-    const c = gf();
-    let a;
-    for (a = 0; a < 16; a++)
-        c[a] = i[a];
-    for (a = 250; a >= 0; a--) {
-        S(c, c);
-        if (a !== 1)
-            M(c, c, i);
-    }
-    for (a = 0; a < 16; a++)
-        o[a] = c[a];
-}
-function crypto_scalarmult(q, n, p) {
-    const z = new Uint8Array(32);
-    const x = new Float64Array(80);
-    let r;
-    let i;
-    const a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf();
-    for (i = 0; i < 31; i++)
-        z[i] = n[i];
-    z[31] = (n[31] & 127) | 64;
-    z[0] &= 248;
-    unpack25519(x, p);
-    for (i = 0; i < 16; i++) {
-        b[i] = x[i];
-        d[i] = a[i] = c[i] = 0;
-    }
-    a[0] = d[0] = 1;
-    for (i = 254; i >= 0; --i) {
-        r = (z[i >>> 3] >>> (i & 7)) & 1;
-        sel25519(a, b, r);
-        sel25519(c, d, r);
-        A(e, a, c);
-        Z(a, a, c);
-        A(c, b, d);
-        Z(b, b, d);
-        S(d, e);
-        S(f, a);
-        M(a, c, a);
-        M(c, b, e);
-        A(e, a, c);
-        Z(a, a, c);
-        S(b, a);
-        Z(c, d, f);
-        M(a, c, _121665);
-        A(a, a, d);
-        M(c, c, a);
-        M(a, d, f);
-        M(d, b, x);
-        S(b, e);
-        sel25519(a, b, r);
-        sel25519(c, d, r);
-    }
-    for (i = 0; i < 16; i++) {
-        x[i + 16] = a[i];
-        x[i + 32] = c[i];
-        x[i + 48] = b[i];
-        x[i + 64] = d[i];
-    }
-    const x32 = x.subarray(32);
-    const x16 = x.subarray(16);
-    inv25519(x32, x32);
-    M(x16, x16, x32);
-    pack25519(q, x16);
-    return 0;
-}
-function crypto_scalarmult_base(q, n) {
-    return crypto_scalarmult(q, n, _9);
-}
-// prettier-ignore
-const K$1 = [
-    0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd,
-    0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,
-    0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019,
-    0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,
-    0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe,
-    0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,
-    0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1,
-    0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,
-    0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3,
-    0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,
-    0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483,
-    0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,
-    0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210,
-    0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,
-    0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725,
-    0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,
-    0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926,
-    0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,
-    0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8,
-    0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,
-    0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001,
-    0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,
-    0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910,
-    0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,
-    0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53,
-    0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,
-    0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb,
-    0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,
-    0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60,
-    0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,
-    0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9,
-    0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,
-    0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207,
-    0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,
-    0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6,
-    0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,
-    0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493,
-    0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,
-    0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a,
-    0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817
-];
-function crypto_hashblocks_hl(hh, hl, m, n) {
-    const wh = new Int32Array(16), wl = new Int32Array(16);
-    let bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, bl0, bl1, bl2, bl3, bl4, bl5, 
bl6, bl7, th, tl, i, j, h, l, a, b, c, d;
-    let ah0 = hh[0], ah1 = hh[1], ah2 = hh[2], ah3 = hh[3], ah4 = hh[4], ah5 = 
hh[5], ah6 = hh[6], ah7 = hh[7], al0 = hl[0], al1 = hl[1], al2 = hl[2], al3 = 
hl[3], al4 = hl[4], al5 = hl[5], al6 = hl[6], al7 = hl[7];
-    let pos = 0;
-    while (n >= 128) {
-        for (i = 0; i < 16; i++) {
-            j = 8 * i + pos;
-            wh[i] = (m[j + 0] << 24) | (m[j + 1] << 16) | (m[j + 2] << 8) | 
m[j + 3];
-            wl[i] = (m[j + 4] << 24) | (m[j + 5] << 16) | (m[j + 6] << 8) | 
m[j + 7];
-        }
-        for (i = 0; i < 80; i++) {
-            bh0 = ah0;
-            bh1 = ah1;
-            bh2 = ah2;
-            bh3 = ah3;
-            bh4 = ah4;
-            bh5 = ah5;
-            bh6 = ah6;
-            bh7 = ah7;
-            bl0 = al0;
-            bl1 = al1;
-            bl2 = al2;
-            bl3 = al3;
-            bl4 = al4;
-            bl5 = al5;
-            bl6 = al6;
-            bl7 = al7;
-            // add
-            h = ah7;
-            l = al7;
-            a = l & 0xffff;
-            b = l >>> 16;
-            c = h & 0xffff;
-            d = h >>> 16;
-            // Sigma1
-            h =
-                ((ah4 >>> 14) | (al4 << (32 - 14))) ^
-                    ((ah4 >>> 18) | (al4 << (32 - 18))) ^
-                    ((al4 >>> (41 - 32)) | (ah4 << (32 - (41 - 32))));
-            l =
-                ((al4 >>> 14) | (ah4 << (32 - 14))) ^
-                    ((al4 >>> 18) | (ah4 << (32 - 18))) ^
-                    ((ah4 >>> (41 - 32)) | (al4 << (32 - (41 - 32))));
-            a += l & 0xffff;
-            b += l >>> 16;
-            c += h & 0xffff;
-            d += h >>> 16;
-            // Ch
-            h = (ah4 & ah5) ^ (~ah4 & ah6);
-            l = (al4 & al5) ^ (~al4 & al6);
-            a += l & 0xffff;
-            b += l >>> 16;
-            c += h & 0xffff;
-            d += h >>> 16;
-            // K
-            h = K$1[i * 2];
-            l = K$1[i * 2 + 1];
-            a += l & 0xffff;
-            b += l >>> 16;
-            c += h & 0xffff;
-            d += h >>> 16;
-            // w
-            h = wh[i % 16];
-            l = wl[i % 16];
-            a += l & 0xffff;
-            b += l >>> 16;
-            c += h & 0xffff;
-            d += h >>> 16;
-            b += a >>> 16;
-            c += b >>> 16;
-            d += c >>> 16;
-            th = (c & 0xffff) | (d << 16);
-            tl = (a & 0xffff) | (b << 16);
-            // add
-            h = th;
-            l = tl;
-            a = l & 0xffff;
-            b = l >>> 16;
-            c = h & 0xffff;
-            d = h >>> 16;
-            // Sigma0
-            h =
-                ((ah0 >>> 28) | (al0 << (32 - 28))) ^
-                    ((al0 >>> (34 - 32)) | (ah0 << (32 - (34 - 32)))) ^
-                    ((al0 >>> (39 - 32)) | (ah0 << (32 - (39 - 32))));
-            l =
-                ((al0 >>> 28) | (ah0 << (32 - 28))) ^
-                    ((ah0 >>> (34 - 32)) | (al0 << (32 - (34 - 32)))) ^
-                    ((ah0 >>> (39 - 32)) | (al0 << (32 - (39 - 32))));
-            a += l & 0xffff;
-            b += l >>> 16;
-            c += h & 0xffff;
-            d += h >>> 16;
-            // Maj
-            h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2);
-            l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2);
-            a += l & 0xffff;
-            b += l >>> 16;
-            c += h & 0xffff;
-            d += h >>> 16;
-            b += a >>> 16;
-            c += b >>> 16;
-            d += c >>> 16;
-            bh7 = (c & 0xffff) | (d << 16);
-            bl7 = (a & 0xffff) | (b << 16);
-            // add
-            h = bh3;
-            l = bl3;
-            a = l & 0xffff;
-            b = l >>> 16;
-            c = h & 0xffff;
-            d = h >>> 16;
-            h = th;
-            l = tl;
-            a += l & 0xffff;
-            b += l >>> 16;
-            c += h & 0xffff;
-            d += h >>> 16;
-            b += a >>> 16;
-            c += b >>> 16;
-            d += c >>> 16;
-            bh3 = (c & 0xffff) | (d << 16);
-            bl3 = (a & 0xffff) | (b << 16);
-            ah1 = bh0;
-            ah2 = bh1;
-            ah3 = bh2;
-            ah4 = bh3;
-            ah5 = bh4;
-            ah6 = bh5;
-            ah7 = bh6;
-            ah0 = bh7;
-            al1 = bl0;
-            al2 = bl1;
-            al3 = bl2;
-            al4 = bl3;
-            al5 = bl4;
-            al6 = bl5;
-            al7 = bl6;
-            al0 = bl7;
-            if (i % 16 === 15) {
-                for (j = 0; j < 16; j++) {
-                    // add
-                    h = wh[j];
-                    l = wl[j];
-                    a = l & 0xffff;
-                    b = l >>> 16;
-                    c = h & 0xffff;
-                    d = h >>> 16;
-                    h = wh[(j + 9) % 16];
-                    l = wl[(j + 9) % 16];
-                    a += l & 0xffff;
-                    b += l >>> 16;
-                    c += h & 0xffff;
-                    d += h >>> 16;
-                    // sigma0
-                    th = wh[(j + 1) % 16];
-                    tl = wl[(j + 1) % 16];
-                    h =
-                        ((th >>> 1) | (tl << (32 - 1))) ^
-                            ((th >>> 8) | (tl << (32 - 8))) ^
-                            (th >>> 7);
-                    l =
-                        ((tl >>> 1) | (th << (32 - 1))) ^
-                            ((tl >>> 8) | (th << (32 - 8))) ^
-                            ((tl >>> 7) | (th << (32 - 7)));
-                    a += l & 0xffff;
-                    b += l >>> 16;
-                    c += h & 0xffff;
-                    d += h >>> 16;
-                    // sigma1
-                    th = wh[(j + 14) % 16];
-                    tl = wl[(j + 14) % 16];
-                    h =
-                        ((th >>> 19) | (tl << (32 - 19))) ^
-                            ((tl >>> (61 - 32)) | (th << (32 - (61 - 32)))) ^
-                            (th >>> 6);
-                    l =
-                        ((tl >>> 19) | (th << (32 - 19))) ^
-                            ((th >>> (61 - 32)) | (tl << (32 - (61 - 32)))) ^
-                            ((tl >>> 6) | (th << (32 - 6)));
-                    a += l & 0xffff;
-                    b += l >>> 16;
-                    c += h & 0xffff;
-                    d += h >>> 16;
-                    b += a >>> 16;
-                    c += b >>> 16;
-                    d += c >>> 16;
-                    wh[j] = (c & 0xffff) | (d << 16);
-                    wl[j] = (a & 0xffff) | (b << 16);
-                }
-            }
-        }
-        // add
-        h = ah0;
-        l = al0;
-        a = l & 0xffff;
-        b = l >>> 16;
-        c = h & 0xffff;
-        d = h >>> 16;
-        h = hh[0];
-        l = hl[0];
-        a += l & 0xffff;
-        b += l >>> 16;
-        c += h & 0xffff;
-        d += h >>> 16;
-        b += a >>> 16;
-        c += b >>> 16;
-        d += c >>> 16;
-        hh[0] = ah0 = (c & 0xffff) | (d << 16);
-        hl[0] = al0 = (a & 0xffff) | (b << 16);
-        h = ah1;
-        l = al1;
-        a = l & 0xffff;
-        b = l >>> 16;
-        c = h & 0xffff;
-        d = h >>> 16;
-        h = hh[1];
-        l = hl[1];
-        a += l & 0xffff;
-        b += l >>> 16;
-        c += h & 0xffff;
-        d += h >>> 16;
-        b += a >>> 16;
-        c += b >>> 16;
-        d += c >>> 16;
-        hh[1] = ah1 = (c & 0xffff) | (d << 16);
-        hl[1] = al1 = (a & 0xffff) | (b << 16);
-        h = ah2;
-        l = al2;
-        a = l & 0xffff;
-        b = l >>> 16;
-        c = h & 0xffff;
-        d = h >>> 16;
-        h = hh[2];
-        l = hl[2];
-        a += l & 0xffff;
-        b += l >>> 16;
-        c += h & 0xffff;
-        d += h >>> 16;
-        b += a >>> 16;
-        c += b >>> 16;
-        d += c >>> 16;
-        hh[2] = ah2 = (c & 0xffff) | (d << 16);
-        hl[2] = al2 = (a & 0xffff) | (b << 16);
-        h = ah3;
-        l = al3;
-        a = l & 0xffff;
-        b = l >>> 16;
-        c = h & 0xffff;
-        d = h >>> 16;
-        h = hh[3];
-        l = hl[3];
-        a += l & 0xffff;
-        b += l >>> 16;
-        c += h & 0xffff;
-        d += h >>> 16;
-        b += a >>> 16;
-        c += b >>> 16;
-        d += c >>> 16;
-        hh[3] = ah3 = (c & 0xffff) | (d << 16);
-        hl[3] = al3 = (a & 0xffff) | (b << 16);
-        h = ah4;
-        l = al4;
-        a = l & 0xffff;
-        b = l >>> 16;
-        c = h & 0xffff;
-        d = h >>> 16;
-        h = hh[4];
-        l = hl[4];
-        a += l & 0xffff;
-        b += l >>> 16;
-        c += h & 0xffff;
-        d += h >>> 16;
-        b += a >>> 16;
-        c += b >>> 16;
-        d += c >>> 16;
-        hh[4] = ah4 = (c & 0xffff) | (d << 16);
-        hl[4] = al4 = (a & 0xffff) | (b << 16);
-        h = ah5;
-        l = al5;
-        a = l & 0xffff;
-        b = l >>> 16;
-        c = h & 0xffff;
-        d = h >>> 16;
-        h = hh[5];
-        l = hl[5];
-        a += l & 0xffff;
-        b += l >>> 16;
-        c += h & 0xffff;
-        d += h >>> 16;
-        b += a >>> 16;
-        c += b >>> 16;
-        d += c >>> 16;
-        hh[5] = ah5 = (c & 0xffff) | (d << 16);
-        hl[5] = al5 = (a & 0xffff) | (b << 16);
-        h = ah6;
-        l = al6;
-        a = l & 0xffff;
-        b = l >>> 16;
-        c = h & 0xffff;
-        d = h >>> 16;
-        h = hh[6];
-        l = hl[6];
-        a += l & 0xffff;
-        b += l >>> 16;
-        c += h & 0xffff;
-        d += h >>> 16;
-        b += a >>> 16;
-        c += b >>> 16;
-        d += c >>> 16;
-        hh[6] = ah6 = (c & 0xffff) | (d << 16);
-        hl[6] = al6 = (a & 0xffff) | (b << 16);
-        h = ah7;
-        l = al7;
-        a = l & 0xffff;
-        b = l >>> 16;
-        c = h & 0xffff;
-        d = h >>> 16;
-        h = hh[7];
-        l = hl[7];
-        a += l & 0xffff;
-        b += l >>> 16;
-        c += h & 0xffff;
-        d += h >>> 16;
-        b += a >>> 16;
-        c += b >>> 16;
-        d += c >>> 16;
-        hh[7] = ah7 = (c & 0xffff) | (d << 16);
-        hl[7] = al7 = (a & 0xffff) | (b << 16);
-        pos += 128;
-        n -= 128;
-    }
-    return n;
-}
-function crypto_hash(out, m, n) {
-    const hh = new Int32Array(8);
-    const hl = new Int32Array(8);
-    const x = new Uint8Array(256);
-    const b = n;
-    hh[0] = 0x6a09e667;
-    hh[1] = 0xbb67ae85;
-    hh[2] = 0x3c6ef372;
-    hh[3] = 0xa54ff53a;
-    hh[4] = 0x510e527f;
-    hh[5] = 0x9b05688c;
-    hh[6] = 0x1f83d9ab;
-    hh[7] = 0x5be0cd19;
-    hl[0] = 0xf3bcc908;
-    hl[1] = 0x84caa73b;
-    hl[2] = 0xfe94f82b;
-    hl[3] = 0x5f1d36f1;
-    hl[4] = 0xade682d1;
-    hl[5] = 0x2b3e6c1f;
-    hl[6] = 0xfb41bd6b;
-    hl[7] = 0x137e2179;
-    crypto_hashblocks_hl(hh, hl, m, n);
-    n %= 128;
-    for (let i = 0; i < n; i++)
-        x[i] = m[b - n + i];
-    x[n] = 128;
-    n = 256 - 128 * (n < 112 ? 1 : 0);
-    x[n - 9] = 0;
-    ts64(x, n - 8, (b / 0x20000000) | 0, b << 3);
-    crypto_hashblocks_hl(hh, hl, x, n);
-    for (let i = 0; i < 8; i++)
-        ts64(out, 8 * i, hh[i], hl[i]);
-    return 0;
-}
-/**
- * Incremental version of crypto_hash.
- */
-class HashState {
-    constructor() {
-        this.hh = new Int32Array(8);
-        this.hl = new Int32Array(8);
-        this.next = new Uint8Array(128);
-        this.p = 0;
-        this.total = 0;
-        this.hh[0] = 0x6a09e667;
-        this.hh[1] = 0xbb67ae85;
-        this.hh[2] = 0x3c6ef372;
-        this.hh[3] = 0xa54ff53a;
-        this.hh[4] = 0x510e527f;
-        this.hh[5] = 0x9b05688c;
-        this.hh[6] = 0x1f83d9ab;
-        this.hh[7] = 0x5be0cd19;
-        this.hl[0] = 0xf3bcc908;
-        this.hl[1] = 0x84caa73b;
-        this.hl[2] = 0xfe94f82b;
-        this.hl[3] = 0x5f1d36f1;
-        this.hl[4] = 0xade682d1;
-        this.hl[5] = 0x2b3e6c1f;
-        this.hl[6] = 0xfb41bd6b;
-        this.hl[7] = 0x137e2179;
-    }
-    update(data) {
-        this.total += data.length;
-        let i = 0;
-        while (i < data.length) {
-            const r = 128 - this.p;
-            if (r > data.length - i) {
-                for (let j = 0; i + j < data.length; j++) {
-                    this.next[this.p + j] = data[i + j];
-                }
-                this.p += data.length - i;
-                break;
-            }
-            else {
-                for (let j = 0; this.p + j < 128; j++) {
-                    this.next[this.p + j] = data[i + j];
-                }
-                crypto_hashblocks_hl(this.hh, this.hl, this.next, 128);
-                i += 128 - this.p;
-                this.p = 0;
-            }
-        }
-        return this;
-    }
-    finish() {
-        const out = new Uint8Array(64);
-        let n = this.p;
-        const x = new Uint8Array(256);
-        const b = this.total;
-        for (let i = 0; i < n; i++)
-            x[i] = this.next[i];
-        x[n] = 128;
-        n = 256 - 128 * (n < 112 ? 1 : 0);
-        x[n - 9] = 0;
-        ts64(x, n - 8, (b / 0x20000000) | 0, b << 3);
-        crypto_hashblocks_hl(this.hh, this.hl, x, n);
-        for (let i = 0; i < 8; i++)
-            ts64(out, 8 * i, this.hh[i], this.hl[i]);
-        return out;
-    }
-}
-function add(p, q) {
-    const a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf(), g = 
gf(), h = gf(), t = gf();
-    Z(a, p[1], p[0]);
-    Z(t, q[1], q[0]);
-    M(a, a, t);
-    A(b, p[0], p[1]);
-    A(t, q[0], q[1]);
-    M(b, b, t);
-    M(c, p[3], q[3]);
-    M(c, c, D2);
-    M(d, p[2], q[2]);
-    A(d, d, d);
-    Z(e, b, a);
-    Z(f, d, c);
-    A(g, d, c);
-    A(h, b, a);
-    M(p[0], e, f);
-    M(p[1], h, g);
-    M(p[2], g, f);
-    M(p[3], e, h);
-}
-function cswap(p, q, b) {
-    let i;
-    for (i = 0; i < 4; i++) {
-        sel25519(p[i], q[i], b);
-    }
-}
-function pack(r, p) {
-    const tx = gf(), ty = gf(), zi = gf();
-    inv25519(zi, p[2]);
-    M(tx, p[0], zi);
-    M(ty, p[1], zi);
-    pack25519(r, ty);
-    r[31] ^= par25519(tx) << 7;
-}
-function scalarmult(p, q, s) {
-    let b, i;
-    set25519(p[0], gf0);
-    set25519(p[1], gf1);
-    set25519(p[2], gf1);
-    set25519(p[3], gf0);
-    for (i = 255; i >= 0; --i) {
-        b = (s[(i / 8) | 0] >> (i & 7)) & 1;
-        cswap(p, q, b);
-        add(q, p);
-        add(p, p);
-        cswap(p, q, b);
-    }
-}
-function scalarbase(p, s) {
-    const q = [gf(), gf(), gf(), gf()];
-    set25519(q[0], X);
-    set25519(q[1], Y);
-    set25519(q[2], gf1);
-    M(q[3], X, Y);
-    scalarmult(p, q, s);
-}
-function crypto_sign_keypair(pk, sk, seeded) {
-    const d = new Uint8Array(64);
-    const p = [gf(), gf(), gf(), gf()];
-    if (!seeded)
-        randombytes(sk, 32);
-    crypto_hash(d, sk, 32);
-    d[0] &= 248;
-    d[31] &= 127;
-    d[31] |= 64;
-    scalarbase(p, d);
-    pack(pk, p);
-    for (let i = 0; i < 32; i++)
-        sk[i + 32] = pk[i];
-    return 0;
-}
-const L = new Float64Array([
-    0xed,
-    0xd3,
-    0xf5,
-    0x5c,
-    0x1a,
-    0x63,
-    0x12,
-    0x58,
-    0xd6,
-    0x9c,
-    0xf7,
-    0xa2,
-    0xde,
-    0xf9,
-    0xde,
-    0x14,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0,
-    0x10,
-]);
-function modL(r, x) {
-    let carry, i, j, k;
-    for (i = 63; i >= 32; --i) {
-        carry = 0;
-        for (j = i - 32, k = i - 12; j < k; ++j) {
-            x[j] += carry - 16 * x[i] * L[j - (i - 32)];
-            carry = Math.floor((x[j] + 128) / 256);
-            x[j] -= carry * 256;
-        }
-        x[j] += carry;
-        x[i] = 0;
-    }
-    carry = 0;
-    for (j = 0; j < 32; j++) {
-        x[j] += carry - (x[31] >> 4) * L[j];
-        carry = x[j] >> 8;
-        x[j] &= 255;
-    }
-    for (j = 0; j < 32; j++)
-        x[j] -= carry * L[j];
-    for (i = 0; i < 32; i++) {
-        x[i + 1] += x[i] >> 8;
-        r[i] = x[i] & 255;
-    }
-}
-function reduce(r) {
-    const x = new Float64Array(64);
-    for (let i = 0; i < 64; i++)
-        x[i] = r[i];
-    for (let i = 0; i < 64; i++)
-        r[i] = 0;
-    modL(r, x);
-}
-// Note: difference from C - smlen returned, not passed as argument.
-function crypto_sign(sm, m, n, sk) {
-    const d = new Uint8Array(64), h = new Uint8Array(64), r = new 
Uint8Array(64);
-    let i, j;
-    const x = new Float64Array(64);
-    const p = [gf(), gf(), gf(), gf()];
-    crypto_hash(d, sk, 32);
-    d[0] &= 248;
-    d[31] &= 127;
-    d[31] |= 64;
-    const smlen = n + 64;
-    for (i = 0; i < n; i++)
-        sm[64 + i] = m[i];
-    for (i = 0; i < 32; i++)
-        sm[32 + i] = d[32 + i];
-    crypto_hash(r, sm.subarray(32), n + 32);
-    reduce(r);
-    scalarbase(p, r);
-    pack(sm, p);
-    for (i = 32; i < 64; i++)
-        sm[i] = sk[i];
-    crypto_hash(h, sm, n + 64);
-    reduce(h);
-    for (i = 0; i < 64; i++)
-        x[i] = 0;
-    for (i = 0; i < 32; i++)
-        x[i] = r[i];
-    for (i = 0; i < 32; i++) {
-        for (j = 0; j < 32; j++) {
-            x[i + j] += h[i] * d[j];
-        }
-    }
-    modL(sm.subarray(32), x);
-    return smlen;
-}
-function unpackneg(r, p) {
-    const t = gf();
-    const chk = gf();
-    const num = gf();
-    const den = gf();
-    const den2 = gf();
-    const den4 = gf();
-    const den6 = gf();
-    set25519(r[2], gf1);
-    unpack25519(r[1], p);
-    S(num, r[1]);
-    M(den, num, D);
-    Z(num, num, r[2]);
-    A(den, r[2], den);
-    S(den2, den);
-    S(den4, den2);
-    M(den6, den4, den2);
-    M(t, den6, num);
-    M(t, t, den);
-    pow2523(t, t);
-    M(t, t, num);
-    M(t, t, den);
-    M(t, t, den);
-    M(r[0], t, den);
-    S(chk, r[0]);
-    M(chk, chk, den);
-    if (neq25519(chk, num))
-        M(r[0], r[0], I);
-    S(chk, r[0]);
-    M(chk, chk, den);
-    if (neq25519(chk, num))
-        return -1;
-    if (par25519(r[0]) === p[31] >> 7)
-        Z(r[0], gf0, r[0]);
-    M(r[3], r[0], r[1]);
-    return 0;
-}
-function crypto_sign_open(m, sm, n, pk) {
-    let i, mlen;
-    const t = new Uint8Array(32), h = new Uint8Array(64);
-    const p = [gf(), gf(), gf(), gf()], q = [gf(), gf(), gf(), gf()];
-    mlen = -1;
-    if (n < 64)
-        return -1;
-    if (unpackneg(q, pk))
-        return -1;
-    for (i = 0; i < n; i++)
-        m[i] = sm[i];
-    for (i = 0; i < 32; i++)
-        m[i + 32] = pk[i];
-    crypto_hash(h, m, n);
-    reduce(h);
-    scalarmult(p, q, h);
-    scalarbase(q, sm.subarray(32));
-    add(p, q);
-    pack(t, p);
-    n -= 64;
-    if (crypto_verify_32(sm, 0, t, 0)) {
-        for (i = 0; i < n; i++)
-            m[i] = 0;
-        return -1;
-    }
-    for (i = 0; i < n; i++)
-        m[i] = sm[i + 64];
-    mlen = n;
-    return mlen;
-}
-const crypto_secretbox_KEYBYTES = 32;
-const crypto_secretbox_NONCEBYTES = 24;
-const crypto_secretbox_ZEROBYTES = 32;
-const crypto_secretbox_BOXZEROBYTES = 16;
-const crypto_scalarmult_BYTES = 32;
-const crypto_scalarmult_SCALARBYTES = 32;
-const crypto_sign_BYTES = 64;
-const crypto_sign_PUBLICKEYBYTES = 32;
-const crypto_sign_SECRETKEYBYTES = 64;
-const crypto_sign_SEEDBYTES = 32;
-const crypto_hash_BYTES = 64;
-/* High-level API */
-function checkLengths(k, n) {
-    if (k.length !== crypto_secretbox_KEYBYTES)
-        throw new Error("bad key size");
-    if (n.length !== crypto_secretbox_NONCEBYTES)
-        throw new Error("bad nonce size");
-}
-function checkArrayTypes(...args) {
-    for (let i = 0; i < args.length; i++) {
-        if (!(args[i] instanceof Uint8Array))
-            throw new TypeError("unexpected type, use Uint8Array");
-    }
-}
-function randomBytes(n) {
-    const b = new Uint8Array(n);
-    randombytes(b, n);
-    return b;
-}
-function scalarMult(n, p) {
-    checkArrayTypes(n, p);
-    if (n.length !== crypto_scalarmult_SCALARBYTES)
-        throw new Error("bad n size");
-    if (p.length !== crypto_scalarmult_BYTES)
-        throw new Error("bad p size");
-    const q = new Uint8Array(crypto_scalarmult_BYTES);
-    crypto_scalarmult(q, n, p);
-    return q;
-}
-function scalarMult_base(n) {
-    checkArrayTypes(n);
-    if (n.length !== crypto_scalarmult_SCALARBYTES)
-        throw new Error("bad n size");
-    const q = new Uint8Array(crypto_scalarmult_BYTES);
-    crypto_scalarmult_base(q, n);
-    return q;
-}
-function sign(msg, secretKey) {
-    checkArrayTypes(msg, secretKey);
-    if (secretKey.length !== crypto_sign_SECRETKEYBYTES)
-        throw new Error("bad secret key size");
-    const signedMsg = new Uint8Array(crypto_sign_BYTES + msg.length);
-    crypto_sign(signedMsg, msg, msg.length, secretKey);
-    return signedMsg;
-}
-function sign_detached(msg, secretKey) {
-    const signedMsg = sign(msg, secretKey);
-    const sig = new Uint8Array(crypto_sign_BYTES);
-    for (let i = 0; i < sig.length; i++)
-        sig[i] = signedMsg[i];
-    return sig;
-}
-function sign_detached_verify(msg, sig, publicKey) {
-    checkArrayTypes(msg, sig, publicKey);
-    if (sig.length !== crypto_sign_BYTES)
-        throw new Error("bad signature size");
-    if (publicKey.length !== crypto_sign_PUBLICKEYBYTES)
-        throw new Error("bad public key size");
-    const sm = new Uint8Array(crypto_sign_BYTES + msg.length);
-    const m = new Uint8Array(crypto_sign_BYTES + msg.length);
-    let i;
-    for (i = 0; i < crypto_sign_BYTES; i++)
-        sm[i] = sig[i];
-    for (i = 0; i < msg.length; i++)
-        sm[i + crypto_sign_BYTES] = msg[i];
-    return crypto_sign_open(m, sm, sm.length, publicKey) >= 0;
-}
-function sign_keyPair_fromSeed(seed) {
-    checkArrayTypes(seed);
-    if (seed.length !== crypto_sign_SEEDBYTES)
-        throw new Error("bad seed size");
-    const pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
-    const sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
-    for (let i = 0; i < 32; i++)
-        sk[i] = seed[i];
-    crypto_sign_keypair(pk, sk, true);
-    return { publicKey: pk, secretKey: sk };
-}
-function hash$1(msg) {
-    checkArrayTypes(msg);
-    const h = new Uint8Array(crypto_hash_BYTES);
-    crypto_hash(h, msg, msg.length);
-    return h;
-}
-function setPRNG(fn) {
-    randombytes = fn;
-}
-function sign_ed25519_pk_to_curve25519(ed25519_pk) {
-    const ge_a = [gf(), gf(), gf(), gf()];
-    const x = gf();
-    const one_minus_y = gf();
-    const x25519_pk = new Uint8Array(32);
-    if (unpackneg(ge_a, ed25519_pk)) {
-        throw Error("invalid public key");
-    }
-    set25519(one_minus_y, gf1);
-    Z(one_minus_y, one_minus_y, ge_a[1]);
-    set25519(x, gf1);
-    A(x, x, ge_a[1]);
-    inv25519(one_minus_y, one_minus_y);
-    M(x, x, one_minus_y);
-    pack25519(x25519_pk, x);
-    return x25519_pk;
-}
-function secretbox(msg, nonce, key) {
-    checkArrayTypes(msg, nonce, key);
-    checkLengths(key, nonce);
-    var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length);
-    var c = new Uint8Array(m.length);
-    for (var i = 0; i < msg.length; i++)
-        m[i + crypto_secretbox_ZEROBYTES] = msg[i];
-    crypto_secretbox(c, m, m.length, nonce, key);
-    return c.subarray(crypto_secretbox_BOXZEROBYTES);
-}
-function secretbox_open(box, nonce, key) {
-    checkArrayTypes(box, nonce, key);
-    checkLengths(key, nonce);
-    var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length);
-    var m = new Uint8Array(c.length);
-    for (var i = 0; i < box.length; i++)
-        c[i + crypto_secretbox_BOXZEROBYTES] = box[i];
-    if (c.length < 32)
-        return undefined;
-    if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0)
-        return undefined;
-    return m.subarray(crypto_secretbox_ZEROBYTES);
-}
-
-var BigInteger = createCommonjsModule(function (module) {
-var bigInt = (function (undefined$1) {
-
-    var BASE = 1e7,
-        LOG_BASE = 7,
-        MAX_INT = 9007199254740992,
-        MAX_INT_ARR = smallToArray(MAX_INT),
-        DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
-
-    var supportsNativeBigInt = typeof BigInt === "function";
-
-    function Integer(v, radix, alphabet, caseSensitive) {
-        if (typeof v === "undefined") return Integer[0];
-        if (typeof radix !== "undefined") return +radix === 10 && !alphabet ? 
parseValue(v) : parseBase(v, radix, alphabet, caseSensitive);
-        return parseValue(v);
-    }
-
-    function BigInteger(value, sign) {
-        this.value = value;
-        this.sign = sign;
-        this.isSmall = false;
-    }
-    BigInteger.prototype = Object.create(Integer.prototype);
-
-    function SmallInteger(value) {
-        this.value = value;
-        this.sign = value < 0;
-        this.isSmall = true;
-    }
-    SmallInteger.prototype = Object.create(Integer.prototype);
-
-    function NativeBigInt(value) {
-        this.value = value;
-    }
-    NativeBigInt.prototype = Object.create(Integer.prototype);
-
-    function isPrecise(n) {
-        return -MAX_INT < n && n < MAX_INT;
-    }
-
-    function smallToArray(n) { // For performance reasons doesn't reference 
BASE, need to change this function if BASE changes
-        if (n < 1e7)
-            return [n];
-        if (n < 1e14)
-            return [n % 1e7, Math.floor(n / 1e7)];
-        return [n % 1e7, Math.floor(n / 1e7) % 1e7, Math.floor(n / 1e14)];
-    }
-
-    function arrayToSmall(arr) { // If BASE changes this function may need to 
change
-        trim(arr);
-        var length = arr.length;
-        if (length < 4 && compareAbs(arr, MAX_INT_ARR) < 0) {
-            switch (length) {
-                case 0: return 0;
-                case 1: return arr[0];
-                case 2: return arr[0] + arr[1] * BASE;
-                default: return arr[0] + (arr[1] + arr[2] * BASE) * BASE;
-            }
-        }
-        return arr;
-    }
-
-    function trim(v) {
-        var i = v.length;
-        while (v[--i] === 0);
-        v.length = i + 1;
-    }
-
-    function createArray(length) { // function shamelessly stolen from 
Yaffle's library https://github.com/Yaffle/BigInteger
-        var x = new Array(length);
-        var i = -1;
-        while (++i < length) {
-            x[i] = 0;
-        }
-        return x;
-    }
-
-    function truncate(n) {
-        if (n > 0) return Math.floor(n);
-        return Math.ceil(n);
-    }
-
-    function add(a, b) { // assumes a and b are arrays with a.length >= 
b.length
-        var l_a = a.length,
-            l_b = b.length,
-            r = new Array(l_a),
-            carry = 0,
-            base = BASE,
-            sum, i;
-        for (i = 0; i < l_b; i++) {
-            sum = a[i] + b[i] + carry;
-            carry = sum >= base ? 1 : 0;
-            r[i] = sum - carry * base;
-        }
-        while (i < l_a) {
-            sum = a[i] + carry;
-            carry = sum === base ? 1 : 0;
-            r[i++] = sum - carry * base;
-        }
-        if (carry > 0) r.push(carry);
-        return r;
-    }
-
-    function addAny(a, b) {
-        if (a.length >= b.length) return add(a, b);
-        return add(b, a);
-    }
-
-    function addSmall(a, carry) { // assumes a is array, carry is number with 
0 <= carry < MAX_INT
-        var l = a.length,
-            r = new Array(l),
-            base = BASE,
-            sum, i;
-        for (i = 0; i < l; i++) {
-            sum = a[i] - base + carry;
-            carry = Math.floor(sum / base);
-            r[i] = sum - carry * base;
-            carry += 1;
-        }
-        while (carry > 0) {
-            r[i++] = carry % base;
-            carry = Math.floor(carry / base);
-        }
-        return r;
-    }
-
-    BigInteger.prototype.add = function (v) {
-        var n = parseValue(v);
-        if (this.sign !== n.sign) {
-            return this.subtract(n.negate());
-        }
-        var a = this.value, b = n.value;
-        if (n.isSmall) {
-            return new BigInteger(addSmall(a, Math.abs(b)), this.sign);
-        }
-        return new BigInteger(addAny(a, b), this.sign);
-    };
-    BigInteger.prototype.plus = BigInteger.prototype.add;
-
-    SmallInteger.prototype.add = function (v) {
-        var n = parseValue(v);
-        var a = this.value;
-        if (a < 0 !== n.sign) {
-            return this.subtract(n.negate());
-        }
-        var b = n.value;
-        if (n.isSmall) {
-            if (isPrecise(a + b)) return new SmallInteger(a + b);
-            b = smallToArray(Math.abs(b));
-        }
-        return new BigInteger(addSmall(b, Math.abs(a)), a < 0);
-    };
-    SmallInteger.prototype.plus = SmallInteger.prototype.add;
-
-    NativeBigInt.prototype.add = function (v) {
-        return new NativeBigInt(this.value + parseValue(v).value);
-    };
-    NativeBigInt.prototype.plus = NativeBigInt.prototype.add;
-
-    function subtract(a, b) { // assumes a and b are arrays with a >= b
-        var a_l = a.length,
-            b_l = b.length,
-            r = new Array(a_l),
-            borrow = 0,
-            base = BASE,
-            i, difference;
-        for (i = 0; i < b_l; i++) {
-            difference = a[i] - borrow - b[i];
-            if (difference < 0) {
-                difference += base;
-                borrow = 1;
-            } else borrow = 0;
-            r[i] = difference;
-        }
-        for (i = b_l; i < a_l; i++) {
-            difference = a[i] - borrow;
-            if (difference < 0) difference += base;
-            else {
-                r[i++] = difference;
-                break;
-            }
-            r[i] = difference;
-        }
-        for (; i < a_l; i++) {
-            r[i] = a[i];
-        }
-        trim(r);
-        return r;
-    }
-
-    function subtractAny(a, b, sign) {
-        var value;
-        if (compareAbs(a, b) >= 0) {
-            value = subtract(a, b);
-        } else {
-            value = subtract(b, a);
-            sign = !sign;
-        }
-        value = arrayToSmall(value);
-        if (typeof value === "number") {
-            if (sign) value = -value;
-            return new SmallInteger(value);
-        }
-        return new BigInteger(value, sign);
-    }
-
-    function subtractSmall(a, b, sign) { // assumes a is array, b is number 
with 0 <= b < MAX_INT
-        var l = a.length,
-            r = new Array(l),
-            carry = -b,
-            base = BASE,
-            i, difference;
-        for (i = 0; i < l; i++) {
-            difference = a[i] + carry;
-            carry = Math.floor(difference / base);
-            difference %= base;
-            r[i] = difference < 0 ? difference + base : difference;
-        }
-        r = arrayToSmall(r);
-        if (typeof r === "number") {
-            if (sign) r = -r;
-            return new SmallInteger(r);
-        } return new BigInteger(r, sign);
-    }
-
-    BigInteger.prototype.subtract = function (v) {
-        var n = parseValue(v);
-        if (this.sign !== n.sign) {
-            return this.add(n.negate());
-        }
-        var a = this.value, b = n.value;
-        if (n.isSmall)
-            return subtractSmall(a, Math.abs(b), this.sign);
-        return subtractAny(a, b, this.sign);
-    };
-    BigInteger.prototype.minus = BigInteger.prototype.subtract;
-
-    SmallInteger.prototype.subtract = function (v) {
-        var n = parseValue(v);
-        var a = this.value;
-        if (a < 0 !== n.sign) {
-            return this.add(n.negate());
-        }
-        var b = n.value;
-        if (n.isSmall) {
-            return new SmallInteger(a - b);
-        }
-        return subtractSmall(b, Math.abs(a), a >= 0);
-    };
-    SmallInteger.prototype.minus = SmallInteger.prototype.subtract;
-
-    NativeBigInt.prototype.subtract = function (v) {
-        return new NativeBigInt(this.value - parseValue(v).value);
-    };
-    NativeBigInt.prototype.minus = NativeBigInt.prototype.subtract;
-
-    BigInteger.prototype.negate = function () {
-        return new BigInteger(this.value, !this.sign);
-    };
-    SmallInteger.prototype.negate = function () {
-        var sign = this.sign;
-        var small = new SmallInteger(-this.value);
-        small.sign = !sign;
-        return small;
-    };
-    NativeBigInt.prototype.negate = function () {
-        return new NativeBigInt(-this.value);
-    };
-
-    BigInteger.prototype.abs = function () {
-        return new BigInteger(this.value, false);
-    };
-    SmallInteger.prototype.abs = function () {
-        return new SmallInteger(Math.abs(this.value));
-    };
-    NativeBigInt.prototype.abs = function () {
-        return new NativeBigInt(this.value >= 0 ? this.value : -this.value);
-    };
-
-
-    function multiplyLong(a, b) {
-        var a_l = a.length,
-            b_l = b.length,
-            l = a_l + b_l,
-            r = createArray(l),
-            base = BASE,
-            product, carry, i, a_i, b_j;
-        for (i = 0; i < a_l; ++i) {
-            a_i = a[i];
-            for (var j = 0; j < b_l; ++j) {
-                b_j = b[j];
-                product = a_i * b_j + r[i + j];
-                carry = Math.floor(product / base);
-                r[i + j] = product - carry * base;
-                r[i + j + 1] += carry;
-            }
-        }
-        trim(r);
-        return r;
-    }
-
-    function multiplySmall(a, b) { // assumes a is array, b is number with |b| 
< BASE
-        var l = a.length,
-            r = new Array(l),
-            base = BASE,
-            carry = 0,
-            product, i;
-        for (i = 0; i < l; i++) {
-            product = a[i] * b + carry;
-            carry = Math.floor(product / base);
-            r[i] = product - carry * base;
-        }
-        while (carry > 0) {
-            r[i++] = carry % base;
-            carry = Math.floor(carry / base);
-        }
-        return r;
-    }
-
-    function shiftLeft(x, n) {
-        var r = [];
-        while (n-- > 0) r.push(0);
-        return r.concat(x);
-    }
-
-    function multiplyKaratsuba(x, y) {
-        var n = Math.max(x.length, y.length);
-
-        if (n <= 30) return multiplyLong(x, y);
-        n = Math.ceil(n / 2);
-
-        var b = x.slice(n),
-            a = x.slice(0, n),
-            d = y.slice(n),
-            c = y.slice(0, n);
-
-        var ac = multiplyKaratsuba(a, c),
-            bd = multiplyKaratsuba(b, d),
-            abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d));
-
-        var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), 
bd), n)), shiftLeft(bd, 2 * n));
-        trim(product);
-        return product;
-    }
-
-    // The following function is derived from a surface fit of a graph 
plotting the performance difference
-    // between long multiplication and karatsuba multiplication versus the 
lengths of the two arrays.
-    function useKaratsuba(l1, l2) {
-        return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0;
-    }
-
-    BigInteger.prototype.multiply = function (v) {
-        var n = parseValue(v),
-            a = this.value, b = n.value,
-            sign = this.sign !== n.sign,
-            abs;
-        if (n.isSmall) {
-            if (b === 0) return Integer[0];
-            if (b === 1) return this;
-            if (b === -1) return this.negate();
-            abs = Math.abs(b);
-            if (abs < BASE) {
-                return new BigInteger(multiplySmall(a, abs), sign);
-            }
-            b = smallToArray(abs);
-        }
-        if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for 
certain array sizes
-            return new BigInteger(multiplyKaratsuba(a, b), sign);
-        return new BigInteger(multiplyLong(a, b), sign);
-    };
-
-    BigInteger.prototype.times = BigInteger.prototype.multiply;
-
-    function multiplySmallAndArray(a, b, sign) { // a >= 0
-        if (a < BASE) {
-            return new BigInteger(multiplySmall(b, a), sign);
-        }
-        return new BigInteger(multiplyLong(b, smallToArray(a)), sign);
-    }
-    SmallInteger.prototype._multiplyBySmall = function (a) {
-        if (isPrecise(a.value * this.value)) {
-            return new SmallInteger(a.value * this.value);
-        }
-        return multiplySmallAndArray(Math.abs(a.value), 
smallToArray(Math.abs(this.value)), this.sign !== a.sign);
-    };
-    BigInteger.prototype._multiplyBySmall = function (a) {
-        if (a.value === 0) return Integer[0];
-        if (a.value === 1) return this;
-        if (a.value === -1) return this.negate();
-        return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign 
!== a.sign);
-    };
-    SmallInteger.prototype.multiply = function (v) {
-        return parseValue(v)._multiplyBySmall(this);
-    };
-    SmallInteger.prototype.times = SmallInteger.prototype.multiply;
-
-    NativeBigInt.prototype.multiply = function (v) {
-        return new NativeBigInt(this.value * parseValue(v).value);
-    };
-    NativeBigInt.prototype.times = NativeBigInt.prototype.multiply;
-
-    function square(a) {
-        //console.assert(2 * BASE * BASE < MAX_INT);
-        var l = a.length,
-            r = createArray(l + l),
-            base = BASE,
-            product, carry, i, a_i, a_j;
-        for (i = 0; i < l; i++) {
-            a_i = a[i];
-            carry = 0 - a_i * a_i;
-            for (var j = i; j < l; j++) {
-                a_j = a[j];
-                product = 2 * (a_i * a_j) + r[i + j] + carry;
-                carry = Math.floor(product / base);
-                r[i + j] = product - carry * base;
-            }
-            r[i + l] = carry;
-        }
-        trim(r);
-        return r;
-    }
-
-    BigInteger.prototype.square = function () {
-        return new BigInteger(square(this.value), false);
-    };
-
-    SmallInteger.prototype.square = function () {
-        var value = this.value * this.value;
-        if (isPrecise(value)) return new SmallInteger(value);
-        return new BigInteger(square(smallToArray(Math.abs(this.value))), 
false);
-    };
-
-    NativeBigInt.prototype.square = function (v) {
-        return new NativeBigInt(this.value * this.value);
-    };
-
-    function divMod1(a, b) { // Left over from previous version. Performs 
faster than divMod2 on smaller input sizes.
-        var a_l = a.length,
-            b_l = b.length,
-            base = BASE,
-            result = createArray(b.length),
-            divisorMostSignificantDigit = b[b_l - 1],
-            // normalization
-            lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)),
-            remainder = multiplySmall(a, lambda),
-            divisor = multiplySmall(b, lambda),
-            quotientDigit, shift, carry, borrow, i, l, q;
-        if (remainder.length <= a_l) remainder.push(0);
-        divisor.push(0);
-        divisorMostSignificantDigit = divisor[b_l - 1];
-        for (shift = a_l - b_l; shift >= 0; shift--) {
-            quotientDigit = base - 1;
-            if (remainder[shift + b_l] !== divisorMostSignificantDigit) {
-                quotientDigit = Math.floor((remainder[shift + b_l] * base + 
remainder[shift + b_l - 1]) / divisorMostSignificantDigit);
-            }
-            // quotientDigit <= base - 1
-            carry = 0;
-            borrow = 0;
-            l = divisor.length;
-            for (i = 0; i < l; i++) {
-                carry += quotientDigit * divisor[i];
-                q = Math.floor(carry / base);
-                borrow += remainder[shift + i] - (carry - q * base);
-                carry = q;
-                if (borrow < 0) {
-                    remainder[shift + i] = borrow + base;
-                    borrow = -1;
-                } else {
-                    remainder[shift + i] = borrow;
-                    borrow = 0;
-                }
-            }
-            while (borrow !== 0) {
-                quotientDigit -= 1;
-                carry = 0;
-                for (i = 0; i < l; i++) {
-                    carry += remainder[shift + i] - base + divisor[i];
-                    if (carry < 0) {
-                        remainder[shift + i] = carry + base;
-                        carry = 0;
-                    } else {
-                        remainder[shift + i] = carry;
-                        carry = 1;
-                    }
-                }
-                borrow += carry;
-            }
-            result[shift] = quotientDigit;
-        }
-        // denormalization
-        remainder = divModSmall(remainder, lambda)[0];
-        return [arrayToSmall(result), arrayToSmall(remainder)];
-    }
-
-    function divMod2(a, b) { // Implementation idea shamelessly stolen from 
Silent Matt's library http://silentmatt.com/biginteger/
-        // Performs faster than divMod1 on larger input sizes.
-        var a_l = a.length,
-            b_l = b.length,
-            result = [],
-            part = [],
-            base = BASE,
-            guess, xlen, highx, highy, check;
-        while (a_l) {
-            part.unshift(a[--a_l]);
-            trim(part);
-            if (compareAbs(part, b) < 0) {
-                result.push(0);
-                continue;
-            }
-            xlen = part.length;
-            highx = part[xlen - 1] * base + part[xlen - 2];
-            highy = b[b_l - 1] * base + b[b_l - 2];
-            if (xlen > b_l) {
-                highx = (highx + 1) * base;
-            }
-            guess = Math.ceil(highx / highy);
-            do {
-                check = multiplySmall(b, guess);
-                if (compareAbs(check, part) <= 0) break;
-                guess--;
-            } while (guess);
-            result.push(guess);
-            part = subtract(part, check);
-        }
-        result.reverse();
-        return [arrayToSmall(result), arrayToSmall(part)];
-    }
-
-    function divModSmall(value, lambda) {
-        var length = value.length,
-            quotient = createArray(length),
-            base = BASE,
-            i, q, remainder, divisor;
-        remainder = 0;
-        for (i = length - 1; i >= 0; --i) {
-            divisor = remainder * base + value[i];
-            q = truncate(divisor / lambda);
-            remainder = divisor - q * lambda;
-            quotient[i] = q | 0;
-        }
-        return [quotient, remainder | 0];
-    }
-
-    function divModAny(self, v) {
-        var value, n = parseValue(v);
-        if (supportsNativeBigInt) {
-            return [new NativeBigInt(self.value / n.value), new 
NativeBigInt(self.value % n.value)];
-        }
-        var a = self.value, b = n.value;
-        var quotient;
-        if (b === 0) throw new Error("Cannot divide by zero");
-        if (self.isSmall) {
-            if (n.isSmall) {
-                return [new SmallInteger(truncate(a / b)), new SmallInteger(a 
% b)];
-            }
-            return [Integer[0], self];
-        }
-        if (n.isSmall) {
-            if (b === 1) return [self, Integer[0]];
-            if (b == -1) return [self.negate(), Integer[0]];
-            var abs = Math.abs(b);
-            if (abs < BASE) {
-                value = divModSmall(a, abs);
-                quotient = arrayToSmall(value[0]);
-                var remainder = value[1];
-                if (self.sign) remainder = -remainder;
-                if (typeof quotient === "number") {
-                    if (self.sign !== n.sign) quotient = -quotient;
-                    return [new SmallInteger(quotient), new 
SmallInteger(remainder)];
-                }
-                return [new BigInteger(quotient, self.sign !== n.sign), new 
SmallInteger(remainder)];
-            }
-            b = smallToArray(abs);
-        }
-        var comparison = compareAbs(a, b);
-        if (comparison === -1) return [Integer[0], self];
-        if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], 
Integer[0]];
-
-        // divMod1 is faster on smaller input sizes
-        if (a.length + b.length <= 200)
-            value = divMod1(a, b);
-        else value = divMod2(a, b);
-
-        quotient = value[0];
-        var qSign = self.sign !== n.sign,
-            mod = value[1],
-            mSign = self.sign;
-        if (typeof quotient === "number") {
-            if (qSign) quotient = -quotient;
-            quotient = new SmallInteger(quotient);
-        } else quotient = new BigInteger(quotient, qSign);
-        if (typeof mod === "number") {
-            if (mSign) mod = -mod;
-            mod = new SmallInteger(mod);
-        } else mod = new BigInteger(mod, mSign);
-        return [quotient, mod];
-    }
-
-    BigInteger.prototype.divmod = function (v) {
-        var result = divModAny(this, v);
-        return {
-            quotient: result[0],
-            remainder: result[1]
-        };
-    };
-    NativeBigInt.prototype.divmod = SmallInteger.prototype.divmod = 
BigInteger.prototype.divmod;
-
-
-    BigInteger.prototype.divide = function (v) {
-        return divModAny(this, v)[0];
-    };
-    NativeBigInt.prototype.over = NativeBigInt.prototype.divide = function (v) 
{
-        return new NativeBigInt(this.value / parseValue(v).value);
-    };
-    SmallInteger.prototype.over = SmallInteger.prototype.divide = 
BigInteger.prototype.over = BigInteger.prototype.divide;
-
-    BigInteger.prototype.mod = function (v) {
-        return divModAny(this, v)[1];
-    };
-    NativeBigInt.prototype.mod = NativeBigInt.prototype.remainder = function 
(v) {
-        return new NativeBigInt(this.value % parseValue(v).value);
-    };
-    SmallInteger.prototype.remainder = SmallInteger.prototype.mod = 
BigInteger.prototype.remainder = BigInteger.prototype.mod;
-
-    BigInteger.prototype.pow = function (v) {
-        var n = parseValue(v),
-            a = this.value,
-            b = n.value,
-            value, x, y;
-        if (b === 0) return Integer[1];
-        if (a === 0) return Integer[0];
-        if (a === 1) return Integer[1];
-        if (a === -1) return n.isEven() ? Integer[1] : Integer[-1];
-        if (n.sign) {
-            return Integer[0];
-        }
-        if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is 
too large.");
-        if (this.isSmall) {
-            if (isPrecise(value = Math.pow(a, b)))
-                return new SmallInteger(truncate(value));
-        }
-        x = this;
-        y = Integer[1];
-        while (true) {
-            if (b & 1 === 1) {
-                y = y.times(x);
-                --b;
-            }
-            if (b === 0) break;
-            b /= 2;
-            x = x.square();
-        }
-        return y;
-    };
-    SmallInteger.prototype.pow = BigInteger.prototype.pow;
-
-    NativeBigInt.prototype.pow = function (v) {
-        var n = parseValue(v);
-        var a = this.value, b = n.value;
-        var _0 = BigInt(0), _1 = BigInt(1), _2 = BigInt(2);
-        if (b === _0) return Integer[1];
-        if (a === _0) return Integer[0];
-        if (a === _1) return Integer[1];
-        if (a === BigInt(-1)) return n.isEven() ? Integer[1] : Integer[-1];
-        if (n.isNegative()) return new NativeBigInt(_0);
-        var x = this;
-        var y = Integer[1];
-        while (true) {
-            if ((b & _1) === _1) {
-                y = y.times(x);
-                --b;
-            }
-            if (b === _0) break;
-            b /= _2;
-            x = x.square();
-        }
-        return y;
-    };
-
-    BigInteger.prototype.modPow = function (exp, mod) {
-        exp = parseValue(exp);
-        mod = parseValue(mod);
-        if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0");
-        var r = Integer[1],
-            base = this.mod(mod);
-        if (exp.isNegative()) {
-            exp = exp.multiply(Integer[-1]);
-            base = base.modInv(mod);
-        }
-        while (exp.isPositive()) {
-            if (base.isZero()) return Integer[0];
-            if (exp.isOdd()) r = r.multiply(base).mod(mod);
-            exp = exp.divide(2);
-            base = base.square().mod(mod);
-        }
-        return r;
-    };
-    NativeBigInt.prototype.modPow = SmallInteger.prototype.modPow = 
BigInteger.prototype.modPow;
-
-    function compareAbs(a, b) {
-        if (a.length !== b.length) {
-            return a.length > b.length ? 1 : -1;
-        }
-        for (var i = a.length - 1; i >= 0; i--) {
-            if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1;
-        }
-        return 0;
-    }
-
-    BigInteger.prototype.compareAbs = function (v) {
-        var n = parseValue(v),
-            a = this.value,
-            b = n.value;
-        if (n.isSmall) return 1;
-        return compareAbs(a, b);
-    };
-    SmallInteger.prototype.compareAbs = function (v) {
-        var n = parseValue(v),
-            a = Math.abs(this.value),
-            b = n.value;
-        if (n.isSmall) {
-            b = Math.abs(b);
-            return a === b ? 0 : a > b ? 1 : -1;
-        }
-        return -1;
-    };
-    NativeBigInt.prototype.compareAbs = function (v) {
-        var a = this.value;
-        var b = parseValue(v).value;
-        a = a >= 0 ? a : -a;
-        b = b >= 0 ? b : -b;
-        return a === b ? 0 : a > b ? 1 : -1;
-    };
-
-    BigInteger.prototype.compare = function (v) {
-        // See discussion about comparison with Infinity:
-        // https://github.com/peterolson/BigInteger.js/issues/61
-        if (v === Infinity) {
-            return -1;
-        }
-        if (v === -Infinity) {
-            return 1;
-        }
-
-        var n = parseValue(v),
-            a = this.value,
-            b = n.value;
-        if (this.sign !== n.sign) {
-            return n.sign ? 1 : -1;
-        }
-        if (n.isSmall) {
-            return this.sign ? -1 : 1;
-        }
-        return compareAbs(a, b) * (this.sign ? -1 : 1);
-    };
-    BigInteger.prototype.compareTo = BigInteger.prototype.compare;
-
-    SmallInteger.prototype.compare = function (v) {
-        if (v === Infinity) {
-            return -1;
-        }
-        if (v === -Infinity) {
-            return 1;
-        }
-
-        var n = parseValue(v),
-            a = this.value,
-            b = n.value;
-        if (n.isSmall) {
-            return a == b ? 0 : a > b ? 1 : -1;
-        }
-        if (a < 0 !== n.sign) {
-            return a < 0 ? -1 : 1;
-        }
-        return a < 0 ? 1 : -1;
-    };
-    SmallInteger.prototype.compareTo = SmallInteger.prototype.compare;
-
-    NativeBigInt.prototype.compare = function (v) {
-        if (v === Infinity) {
-            return -1;
-        }
-        if (v === -Infinity) {
-            return 1;
-        }
-        var a = this.value;
-        var b = parseValue(v).value;
-        return a === b ? 0 : a > b ? 1 : -1;
-    };
-    NativeBigInt.prototype.compareTo = NativeBigInt.prototype.compare;
-
-    BigInteger.prototype.equals = function (v) {
-        return this.compare(v) === 0;
-    };
-    NativeBigInt.prototype.eq = NativeBigInt.prototype.equals = 
SmallInteger.prototype.eq = SmallInteger.prototype.equals = 
BigInteger.prototype.eq = BigInteger.prototype.equals;
-
-    BigInteger.prototype.notEquals = function (v) {
-        return this.compare(v) !== 0;
-    };
-    NativeBigInt.prototype.neq = NativeBigInt.prototype.notEquals = 
SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = 
BigInteger.prototype.neq = BigInteger.prototype.notEquals;
-
-    BigInteger.prototype.greater = function (v) {
-        return this.compare(v) > 0;
-    };
-    NativeBigInt.prototype.gt = NativeBigInt.prototype.greater = 
SmallInteger.prototype.gt = SmallInteger.prototype.greater = 
BigInteger.prototype.gt = BigInteger.prototype.greater;
-
-    BigInteger.prototype.lesser = function (v) {
-        return this.compare(v) < 0;
-    };
-    NativeBigInt.prototype.lt = NativeBigInt.prototype.lesser = 
SmallInteger.prototype.lt = SmallInteger.prototype.lesser = 
BigInteger.prototype.lt = BigInteger.prototype.lesser;
-
-    BigInteger.prototype.greaterOrEquals = function (v) {
-        return this.compare(v) >= 0;
-    };
-    NativeBigInt.prototype.geq = NativeBigInt.prototype.greaterOrEquals = 
SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = 
BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals;
-
-    BigInteger.prototype.lesserOrEquals = function (v) {
-        return this.compare(v) <= 0;
-    };
-    NativeBigInt.prototype.leq = NativeBigInt.prototype.lesserOrEquals = 
SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = 
BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals;
-
-    BigInteger.prototype.isEven = function () {
-        return (this.value[0] & 1) === 0;
-    };
-    SmallInteger.prototype.isEven = function () {
-        return (this.value & 1) === 0;
-    };
-    NativeBigInt.prototype.isEven = function () {
-        return (this.value & BigInt(1)) === BigInt(0);
-    };
-
-    BigInteger.prototype.isOdd = function () {
-        return (this.value[0] & 1) === 1;
-    };
-    SmallInteger.prototype.isOdd = function () {
-        return (this.value & 1) === 1;
-    };
-    NativeBigInt.prototype.isOdd = function () {
-        return (this.value & BigInt(1)) === BigInt(1);
-    };
-
-    BigInteger.prototype.isPositive = function () {
-        return !this.sign;
-    };
-    SmallInteger.prototype.isPositive = function () {
-        return this.value > 0;
-    };
-    NativeBigInt.prototype.isPositive = SmallInteger.prototype.isPositive;
-
-    BigInteger.prototype.isNegative = function () {
-        return this.sign;
-    };
-    SmallInteger.prototype.isNegative = function () {
-        return this.value < 0;
-    };
-    NativeBigInt.prototype.isNegative = SmallInteger.prototype.isNegative;
-
-    BigInteger.prototype.isUnit = function () {
-        return false;
-    };
-    SmallInteger.prototype.isUnit = function () {
-        return Math.abs(this.value) === 1;
-    };
-    NativeBigInt.prototype.isUnit = function () {
-        return this.abs().value === BigInt(1);
-    };
-
-    BigInteger.prototype.isZero = function () {
-        return false;
-    };
-    SmallInteger.prototype.isZero = function () {
-        return this.value === 0;
-    };
-    NativeBigInt.prototype.isZero = function () {
-        return this.value === BigInt(0);
-    };
-
-    BigInteger.prototype.isDivisibleBy = function (v) {
-        var n = parseValue(v);
-        if (n.isZero()) return false;
-        if (n.isUnit()) return true;
-        if (n.compareAbs(2) === 0) return this.isEven();
-        return this.mod(n).isZero();
-    };
-    NativeBigInt.prototype.isDivisibleBy = 
SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy;
-
-    function isBasicPrime(v) {
-        var n = v.abs();
-        if (n.isUnit()) return false;
-        if (n.equals(2) || n.equals(3) || n.equals(5)) return true;
-        if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return 
false;
-        if (n.lesser(49)) return true;
-        // we don't know if it's prime: let the other functions figure it out
-    }
-
-    function millerRabinTest(n, a) {
-        var nPrev = n.prev(),
-            b = nPrev,
-            r = 0,
-            d, i, x;
-        while (b.isEven()) b = b.divide(2), r++;
-        next: for (i = 0; i < a.length; i++) {
-            if (n.lesser(a[i])) continue;
-            x = bigInt(a[i]).modPow(b, n);
-            if (x.isUnit() || x.equals(nPrev)) continue;
-            for (d = r - 1; d != 0; d--) {
-                x = x.square().mod(n);
-                if (x.isUnit()) return false;
-                if (x.equals(nPrev)) continue next;
-            }
-            return false;
-        }
-        return true;
-    }
-
-    // Set "strict" to true to force GRH-supported lower bound of 2*log(N)^2
-    BigInteger.prototype.isPrime = function (strict) {
-        var isPrime = isBasicPrime(this);
-        if (isPrime !== undefined$1) return isPrime;
-        var n = this.abs();
-        var bits = n.bitLength();
-        if (bits <= 64)
-            return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 
37]);
-        var logN = Math.log(2) * bits.toJSNumber();
-        var t = Math.ceil((strict === true) ? (2 * Math.pow(logN, 2)) : logN);
-        for (var a = [], i = 0; i < t; i++) {
-            a.push(bigInt(i + 2));
-        }
-        return millerRabinTest(n, a);
-    };
-    NativeBigInt.prototype.isPrime = SmallInteger.prototype.isPrime = 
BigInteger.prototype.isPrime;
-
-    BigInteger.prototype.isProbablePrime = function (iterations, rng) {
-        var isPrime = isBasicPrime(this);
-        if (isPrime !== undefined$1) return isPrime;
-        var n = this.abs();
-        var t = iterations === undefined$1 ? 5 : iterations;
-        for (var a = [], i = 0; i < t; i++) {
-            a.push(bigInt.randBetween(2, n.minus(2), rng));
-        }
-        return millerRabinTest(n, a);
-    };
-    NativeBigInt.prototype.isProbablePrime = 
SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime;
-
-    BigInteger.prototype.modInv = function (n) {
-        var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = 
this.abs(), q, lastT, lastR;
-        while (!newR.isZero()) {
-            q = r.divide(newR);
-            lastT = t;
-            lastR = r;
-            t = newT;
-            r = newR;
-            newT = lastT.subtract(q.multiply(newT));
-            newR = lastR.subtract(q.multiply(newR));
-        }
-        if (!r.isUnit()) throw new Error(this.toString() + " and " + 
n.toString() + " are not co-prime");
-        if (t.compare(0) === -1) {
-            t = t.add(n);
-        }
-        if (this.isNegative()) {
-            return t.negate();
-        }
-        return t;
-    };
-
-    NativeBigInt.prototype.modInv = SmallInteger.prototype.modInv = 
BigInteger.prototype.modInv;
-
-    BigInteger.prototype.next = function () {
-        var value = this.value;
-        if (this.sign) {
-            return subtractSmall(value, 1, this.sign);
-        }
-        return new BigInteger(addSmall(value, 1), this.sign);
-    };
-    SmallInteger.prototype.next = function () {
-        var value = this.value;
-        if (value + 1 < MAX_INT) return new SmallInteger(value + 1);
-        return new BigInteger(MAX_INT_ARR, false);
-    };
-    NativeBigInt.prototype.next = function () {
-        return new NativeBigInt(this.value + BigInt(1));
-    };
-
-    BigInteger.prototype.prev = function () {
-        var value = this.value;
-        if (this.sign) {
-            return new BigInteger(addSmall(value, 1), true);
-        }
-        return subtractSmall(value, 1, this.sign);
-    };
-    SmallInteger.prototype.prev = function () {
-        var value = this.value;
-        if (value - 1 > -MAX_INT) return new SmallInteger(value - 1);
-        return new BigInteger(MAX_INT_ARR, true);
-    };
-    NativeBigInt.prototype.prev = function () {
-        return new NativeBigInt(this.value - BigInt(1));
-    };
-
-    var powersOfTwo = [1];
-    while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 
* powersOfTwo[powersOfTwo.length - 1]);
-    var powers2Length = powersOfTwo.length, highestPower2 = 
powersOfTwo[powers2Length - 1];
-
-    function shift_isSmall(n) {
-        return Math.abs(n) <= BASE;
-    }
-
-    BigInteger.prototype.shiftLeft = function (v) {
-        var n = parseValue(v).toJSNumber();
-        if (!shift_isSmall(n)) {
-            throw new Error(String(n) + " is too large for shifting.");
-        }
-        if (n < 0) return this.shiftRight(-n);
-        var result = this;
-        if (result.isZero()) return result;
-        while (n >= powers2Length) {
-            result = result.multiply(highestPower2);
-            n -= powers2Length - 1;
-        }
-        return result.multiply(powersOfTwo[n]);
-    };
-    NativeBigInt.prototype.shiftLeft = SmallInteger.prototype.shiftLeft = 
BigInteger.prototype.shiftLeft;
-
-    BigInteger.prototype.shiftRight = function (v) {
-        var remQuo;
-        var n = parseValue(v).toJSNumber();
-        if (!shift_isSmall(n)) {
-            throw new Error(String(n) + " is too large for shifting.");
-        }
-        if (n < 0) return this.shiftLeft(-n);
-        var result = this;
-        while (n >= powers2Length) {
-            if (result.isZero() || (result.isNegative() && result.isUnit())) 
return result;
-            remQuo = divModAny(result, highestPower2);
-            result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0];
-            n -= powers2Length - 1;
-        }
-        remQuo = divModAny(result, powersOfTwo[n]);
-        return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0];
-    };
-    NativeBigInt.prototype.shiftRight = SmallInteger.prototype.shiftRight = 
BigInteger.prototype.shiftRight;
-
-    function bitwise(x, y, fn) {
-        y = parseValue(y);
-        var xSign = x.isNegative(), ySign = y.isNegative();
-        var xRem = xSign ? x.not() : x,
-            yRem = ySign ? y.not() : y;
-        var xDigit = 0, yDigit = 0;
-        var xDivMod = null, yDivMod = null;
-        var result = [];
-        while (!xRem.isZero() || !yRem.isZero()) {
-            xDivMod = divModAny(xRem, highestPower2);
-            xDigit = xDivMod[1].toJSNumber();
-            if (xSign) {
-                xDigit = highestPower2 - 1 - xDigit; // two's complement for 
negative numbers
-            }
-
-            yDivMod = divModAny(yRem, highestPower2);
-            yDigit = yDivMod[1].toJSNumber();
-            if (ySign) {
-                yDigit = highestPower2 - 1 - yDigit; // two's complement for 
negative numbers
-            }
-
-            xRem = xDivMod[0];
-            yRem = yDivMod[0];
-            result.push(fn(xDigit, yDigit));
-        }
-        var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : 
bigInt(0);
-        for (var i = result.length - 1; i >= 0; i -= 1) {
-            sum = sum.multiply(highestPower2).add(bigInt(result[i]));
-        }
-        return sum;
-    }
-
-    BigInteger.prototype.not = function () {
-        return this.negate().prev();
-    };
-    NativeBigInt.prototype.not = SmallInteger.prototype.not = 
BigInteger.prototype.not;
-
-    BigInteger.prototype.and = function (n) {
-        return bitwise(this, n, function (a, b) { return a & b; });
-    };
-    NativeBigInt.prototype.and = SmallInteger.prototype.and = 
BigInteger.prototype.and;
-
-    BigInteger.prototype.or = function (n) {
-        return bitwise(this, n, function (a, b) { return a | b; });
-    };
-    NativeBigInt.prototype.or = SmallInteger.prototype.or = 
BigInteger.prototype.or;
-
-    BigInteger.prototype.xor = function (n) {
-        return bitwise(this, n, function (a, b) { return a ^ b; });
-    };
-    NativeBigInt.prototype.xor = SmallInteger.prototype.xor = 
BigInteger.prototype.xor;
-
-    var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | 
LOBMASK_I;
-    function roughLOB(n) { // get lowestOneBit (rough)
-        // SmallInteger: return Min(lowestOneBit(n), 1 << 30)
-        // BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7]
-        var v = n.value,
-            x = typeof v === "number" ? v | LOBMASK_I :
-                typeof v === "bigint" ? v | BigInt(LOBMASK_I) :
-                    v[0] + v[1] * BASE | LOBMASK_BI;
-        return x & -x;
-    }
-
-    function integerLogarithm(value, base) {
-        if (base.compareTo(value) <= 0) {
-            var tmp = integerLogarithm(value, base.square(base));
-            var p = tmp.p;
-            var e = tmp.e;
-            var t = p.multiply(base);
-            return t.compareTo(value) <= 0 ? { p: t, e: e * 2 + 1 } : { p: p, 
e: e * 2 };
-        }
-        return { p: bigInt(1), e: 0 };
-    }
-
-    BigInteger.prototype.bitLength = function () {
-        var n = this;
-        if (n.compareTo(bigInt(0)) < 0) {
-            n = n.negate().subtract(bigInt(1));
-        }
-        if (n.compareTo(bigInt(0)) === 0) {
-            return bigInt(0);
-        }
-        return bigInt(integerLogarithm(n, bigInt(2)).e).add(bigInt(1));
-    };
-    NativeBigInt.prototype.bitLength = SmallInteger.prototype.bitLength = 
BigInteger.prototype.bitLength;
-
-    function max(a, b) {
-        a = parseValue(a);
-        b = parseValue(b);
-        return a.greater(b) ? a : b;
-    }
-    function min(a, b) {
-        a = parseValue(a);
-        b = parseValue(b);
-        return a.lesser(b) ? a : b;
-    }
-    function gcd(a, b) {
-        a = parseValue(a).abs();
-        b = parseValue(b).abs();
-        if (a.equals(b)) return a;
-        if (a.isZero()) return b;
-        if (b.isZero()) return a;
-        var c = Integer[1], d, t;
-        while (a.isEven() && b.isEven()) {
-            d = min(roughLOB(a), roughLOB(b));
-            a = a.divide(d);
-            b = b.divide(d);
-            c = c.multiply(d);
-        }
-        while (a.isEven()) {
-            a = a.divide(roughLOB(a));
-        }
-        do {
-            while (b.isEven()) {
-                b = b.divide(roughLOB(b));
-            }
-            if (a.greater(b)) {
-                t = b; b = a; a = t;
-            }
-            b = b.subtract(a);
-        } while (!b.isZero());
-        return c.isUnit() ? a : a.multiply(c);
-    }
-    function lcm(a, b) {
-        a = parseValue(a).abs();
-        b = parseValue(b).abs();
-        return a.divide(gcd(a, b)).multiply(b);
-    }
-    function randBetween(a, b, rng) {
-        a = parseValue(a);
-        b = parseValue(b);
-        var usedRNG = rng || Math.random;
-        var low = min(a, b), high = max(a, b);
-        var range = high.subtract(low).add(1);
-        if (range.isSmall) return low.add(Math.floor(usedRNG() * range));
-        var digits = toBase(range, BASE).value;
-        var result = [], restricted = true;
-        for (var i = 0; i < digits.length; i++) {
-            var top = restricted ? digits[i] : BASE;
-            var digit = truncate(usedRNG() * top);
-            result.push(digit);
-            if (digit < top) restricted = false;
-        }
-        return low.add(Integer.fromArray(result, BASE, false));
-    }
-
-    var parseBase = function (text, base, alphabet, caseSensitive) {
-        alphabet = alphabet || DEFAULT_ALPHABET;
-        text = String(text);
-        if (!caseSensitive) {
-            text = text.toLowerCase();
-            alphabet = alphabet.toLowerCase();
-        }
-        var length = text.length;
-        var i;
-        var absBase = Math.abs(base);
-        var alphabetValues = {};
-        for (i = 0; i < alphabet.length; i++) {
-            alphabetValues[alphabet[i]] = i;
-        }
-        for (i = 0; i < length; i++) {
-            var c = text[i];
-            if (c === "-") continue;
-            if (c in alphabetValues) {
-                if (alphabetValues[c] >= absBase) {
-                    if (c === "1" && absBase === 1) continue;
-                    throw new Error(c + " is not a valid digit in base " + 
base + ".");
-                }
-            }
-        }
-        base = parseValue(base);
-        var digits = [];
-        var isNegative = text[0] === "-";
-        for (i = isNegative ? 1 : 0; i < text.length; i++) {
-            var c = text[i];
-            if (c in alphabetValues) 
digits.push(parseValue(alphabetValues[c]));
-            else if (c === "<") {
-                var start = i;
-                do { i++; } while (text[i] !== ">" && i < text.length);
-                digits.push(parseValue(text.slice(start + 1, i)));
-            }
-            else throw new Error(c + " is not a valid character");
-        }
-        return parseBaseFromArray(digits, base, isNegative);
-    };
-
-    function parseBaseFromArray(digits, base, isNegative) {
-        var val = Integer[0], pow = Integer[1], i;
-        for (i = digits.length - 1; i >= 0; i--) {
-            val = val.add(digits[i].times(pow));
-            pow = pow.times(base);
-        }
-        return isNegative ? val.negate() : val;
-    }
-
-    function stringify(digit, alphabet) {
-        alphabet = alphabet || DEFAULT_ALPHABET;
-        if (digit < alphabet.length) {
-            return alphabet[digit];
-        }
-        return "<" + digit + ">";
-    }
-
-    function toBase(n, base) {
-        base = bigInt(base);
-        if (base.isZero()) {
-            if (n.isZero()) return { value: [0], isNegative: false };
-            throw new Error("Cannot convert nonzero numbers to base 0.");
-        }
-        if (base.equals(-1)) {
-            if (n.isZero()) return { value: [0], isNegative: false };
-            if (n.isNegative())
-                return {
-                    value: [].concat.apply([], Array.apply(null, 
Array(-n.toJSNumber()))
-                        .map(Array.prototype.valueOf, [1, 0])
-                    ),
-                    isNegative: false
-                };
-
-            var arr = Array.apply(null, Array(n.toJSNumber() - 1))
-                .map(Array.prototype.valueOf, [0, 1]);
-            arr.unshift([1]);
-            return {
-                value: [].concat.apply([], arr),
-                isNegative: false
-            };
-        }
-
-        var neg = false;
-        if (n.isNegative() && base.isPositive()) {
-            neg = true;
-            n = n.abs();
-        }
-        if (base.isUnit()) {
-            if (n.isZero()) return { value: [0], isNegative: false };
-
-            return {
-                value: Array.apply(null, Array(n.toJSNumber()))
-                    .map(Number.prototype.valueOf, 1),
-                isNegative: neg
-            };
-        }
-        var out = [];
-        var left = n, divmod;
-        while (left.isNegative() || left.compareAbs(base) >= 0) {
-            divmod = left.divmod(base);
-            left = divmod.quotient;
-            var digit = divmod.remainder;
-            if (digit.isNegative()) {
-                digit = base.minus(digit).abs();
-                left = left.next();
-            }
-            out.push(digit.toJSNumber());
-        }
-        out.push(left.toJSNumber());
-        return { value: out.reverse(), isNegative: neg };
-    }
-
-    function toBaseString(n, base, alphabet) {
-        var arr = toBase(n, base);
-        return (arr.isNegative ? "-" : "") + arr.value.map(function (x) {
-            return stringify(x, alphabet);
-        }).join('');
-    }
-
-    BigInteger.prototype.toArray = function (radix) {
-        return toBase(this, radix);
-    };
-
-    SmallInteger.prototype.toArray = function (radix) {
-        return toBase(this, radix);
-    };
-
-    NativeBigInt.prototype.toArray = function (radix) {
-        return toBase(this, radix);
-    };
-
-    BigInteger.prototype.toString = function (radix, alphabet) {
-        if (radix === undefined$1) radix = 10;
-        if (radix !== 10) return toBaseString(this, radix, alphabet);
-        var v = this.value, l = v.length, str = String(v[--l]), zeros = 
"0000000", digit;
-        while (--l >= 0) {
-            digit = String(v[l]);
-            str += zeros.slice(digit.length) + digit;
-        }
-        var sign = this.sign ? "-" : "";
-        return sign + str;
-    };
-
-    SmallInteger.prototype.toString = function (radix, alphabet) {
-        if (radix === undefined$1) radix = 10;
-        if (radix != 10) return toBaseString(this, radix, alphabet);
-        return String(this.value);
-    };
-
-    NativeBigInt.prototype.toString = SmallInteger.prototype.toString;
-
-    NativeBigInt.prototype.toJSON = BigInteger.prototype.toJSON = 
SmallInteger.prototype.toJSON = function () { return this.toString(); };
-
-    BigInteger.prototype.valueOf = function () {
-        return parseInt(this.toString(), 10);
-    };
-    BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf;
-
-    SmallInteger.prototype.valueOf = function () {
-        return this.value;
-    };
-    SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf;
-    NativeBigInt.prototype.valueOf = NativeBigInt.prototype.toJSNumber = 
function () {
-        return parseInt(this.toString(), 10);
-    };
-
-    function parseStringValue(v) {
-        if (isPrecise(+v)) {
-            var x = +v;
-            if (x === truncate(x))
-                return supportsNativeBigInt ? new NativeBigInt(BigInt(x)) : 
new SmallInteger(x);
-            throw new Error("Invalid integer: " + v);
-        }
-        var sign = v[0] === "-";
-        if (sign) v = v.slice(1);
-        var split = v.split(/e/i);
-        if (split.length > 2) throw new Error("Invalid integer: " + 
split.join("e"));
-        if (split.length === 2) {
-            var exp = split[1];
-            if (exp[0] === "+") exp = exp.slice(1);
-            exp = +exp;
-            if (exp !== truncate(exp) || !isPrecise(exp)) throw new 
Error("Invalid integer: " + exp + " is not a valid exponent.");
-            var text = split[0];
-            var decimalPlace = text.indexOf(".");
-            if (decimalPlace >= 0) {
-                exp -= text.length - decimalPlace - 1;
-                text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 
1);
-            }
-            if (exp < 0) throw new Error("Cannot include negative exponent 
part for integers");
-            text += (new Array(exp + 1)).join("0");
-            v = text;
-        }
-        var isValid = /^([0-9][0-9]*)$/.test(v);
-        if (!isValid) throw new Error("Invalid integer: " + v);
-        if (supportsNativeBigInt) {
-            return new NativeBigInt(BigInt(sign ? "-" + v : v));
-        }
-        var r = [], max = v.length, l = LOG_BASE, min = max - l;
-        while (max > 0) {
-            r.push(+v.slice(min, max));
-            min -= l;
-            if (min < 0) min = 0;
-            max -= l;
-        }
-        trim(r);
-        return new BigInteger(r, sign);
-    }
-
-    function parseNumberValue(v) {
-        if (supportsNativeBigInt) {
-            return new NativeBigInt(BigInt(v));
-        }
-        if (isPrecise(v)) {
-            if (v !== truncate(v)) throw new Error(v + " is not an integer.");
-            return new SmallInteger(v);
-        }
-        return parseStringValue(v.toString());
-    }
-
-    function parseValue(v) {
-        if (typeof v === "number") {
-            return parseNumberValue(v);
-        }
-        if (typeof v === "string") {
-            return parseStringValue(v);
-        }
-        if (typeof v === "bigint") {
-            return new NativeBigInt(v);
-        }
-        return v;
-    }
-    // Pre-define numbers in range [-999,999]
-    for (var i = 0; i < 1000; i++) {
-        Integer[i] = parseValue(i);
-        if (i > 0) Integer[-i] = parseValue(-i);
-    }
-    // Backwards compatibility
-    Integer.one = Integer[1];
-    Integer.zero = Integer[0];
-    Integer.minusOne = Integer[-1];
-    Integer.max = max;
-    Integer.min = min;
-    Integer.gcd = gcd;
-    Integer.lcm = lcm;
-    Integer.isInstance = function (x) { return x instanceof BigInteger || x 
instanceof SmallInteger || x instanceof NativeBigInt; };
-    Integer.randBetween = randBetween;
-
-    Integer.fromArray = function (digits, base, isNegative) {
-        return parseBaseFromArray(digits.map(parseValue), parseValue(base || 
10), isNegative);
-    };
-
-    return Integer;
-})();
-
-// Node.js check
-if (module.hasOwnProperty("exports")) {
-    module.exports = bigInt;
-}
-});
-
-// SHA-256 for JavaScript.
-//
-// Written in 2014-2016 by Dmitry Chestnykh.
-// Public domain, no warranty.
-//
-// Functions (accept and return Uint8Arrays):
-//
-//   sha256(message) -> hash
-//   sha256.hmac(key, message) -> mac
-//
-//  Classes:
-//
-//   new sha256.Hash()
-const digestLength = 32;
-const blockSize = 64;
-// SHA-256 constants
-const K = new Uint32Array([
-    0x428a2f98,
-    0x71374491,
-    0xb5c0fbcf,
-    0xe9b5dba5,
-    0x3956c25b,
-    0x59f111f1,
-    0x923f82a4,
-    0xab1c5ed5,
-    0xd807aa98,
-    0x12835b01,
-    0x243185be,
-    0x550c7dc3,
-    0x72be5d74,
-    0x80deb1fe,
-    0x9bdc06a7,
-    0xc19bf174,
-    0xe49b69c1,
-    0xefbe4786,
-    0x0fc19dc6,
-    0x240ca1cc,
-    0x2de92c6f,
-    0x4a7484aa,
-    0x5cb0a9dc,
-    0x76f988da,
-    0x983e5152,
-    0xa831c66d,
-    0xb00327c8,
-    0xbf597fc7,
-    0xc6e00bf3,
-    0xd5a79147,
-    0x06ca6351,
-    0x14292967,
-    0x27b70a85,
-    0x2e1b2138,
-    0x4d2c6dfc,
-    0x53380d13,
-    0x650a7354,
-    0x766a0abb,
-    0x81c2c92e,
-    0x92722c85,
-    0xa2bfe8a1,
-    0xa81a664b,
-    0xc24b8b70,
-    0xc76c51a3,
-    0xd192e819,
-    0xd6990624,
-    0xf40e3585,
-    0x106aa070,
-    0x19a4c116,
-    0x1e376c08,
-    0x2748774c,
-    0x34b0bcb5,
-    0x391c0cb3,
-    0x4ed8aa4a,
-    0x5b9cca4f,
-    0x682e6ff3,
-    0x748f82ee,
-    0x78a5636f,
-    0x84c87814,
-    0x8cc70208,
-    0x90befffa,
-    0xa4506ceb,
-    0xbef9a3f7,
-    0xc67178f2,
-]);
-function hashBlocks(w, v, p, pos, len) {
-    let a, b, c, d, e, f, g, h, u, i, j, t1, t2;
-    while (len >= 64) {
-        a = v[0];
-        b = v[1];
-        c = v[2];
-        d = v[3];
-        e = v[4];
-        f = v[5];
-        g = v[6];
-        h = v[7];
-        for (i = 0; i < 16; i++) {
-            j = pos + i * 4;
-            w[i] =
-                ((p[j] & 0xff) << 24) |
-                    ((p[j + 1] & 0xff) << 16) |
-                    ((p[j + 2] & 0xff) << 8) |
-                    (p[j + 3] & 0xff);
-        }
-        for (i = 16; i < 64; i++) {
-            u = w[i - 2];
-            t1 =
-                ((u >>> 17) | (u << (32 - 17))) ^
-                    ((u >>> 19) | (u << (32 - 19))) ^
-                    (u >>> 10);
-            u = w[i - 15];
-            t2 =
-                ((u >>> 7) | (u << (32 - 7))) ^
-                    ((u >>> 18) | (u << (32 - 18))) ^
-                    (u >>> 3);
-            w[i] = ((t1 + w[i - 7]) | 0) + ((t2 + w[i - 16]) | 0);
-        }
-        for (i = 0; i < 64; i++) {
-            t1 =
-                ((((((e >>> 6) | (e << (32 - 6))) ^
-                    ((e >>> 11) | (e << (32 - 11))) ^
-                    ((e >>> 25) | (e << (32 - 25)))) +
-                    ((e & f) ^ (~e & g))) |
-                    0) +
-                    ((h + ((K[i] + w[i]) | 0)) | 0)) |
-                    0;
-            t2 =
-                ((((a >>> 2) | (a << (32 - 2))) ^
-                    ((a >>> 13) | (a << (32 - 13))) ^
-                    ((a >>> 22) | (a << (32 - 22)))) +
-                    ((a & b) ^ (a & c) ^ (b & c))) |
-                    0;
-            h = g;
-            g = f;
-            f = e;
-            e = (d + t1) | 0;
-            d = c;
-            c = b;
-            b = a;
-            a = (t1 + t2) | 0;
-        }
-        v[0] += a;
-        v[1] += b;
-        v[2] += c;
-        v[3] += d;
-        v[4] += e;
-        v[5] += f;
-        v[6] += g;
-        v[7] += h;
-        pos += 64;
-        len -= 64;
-    }
-    return pos;
-}
-// Hash implements SHA256 hash algorithm.
-class HashSha256 {
-    constructor() {
-        this.digestLength = digestLength;
-        this.blockSize = blockSize;
-        // Note: Int32Array is used instead of Uint32Array for performance 
reasons.
-        this.state = new Int32Array(8); // hash state
-        this.temp = new Int32Array(64); // temporary state
-        this.buffer = new Uint8Array(128); // buffer for data to hash
-        this.bufferLength = 0; // number of bytes in buffer
-        this.bytesHashed = 0; // number of total bytes hashed
-        this.finished = false; // indicates whether the hash was finalized
-        this.reset();
-    }
-    // Resets hash state making it possible
-    // to re-use this instance to hash other data.
-    reset() {
-        this.state[0] = 0x6a09e667;
-        this.state[1] = 0xbb67ae85;
-        this.state[2] = 0x3c6ef372;
-        this.state[3] = 0xa54ff53a;
-        this.state[4] = 0x510e527f;
-        this.state[5] = 0x9b05688c;
-        this.state[6] = 0x1f83d9ab;
-        this.state[7] = 0x5be0cd19;
-        this.bufferLength = 0;
-        this.bytesHashed = 0;
-        this.finished = false;
-        return this;
-    }
-    // Cleans internal buffers and re-initializes hash state.
-    clean() {
-        for (let i = 0; i < this.buffer.length; i++) {
-            this.buffer[i] = 0;
-        }
-        for (let i = 0; i < this.temp.length; i++) {
-            this.temp[i] = 0;
-        }
-        this.reset();
-    }
-    // Updates hash state with the given data.
-    //
-    // Optionally, length of the data can be specified to hash
-    // fewer bytes than data.length.
-    //
-    // Throws error when trying to update already finalized hash:
-    // instance must be reset to use it again.
-    update(data, dataLength = data.length) {
-        if (this.finished) {
-            throw new Error("SHA256: can't update because hash was finished.");
-        }
-        let dataPos = 0;
-        this.bytesHashed += dataLength;
-        if (this.bufferLength > 0) {
-            while (this.bufferLength < 64 && dataLength > 0) {
-                this.buffer[this.bufferLength++] = data[dataPos++];
-                dataLength--;
-            }
-            if (this.bufferLength === 64) {
-                hashBlocks(this.temp, this.state, this.buffer, 0, 64);
-                this.bufferLength = 0;
-            }
-        }
-        if (dataLength >= 64) {
-            dataPos = hashBlocks(this.temp, this.state, data, dataPos, 
dataLength);
-            dataLength %= 64;
-        }
-        while (dataLength > 0) {
-            this.buffer[this.bufferLength++] = data[dataPos++];
-            dataLength--;
-        }
-        return this;
-    }
-    // Finalizes hash state and puts hash into out.
-    //
-    // If hash was already finalized, puts the same value.
-    finish(out) {
-        if (!this.finished) {
-            const bytesHashed = this.bytesHashed;
-            const left = this.bufferLength;
-            const bitLenHi = (bytesHashed / 0x20000000) | 0;
-            const bitLenLo = bytesHashed << 3;
-            const padLength = bytesHashed % 64 < 56 ? 64 : 128;
-            this.buffer[left] = 0x80;
-            for (let i = left + 1; i < padLength - 8; i++) {
-                this.buffer[i] = 0;
-            }
-            this.buffer[padLength - 8] = (bitLenHi >>> 24) & 0xff;
-            this.buffer[padLength - 7] = (bitLenHi >>> 16) & 0xff;
-            this.buffer[padLength - 6] = (bitLenHi >>> 8) & 0xff;
-            this.buffer[padLength - 5] = (bitLenHi >>> 0) & 0xff;
-            this.buffer[padLength - 4] = (bitLenLo >>> 24) & 0xff;
-            this.buffer[padLength - 3] = (bitLenLo >>> 16) & 0xff;
-            this.buffer[padLength - 2] = (bitLenLo >>> 8) & 0xff;
-            this.buffer[padLength - 1] = (bitLenLo >>> 0) & 0xff;
-            hashBlocks(this.temp, this.state, this.buffer, 0, padLength);
-            this.finished = true;
-        }
-        for (let i = 0; i < 8; i++) {
-            out[i * 4 + 0] = (this.state[i] >>> 24) & 0xff;
-            out[i * 4 + 1] = (this.state[i] >>> 16) & 0xff;
-            out[i * 4 + 2] = (this.state[i] >>> 8) & 0xff;
-            out[i * 4 + 3] = (this.state[i] >>> 0) & 0xff;
-        }
-        return this;
-    }
-    // Returns the final hash digest.
-    digest() {
-        const out = new Uint8Array(this.digestLength);
-        this.finish(out);
-        return out;
-    }
-    // Internal function for use in HMAC for optimization.
-    _saveState(out) {
-        for (let i = 0; i < this.state.length; i++) {
-            out[i] = this.state[i];
-        }
-    }
-    // Internal function for use in HMAC for optimization.
-    _restoreState(from, bytesHashed) {
-        for (let i = 0; i < this.state.length; i++) {
-            this.state[i] = from[i];
-        }
-        this.bytesHashed = bytesHashed;
-        this.finished = false;
-        this.bufferLength = 0;
-    }
-}
-// Returns SHA256 hash of data.
-function sha256(data) {
-    const h = new HashSha256().update(data);
-    const digest = h.digest();
-    h.clean();
-    return digest;
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-function sha512(data) {
-    return hash$1(data);
-}
-function hmac(digest, blockSize, key, message) {
-    if (key.byteLength > blockSize) {
-        key = digest(key);
-    }
-    if (key.byteLength < blockSize) {
-        const k = key;
-        key = new Uint8Array(blockSize);
-        key.set(k, 0);
-    }
-    const okp = new Uint8Array(blockSize);
-    const ikp = new Uint8Array(blockSize);
-    for (let i = 0; i < blockSize; i++) {
-        ikp[i] = key[i] ^ 0x36;
-        okp[i] = key[i] ^ 0x5c;
-    }
-    const b1 = new Uint8Array(blockSize + message.byteLength);
-    b1.set(ikp, 0);
-    b1.set(message, blockSize);
-    const h0 = digest(b1);
-    const b2 = new Uint8Array(blockSize + h0.length);
-    b2.set(okp, 0);
-    b2.set(h0, blockSize);
-    return digest(b2);
-}
-function hmacSha512(key, message) {
-    return hmac(sha512, 128, key, message);
-}
-function hmacSha256(key, message) {
-    return hmac(sha256, 64, key, message);
-}
-function kdf(outputLength, ikm, salt, info) {
-    // extract
-    const prk = hmacSha512(salt, ikm);
-    // expand
-    const N = Math.ceil(outputLength / 32);
-    const output = new Uint8Array(N * 32);
-    for (let i = 0; i < N; i++) {
-        let buf;
-        if (i == 0) {
-            buf = new Uint8Array(info.byteLength + 1);
-            buf.set(info, 0);
-        }
-        else {
-            buf = new Uint8Array(info.byteLength + 1 + 32);
-            for (let j = 0; j < 32; j++) {
-                buf[j] = output[(i - 1) * 32 + j];
-            }
-            buf.set(info, 32);
-        }
-        buf[buf.length - 1] = i + 1;
-        const chunk = hmacSha256(prk, buf);
-        output.set(chunk, i * 32);
-    }
-    return output.slice(0, outputLength);
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-// @ts-ignore
-const decoder = new TextDecoder();
-if (typeof decoder !== "object") {
-    throw Error("FATAL: TextDecoder not available");
-}
-// @ts-ignore
-const encoder = new TextEncoder();
-if (typeof encoder !== "object") {
-    throw Error("FATAL: TextEncoder not available");
-}
-function getRandomBytes(n) {
-    return randomBytes(n);
-}
-const encTable = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
-class EncodingError extends Error {
-    constructor() {
-        super("Encoding error");
-        Object.setPrototypeOf(this, EncodingError.prototype);
-    }
-}
-function getValue(chr) {
-    let a = chr;
-    switch (chr) {
-        case "O":
-        case "o":
-            a = "0;";
-            break;
-        case "i":
-        case "I":
-        case "l":
-        case "L":
-            a = "1";
-            break;
-        case "u":
-        case "U":
-            a = "V";
-    }
-    if (a >= "0" && a <= "9") {
-        return a.charCodeAt(0) - "0".charCodeAt(0);
-    }
-    if (a >= "a" && a <= "z")
-        a = a.toUpperCase();
-    let dec = 0;
-    if (a >= "A" && a <= "Z") {
-        if ("I" < a)
-            dec++;
-        if ("L" < a)
-            dec++;
-        if ("O" < a)
-            dec++;
-        if ("U" < a)
-            dec++;
-        return a.charCodeAt(0) - "A".charCodeAt(0) + 10 - dec;
-    }
-    throw new EncodingError();
-}
-function encodeCrock(data) {
-    const dataBytes = new Uint8Array(data);
-    let sb = "";
-    const size = data.byteLength;
-    let bitBuf = 0;
-    let numBits = 0;
-    let pos = 0;
-    while (pos < size || numBits > 0) {
-        if (pos < size && numBits < 5) {
-            const d = dataBytes[pos++];
-            bitBuf = (bitBuf << 8) | d;
-            numBits += 8;
-        }
-        if (numBits < 5) {
-            // zero-padding
-            bitBuf = bitBuf << (5 - numBits);
-            numBits = 5;
-        }
-        const v = (bitBuf >>> (numBits - 5)) & 31;
-        sb += encTable[v];
-        numBits -= 5;
-    }
-    return sb;
-}
-function decodeCrock(encoded) {
-    const size = encoded.length;
-    let bitpos = 0;
-    let bitbuf = 0;
-    let readPosition = 0;
-    const outLen = Math.floor((size * 5) / 8);
-    const out = new Uint8Array(outLen);
-    let outPos = 0;
-    while (readPosition < size || bitpos > 0) {
-        if (readPosition < size) {
-            const v = getValue(encoded[readPosition++]);
-            bitbuf = (bitbuf << 5) | v;
-            bitpos += 5;
-        }
-        while (bitpos >= 8) {
-            const d = (bitbuf >>> (bitpos - 8)) & 0xff;
-            out[outPos++] = d;
-            bitpos -= 8;
-        }
-        if (readPosition == size && bitpos > 0) {
-            bitbuf = (bitbuf << (8 - bitpos)) & 0xff;
-            bitpos = bitbuf == 0 ? 0 : 8;
-        }
-    }
-    return out;
-}
-function eddsaGetPublic(eddsaPriv) {
-    const pair = sign_keyPair_fromSeed(eddsaPriv);
-    return pair.publicKey;
-}
-function ecdheGetPublic(ecdhePriv) {
-    return scalarMult_base(ecdhePriv);
-}
-function keyExchangeEcdheEddsa(ecdhePriv, eddsaPub) {
-    const curve25519Pub = sign_ed25519_pk_to_curve25519(eddsaPub);
-    const x = scalarMult(ecdhePriv, curve25519Pub);
-    return hash$1(x);
-}
-/**
- * KDF modulo a big integer.
- */
-function kdfMod(n, ikm, salt, info) {
-    const nbits = n.bitLength().toJSNumber();
-    const buflen = Math.floor((nbits - 1) / 8 + 1);
-    const mask = (1 << (8 - (buflen * 8 - nbits))) - 1;
-    let counter = 0;
-    while (true) {
-        const ctx = new Uint8Array(info.byteLength + 2);
-        ctx.set(info, 0);
-        ctx[ctx.length - 2] = (counter >>> 8) & 0xff;
-        ctx[ctx.length - 1] = counter & 0xff;
-        const buf = kdf(buflen, ikm, salt, ctx);
-        const arr = Array.from(buf);
-        arr[0] = arr[0] & mask;
-        const r = BigInteger.fromArray(arr, 256, false);
-        if (r.lt(n)) {
-            return r;
-        }
-        counter++;
-    }
-}
-function stringToBytes(s) {
-    return encoder.encode(s);
-}
-function bytesToString(b) {
-    return decoder.decode(b);
-}
-function loadBigInt(arr) {
-    return BigInteger.fromArray(Array.from(arr), 256, false);
-}
-function rsaBlindingKeyDerive(rsaPub, bks) {
-    const salt = stringToBytes("Blinding KDF extractor HMAC key");
-    const info = stringToBytes("Blinding KDF");
-    return kdfMod(rsaPub.N, bks, salt, info);
-}
-/*
- * Test for malicious RSA key.
- *
- * Assuming n is an RSA modulous and r is generated using a call to
- * GNUNET_CRYPTO_kdf_mod_mpi, if gcd(r,n) != 1 then n must be a
- * malicious RSA key designed to deanomize the user.
- *
- * @param r KDF result
- * @param n RSA modulus of the public key
- */
-function rsaGcdValidate(r, n) {
-    const t = BigInteger.gcd(r, n);
-    if (!t.equals(BigInteger.one)) {
-        throw Error("malicious RSA public key");
-    }
-}
-function rsaFullDomainHash(hm, rsaPub) {
-    const info = stringToBytes("RSA-FDA FTpsW!");
-    const salt = rsaPubEncode(rsaPub);
-    const r = kdfMod(rsaPub.N, hm, salt, info);
-    rsaGcdValidate(r, rsaPub.N);
-    return r;
-}
-function rsaPubDecode(rsaPub) {
-    const modulusLength = (rsaPub[0] << 8) | rsaPub[1];
-    const exponentLength = (rsaPub[2] << 8) | rsaPub[3];
-    if (4 + exponentLength + modulusLength != rsaPub.length) {
-        throw Error("invalid RSA public key (format wrong)");
-    }
-    const modulus = rsaPub.slice(4, 4 + modulusLength);
-    const exponent = rsaPub.slice(4 + modulusLength, 4 + modulusLength + 
exponentLength);
-    const res = {
-        N: loadBigInt(modulus),
-        e: loadBigInt(exponent),
-    };
-    return res;
-}
-function rsaPubEncode(rsaPub) {
-    const mb = rsaPub.N.toArray(256).value;
-    const eb = rsaPub.e.toArray(256).value;
-    const out = new Uint8Array(4 + mb.length + eb.length);
-    out[0] = (mb.length >>> 8) & 0xff;
-    out[1] = mb.length & 0xff;
-    out[2] = (eb.length >>> 8) & 0xff;
-    out[3] = eb.length & 0xff;
-    out.set(mb, 4);
-    out.set(eb, 4 + mb.length);
-    return out;
-}
-function rsaBlind(hm, bks, rsaPubEnc) {
-    const rsaPub = rsaPubDecode(rsaPubEnc);
-    const data = rsaFullDomainHash(hm, rsaPub);
-    const r = rsaBlindingKeyDerive(rsaPub, bks);
-    const r_e = r.modPow(rsaPub.e, rsaPub.N);
-    const bm = r_e.multiply(data).mod(rsaPub.N);
-    return new Uint8Array(bm.toArray(256).value);
-}
-function rsaUnblind(sig, rsaPubEnc, bks) {
-    const rsaPub = rsaPubDecode(rsaPubEnc);
-    const blinded_s = loadBigInt(sig);
-    const r = rsaBlindingKeyDerive(rsaPub, bks);
-    const r_inv = r.modInv(rsaPub.N);
-    const s = blinded_s.multiply(r_inv).mod(rsaPub.N);
-    return new Uint8Array(s.toArray(256).value);
-}
-function rsaVerify(hm, rsaSig, rsaPubEnc) {
-    const rsaPub = rsaPubDecode(rsaPubEnc);
-    const d = rsaFullDomainHash(hm, rsaPub);
-    const sig = loadBigInt(rsaSig);
-    const sig_e = sig.modPow(rsaPub.e, rsaPub.N);
-    return sig_e.equals(d);
-}
-function createEddsaKeyPair() {
-    const eddsaPriv = randomBytes(32);
-    const eddsaPub = eddsaGetPublic(eddsaPriv);
-    return { eddsaPriv, eddsaPub };
-}
-function hash(d) {
-    return hash$1(d);
-}
-function eddsaSign(msg, eddsaPriv) {
-    const pair = sign_keyPair_fromSeed(eddsaPriv);
-    return sign_detached(msg, pair.secretKey);
-}
-function eddsaVerify(msg, sig, eddsaPub) {
-    return sign_detached_verify(msg, sig, eddsaPub);
-}
-function createHashContext() {
-    return new HashState();
-}
-function setupRefreshPlanchet(secretSeed, coinNumber) {
-    const info = stringToBytes("taler-coin-derivation");
-    const saltArrBuf = new ArrayBuffer(4);
-    const salt = new Uint8Array(saltArrBuf);
-    const saltDataView = new DataView(saltArrBuf);
-    saltDataView.setUint32(0, coinNumber);
-    const out = kdf(64, secretSeed, salt, info);
-    const coinPriv = out.slice(0, 32);
-    const bks = out.slice(32, 64);
-    return {
-        bks,
-        coinPriv,
-        coinPub: eddsaGetPublic(coinPriv),
-    };
-}
-function setupWithdrawPlanchet(secretSeed, coinNumber) {
-    const info = stringToBytes("taler-withdrawal-coin-derivation");
-    const saltArrBuf = new ArrayBuffer(4);
-    const salt = new Uint8Array(saltArrBuf);
-    const saltDataView = new DataView(saltArrBuf);
-    saltDataView.setUint32(0, coinNumber);
-    const out = kdf(64, secretSeed, salt, info);
-    const coinPriv = out.slice(0, 32);
-    const bks = out.slice(32, 64);
-    return {
-        bks,
-        coinPriv,
-        coinPub: eddsaGetPublic(coinPriv),
-    };
-}
-function setupTipPlanchet(secretSeed, coinNumber) {
-    const info = stringToBytes("taler-tip-coin-derivation");
-    const saltArrBuf = new ArrayBuffer(4);
-    const salt = new Uint8Array(saltArrBuf);
-    const saltDataView = new DataView(saltArrBuf);
-    saltDataView.setUint32(0, coinNumber);
-    const out = kdf(64, secretSeed, salt, info);
-    const coinPriv = out.slice(0, 32);
-    const bks = out.slice(32, 64);
-    return {
-        bks,
-        coinPriv,
-        coinPub: eddsaGetPublic(coinPriv),
-    };
-}
-function setupRefreshTransferPub(secretSeed, transferPubIndex) {
-    const info = stringToBytes("taler-transfer-pub-derivation");
-    const saltArrBuf = new ArrayBuffer(4);
-    const salt = new Uint8Array(saltArrBuf);
-    const saltDataView = new DataView(saltArrBuf);
-    saltDataView.setUint32(0, transferPubIndex);
-    const out = kdf(32, secretSeed, salt, info);
-    return {
-        ecdhePriv: out,
-        ecdhePub: ecdheGetPublic(out),
-    };
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019-2020 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var SignaturePurpose;
-(function (SignaturePurpose) {
-    SignaturePurpose[SignaturePurpose["MERCHANT_TRACK_TRANSACTION"] = 1103] = 
"MERCHANT_TRACK_TRANSACTION";
-    SignaturePurpose[SignaturePurpose["WALLET_RESERVE_WITHDRAW"] = 1200] = 
"WALLET_RESERVE_WITHDRAW";
-    SignaturePurpose[SignaturePurpose["WALLET_COIN_DEPOSIT"] = 1201] = 
"WALLET_COIN_DEPOSIT";
-    SignaturePurpose[SignaturePurpose["MASTER_DENOMINATION_KEY_VALIDITY"] = 
1025] = "MASTER_DENOMINATION_KEY_VALIDITY";
-    SignaturePurpose[SignaturePurpose["MASTER_WIRE_FEES"] = 1028] = 
"MASTER_WIRE_FEES";
-    SignaturePurpose[SignaturePurpose["MASTER_WIRE_DETAILS"] = 1030] = 
"MASTER_WIRE_DETAILS";
-    SignaturePurpose[SignaturePurpose["WALLET_COIN_MELT"] = 1202] = 
"WALLET_COIN_MELT";
-    SignaturePurpose[SignaturePurpose["TEST"] = 4242] = "TEST";
-    SignaturePurpose[SignaturePurpose["MERCHANT_PAYMENT_OK"] = 1104] = 
"MERCHANT_PAYMENT_OK";
-    SignaturePurpose[SignaturePurpose["MERCHANT_CONTRACT"] = 1101] = 
"MERCHANT_CONTRACT";
-    SignaturePurpose[SignaturePurpose["WALLET_COIN_RECOUP"] = 1203] = 
"WALLET_COIN_RECOUP";
-    SignaturePurpose[SignaturePurpose["WALLET_COIN_LINK"] = 1204] = 
"WALLET_COIN_LINK";
-    SignaturePurpose[SignaturePurpose["EXCHANGE_CONFIRM_RECOUP"] = 1039] = 
"EXCHANGE_CONFIRM_RECOUP";
-    SignaturePurpose[SignaturePurpose["EXCHANGE_CONFIRM_RECOUP_REFRESH"] = 
1041] = "EXCHANGE_CONFIRM_RECOUP_REFRESH";
-    SignaturePurpose[SignaturePurpose["SYNC_BACKUP_UPLOAD"] = 1450] = 
"SYNC_BACKUP_UPLOAD";
-})(SignaturePurpose || (SignaturePurpose = {}));
-function amountToBuffer(amount) {
-    const buffer = new ArrayBuffer(8 + 4 + 12);
-    const dvbuf = new DataView(buffer);
-    const u8buf = new Uint8Array(buffer);
-    const curr = stringToBytes(amount.currency);
-    if (typeof dvbuf.setBigUint64 !== "undefined") {
-        dvbuf.setBigUint64(0, BigInt(amount.value));
-    }
-    else {
-        const arr = BigInteger(amount.value).toArray(2 ** 8).value;
-        let offset = 8 - arr.length;
-        for (let i = 0; i < arr.length; i++) {
-            dvbuf.setUint8(offset++, arr[i]);
-        }
-    }
-    dvbuf.setUint32(8, amount.fraction);
-    u8buf.set(curr, 8 + 4);
-    return u8buf;
-}
-function timestampRoundedToBuffer(ts) {
-    const b = new ArrayBuffer(8);
-    const v = new DataView(b);
-    const tsRounded = timestampTruncateToSecond(ts);
-    if (typeof v.setBigUint64 !== "undefined") {
-        const s = BigInt(tsRounded.t_ms) * BigInt(1000);
-        v.setBigUint64(0, s);
-    }
-    else {
-        const s = (tsRounded.t_ms === "never" ? BigInteger.zero : 
BigInteger(tsRounded.t_ms).times(1000));
-        const arr = s.toArray(2 ** 8).value;
-        let offset = 8 - arr.length;
-        for (let i = 0; i < arr.length; i++) {
-            v.setUint8(offset++, arr[i]);
-        }
-    }
-    return new Uint8Array(b);
-}
-class SignaturePurposeBuilder {
-    constructor(purposeNum) {
-        this.purposeNum = purposeNum;
-        this.chunks = [];
-    }
-    put(bytes) {
-        this.chunks.push(Uint8Array.from(bytes));
-        return this;
-    }
-    build() {
-        let payloadLen = 0;
-        for (const c of this.chunks) {
-            payloadLen += c.byteLength;
-        }
-        const buf = new ArrayBuffer(4 + 4 + payloadLen);
-        const u8buf = new Uint8Array(buf);
-        let p = 8;
-        for (const c of this.chunks) {
-            u8buf.set(c, p);
-            p += c.byteLength;
-        }
-        const dvbuf = new DataView(buf);
-        dvbuf.setUint32(0, payloadLen + 4 + 4);
-        dvbuf.setUint32(4, this.purposeNum);
-        return u8buf;
-    }
-}
-function buildSigPS(purposeNum) {
-    return new SignaturePurposeBuilder(purposeNum);
-}
-class CryptoImplementation {
-    /**
-     * Create a pre-coin of the given denomination to be withdrawn from then 
given
-     * reserve.
-     */
-    createPlanchet(req) {
-        const reservePub = decodeCrock(req.reservePub);
-        const reservePriv = decodeCrock(req.reservePriv);
-        const denomPub = decodeCrock(req.denomPub);
-        const derivedPlanchet = 
setupWithdrawPlanchet(decodeCrock(req.secretSeed), req.coinIndex);
-        const coinPubHash = hash(derivedPlanchet.coinPub);
-        const ev = rsaBlind(coinPubHash, derivedPlanchet.bks, denomPub);
-        const amountWithFee = Amounts.add(req.value, req.feeWithdraw).amount;
-        const denomPubHash = hash(denomPub);
-        const evHash = hash(ev);
-        const withdrawRequest = 
buildSigPS(SignaturePurpose.WALLET_RESERVE_WITHDRAW)
-            .put(reservePub)
-            .put(amountToBuffer(amountWithFee))
-            .put(denomPubHash)
-            .put(evHash)
-            .build();
-        const sig = eddsaSign(withdrawRequest, reservePriv);
-        const planchet = {
-            blindingKey: encodeCrock(derivedPlanchet.bks),
-            coinEv: encodeCrock(ev),
-            coinPriv: encodeCrock(derivedPlanchet.coinPriv),
-            coinPub: encodeCrock(derivedPlanchet.coinPub),
-            coinValue: req.value,
-            denomPub: encodeCrock(denomPub),
-            denomPubHash: encodeCrock(denomPubHash),
-            reservePub: encodeCrock(reservePub),
-            withdrawSig: encodeCrock(sig),
-            coinEvHash: encodeCrock(evHash),
-        };
-        return planchet;
-    }
-    /**
-     * Create a planchet used for tipping, including the private keys.
-     */
-    createTipPlanchet(req) {
-        const fc = setupTipPlanchet(decodeCrock(req.secretSeed), 
req.planchetIndex);
-        const denomPub = decodeCrock(req.denomPub);
-        const coinPubHash = hash(fc.coinPub);
-        const ev = rsaBlind(coinPubHash, fc.bks, denomPub);
-        const tipPlanchet = {
-            blindingKey: encodeCrock(fc.bks),
-            coinEv: encodeCrock(ev),
-            coinEvHash: encodeCrock(hash(ev)),
-            coinPriv: encodeCrock(fc.coinPriv),
-            coinPub: encodeCrock(fc.coinPub),
-        };
-        return tipPlanchet;
-    }
-    signTrackTransaction(req) {
-        const p = buildSigPS(SignaturePurpose.MERCHANT_TRACK_TRANSACTION)
-            .put(decodeCrock(req.contractTermsHash))
-            .put(decodeCrock(req.wireHash))
-            .put(decodeCrock(req.merchantPub))
-            .put(decodeCrock(req.coinPub))
-            .build();
-        return encodeCrock(eddsaSign(p, decodeCrock(req.merchantPriv)));
-    }
-    /**
-     * Create and sign a message to recoup a coin.
-     */
-    createRecoupRequest(coin) {
-        const p = buildSigPS(SignaturePurpose.WALLET_COIN_RECOUP)
-            .put(decodeCrock(coin.coinPub))
-            .put(decodeCrock(coin.denomPubHash))
-            .put(decodeCrock(coin.blindingKey))
-            .build();
-        const coinPriv = decodeCrock(coin.coinPriv);
-        const coinSig = eddsaSign(p, coinPriv);
-        const paybackRequest = {
-            coin_blind_key_secret: coin.blindingKey,
-            coin_pub: coin.coinPub,
-            coin_sig: encodeCrock(coinSig),
-            denom_pub_hash: coin.denomPubHash,
-            denom_sig: coin.denomSig,
-            refreshed: coin.coinSource.type === CoinSourceType.Refresh,
-        };
-        return paybackRequest;
-    }
-    /**
-     * Check if a payment signature is valid.
-     */
-    isValidPaymentSignature(sig, contractHash, merchantPub) {
-        const p = buildSigPS(SignaturePurpose.MERCHANT_PAYMENT_OK)
-            .put(decodeCrock(contractHash))
-            .build();
-        const sigBytes = decodeCrock(sig);
-        const pubBytes = decodeCrock(merchantPub);
-        return eddsaVerify(p, sigBytes, pubBytes);
-    }
-    /**
-     * Check if a wire fee is correctly signed.
-     */
-    isValidWireFee(type, wf, masterPub) {
-        const p = buildSigPS(SignaturePurpose.MASTER_WIRE_FEES)
-            .put(hash(stringToBytes(type + "\0")))
-            .put(timestampRoundedToBuffer(wf.startStamp))
-            .put(timestampRoundedToBuffer(wf.endStamp))
-            .put(amountToBuffer(wf.wireFee))
-            .put(amountToBuffer(wf.closingFee))
-            .build();
-        const sig = decodeCrock(wf.sig);
-        const pub = decodeCrock(masterPub);
-        return eddsaVerify(p, sig, pub);
-    }
-    /**
-     * Check if the signature of a denomination is valid.
-     */
-    isValidDenom(denom, masterPub) {
-        const p = buildSigPS(SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
-            .put(decodeCrock(masterPub))
-            .put(timestampRoundedToBuffer(denom.stampStart))
-            .put(timestampRoundedToBuffer(denom.stampExpireWithdraw))
-            .put(timestampRoundedToBuffer(denom.stampExpireDeposit))
-            .put(timestampRoundedToBuffer(denom.stampExpireLegal))
-            .put(amountToBuffer(denom.value))
-            .put(amountToBuffer(denom.feeWithdraw))
-            .put(amountToBuffer(denom.feeDeposit))
-            .put(amountToBuffer(denom.feeRefresh))
-            .put(amountToBuffer(denom.feeRefund))
-            .put(decodeCrock(denom.denomPubHash))
-            .build();
-        const sig = decodeCrock(denom.masterSig);
-        const pub = decodeCrock(masterPub);
-        return eddsaVerify(p, sig, pub);
-    }
-    isValidWireAccount(paytoUri, sig, masterPub) {
-        const h = kdf(64, stringToBytes("exchange-wire-signature"), 
stringToBytes(paytoUri + "\0"), new Uint8Array(0));
-        const p = 
buildSigPS(SignaturePurpose.MASTER_WIRE_DETAILS).put(h).build();
-        return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
-    }
-    isValidContractTermsSignature(contractTermsHash, sig, merchantPub) {
-        const cthDec = decodeCrock(contractTermsHash);
-        const p = buildSigPS(SignaturePurpose.MERCHANT_CONTRACT)
-            .put(cthDec)
-            .build();
-        return eddsaVerify(p, decodeCrock(sig), decodeCrock(merchantPub));
-    }
-    /**
-     * Create a new EdDSA key pair.
-     */
-    createEddsaKeypair() {
-        const pair = createEddsaKeyPair();
-        return {
-            priv: encodeCrock(pair.eddsaPriv),
-            pub: encodeCrock(pair.eddsaPub),
-        };
-    }
-    /**
-     * Unblind a blindly signed value.
-     */
-    rsaUnblind(blindedSig, bk, pk) {
-        const denomSig = rsaUnblind(decodeCrock(blindedSig), decodeCrock(pk), 
decodeCrock(bk));
-        return encodeCrock(denomSig);
-    }
-    /**
-     * Unblind a blindly signed value.
-     */
-    rsaVerify(hm, sig, pk) {
-        return rsaVerify(hash(decodeCrock(hm)), decodeCrock(sig), 
decodeCrock(pk));
-    }
-    /**
-     * Generate updated coins (to store in the database)
-     * and deposit permissions for each given coin.
-     */
-    signDepositPermission(depositInfo) {
-        const d = buildSigPS(SignaturePurpose.WALLET_COIN_DEPOSIT)
-            .put(decodeCrock(depositInfo.contractTermsHash))
-            .put(decodeCrock(depositInfo.wireInfoHash))
-            .put(decodeCrock(depositInfo.denomPubHash))
-            .put(timestampRoundedToBuffer(depositInfo.timestamp))
-            .put(timestampRoundedToBuffer(depositInfo.refundDeadline))
-            .put(amountToBuffer(depositInfo.spendAmount))
-            .put(amountToBuffer(depositInfo.feeDeposit))
-            .put(decodeCrock(depositInfo.merchantPub))
-            .put(decodeCrock(depositInfo.coinPub))
-            .build();
-        const coinSig = eddsaSign(d, decodeCrock(depositInfo.coinPriv));
-        const s = {
-            coin_pub: depositInfo.coinPub,
-            coin_sig: encodeCrock(coinSig),
-            contribution: Amounts.stringify(depositInfo.spendAmount),
-            h_denom: depositInfo.denomPubHash,
-            exchange_url: depositInfo.exchangeBaseUrl,
-            ub_sig: depositInfo.denomSig,
-        };
-        return s;
-    }
-    deriveRefreshSession(req) {
-        const { newCoinDenoms, feeRefresh: meltFee, kappa, 
meltCoinDenomPubHash, meltCoinPriv, meltCoinPub, sessionSecretSeed: 
refreshSessionSecretSeed, } = req;
-        const currency = newCoinDenoms[0].value.currency;
-        let valueWithFee = Amounts.getZero(currency);
-        for (const ncd of newCoinDenoms) {
-            const t = Amounts.add(ncd.value, ncd.feeWithdraw).amount;
-            valueWithFee = Amounts.add(valueWithFee, Amounts.mult(t, 
ncd.count).amount).amount;
-        }
-        // melt fee
-        valueWithFee = Amounts.add(valueWithFee, meltFee).amount;
-        const sessionHc = createHashContext();
-        const transferPubs = [];
-        const transferPrivs = [];
-        const planchetsForGammas = [];
-        for (let i = 0; i < kappa; i++) {
-            const transferKeyPair = 
setupRefreshTransferPub(decodeCrock(refreshSessionSecretSeed), i);
-            sessionHc.update(transferKeyPair.ecdhePub);
-            transferPrivs.push(encodeCrock(transferKeyPair.ecdhePriv));
-            transferPubs.push(encodeCrock(transferKeyPair.ecdhePub));
-        }
-        for (const denomSel of newCoinDenoms) {
-            for (let i = 0; i < denomSel.count; i++) {
-                const r = decodeCrock(denomSel.denomPub);
-                sessionHc.update(r);
-            }
-        }
-        sessionHc.update(decodeCrock(meltCoinPub));
-        sessionHc.update(amountToBuffer(valueWithFee));
-        for (let i = 0; i < kappa; i++) {
-            const planchets = [];
-            for (let j = 0; j < newCoinDenoms.length; j++) {
-                const denomSel = newCoinDenoms[j];
-                for (let k = 0; k < denomSel.count; k++) {
-                    const coinNumber = planchets.length;
-                    const transferPriv = decodeCrock(transferPrivs[i]);
-                    const oldCoinPub = decodeCrock(meltCoinPub);
-                    const transferSecret = keyExchangeEcdheEddsa(transferPriv, 
oldCoinPub);
-                    const fresh = setupRefreshPlanchet(transferSecret, 
coinNumber);
-                    const coinPriv = fresh.coinPriv;
-                    const coinPub = fresh.coinPub;
-                    const blindingFactor = fresh.bks;
-                    const pubHash = hash(coinPub);
-                    const denomPub = decodeCrock(denomSel.denomPub);
-                    const ev = rsaBlind(pubHash, blindingFactor, denomPub);
-                    const planchet = {
-                        blindingKey: encodeCrock(blindingFactor),
-                        coinEv: encodeCrock(ev),
-                        privateKey: encodeCrock(coinPriv),
-                        publicKey: encodeCrock(coinPub),
-                        coinEvHash: encodeCrock(hash(ev)),
-                    };
-                    planchets.push(planchet);
-                    sessionHc.update(ev);
-                }
-            }
-            planchetsForGammas.push(planchets);
-        }
-        const sessionHash = sessionHc.finish();
-        const confirmData = buildSigPS(SignaturePurpose.WALLET_COIN_MELT)
-            .put(sessionHash)
-            .put(decodeCrock(meltCoinDenomPubHash))
-            .put(amountToBuffer(valueWithFee))
-            .put(amountToBuffer(meltFee))
-            .put(decodeCrock(meltCoinPub))
-            .build();
-        const confirmSig = eddsaSign(confirmData, decodeCrock(meltCoinPriv));
-        const refreshSession = {
-            confirmSig: encodeCrock(confirmSig),
-            hash: encodeCrock(sessionHash),
-            meltCoinPub: meltCoinPub,
-            planchetsForGammas: planchetsForGammas,
-            transferPrivs,
-            transferPubs,
-            meltValueWithFee: valueWithFee,
-        };
-        return refreshSession;
-    }
-    /**
-     * Hash a string including the zero terminator.
-     */
-    hashString(str) {
-        const b = stringToBytes(str + "\0");
-        return encodeCrock(hash(b));
-    }
-    /**
-     * Hash a crockford encoded value.
-     */
-    hashEncoded(encodedBytes) {
-        return encodeCrock(hash(decodeCrock(encodedBytes)));
-    }
-    signCoinLink(oldCoinPriv, newDenomHash, oldCoinPub, transferPub, coinEv) {
-        const coinEvHash = hash(decodeCrock(coinEv));
-        const coinLink = buildSigPS(SignaturePurpose.WALLET_COIN_LINK)
-            .put(decodeCrock(newDenomHash))
-            .put(decodeCrock(transferPub))
-            .put(coinEvHash)
-            .build();
-        const coinPriv = decodeCrock(oldCoinPriv);
-        const sig = eddsaSign(coinLink, coinPriv);
-        return encodeCrock(sig);
-    }
-    benchmark(repetitions) {
-        let time_hash = 0;
-        for (let i = 0; i < repetitions; i++) {
-            const start = performanceNow();
-            this.hashString("hello world");
-            time_hash += performanceNow() - start;
-        }
-        let time_hash_big = 0;
-        for (let i = 0; i < repetitions; i++) {
-            const ba = randomBytes(4096);
-            const start = performanceNow();
-            hash(ba);
-            time_hash_big += performanceNow() - start;
-        }
-        let time_eddsa_create = 0;
-        for (let i = 0; i < repetitions; i++) {
-            const start = performanceNow();
-            createEddsaKeyPair();
-            time_eddsa_create += performanceNow() - start;
-        }
-        let time_eddsa_sign = 0;
-        const p = randomBytes(4096);
-        const pair = createEddsaKeyPair();
-        for (let i = 0; i < repetitions; i++) {
-            const start = performanceNow();
-            eddsaSign(p, pair.eddsaPriv);
-            time_eddsa_sign += performanceNow() - start;
-        }
-        const sig = eddsaSign(p, pair.eddsaPriv);
-        let time_eddsa_verify = 0;
-        for (let i = 0; i < repetitions; i++) {
-            const start = performanceNow();
-            eddsaVerify(p, sig, pair.eddsaPub);
-            time_eddsa_verify += performanceNow() - start;
-        }
-        return {
-            repetitions,
-            time: {
-                hash_small: time_hash,
-                hash_big: time_hash_big,
-                eddsa_create: time_eddsa_create,
-                eddsa_sign: time_eddsa_sign,
-                eddsa_verify: time_eddsa_verify,
-            },
-        };
-    }
-    makeSyncSignature(req) {
-        const hNew = decodeCrock(req.newHash);
-        let hOld;
-        if (req.oldHash) {
-            hOld = decodeCrock(req.oldHash);
-        }
-        else {
-            hOld = new Uint8Array(64);
-        }
-        const sigBlob = new 
SignaturePurposeBuilder(SignaturePurpose.SYNC_BACKUP_UPLOAD)
-            .put(hOld)
-            .put(hNew)
-            .build();
-        const uploadSig = eddsaSign(sigBlob, decodeCrock(req.accountPriv));
-        return encodeCrock(uploadSig);
-    }
-}
-CryptoImplementation.enableTracing = false;
-
-/*
- This file is part of GNU Taler
- (C) 2016 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$i = new Logger("cryptoApi.ts");
-/**
- * Number of different priorities. Each priority p
- * must be 0 <= p < NUM_PRIO.
- */
-const NUM_PRIO = 5;
-/**
- * Crypto API that interfaces manages a background crypto thread
- * for the execution of expensive operations.
- */
-class CryptoApi {
-    constructor(workerFactory) {
-        this.nextRpcId = 1;
-        /**
-         * Number of busy workers.
-         */
-        this.numBusy = 0;
-        /**
-         * Did we stop accepting new requests?
-         */
-        this.stopped = false;
-        this.workerFactory = workerFactory;
-        this.workers = new Array(workerFactory.getConcurrency());
-        for (let i = 0; i < this.workers.length; i++) {
-            this.workers[i] = {
-                currentWorkItem: null,
-                terminationTimerHandle: null,
-                w: null,
-            };
-        }
-        this.workQueues = [];
-        for (let i = 0; i < NUM_PRIO; i++) {
-            this.workQueues.push([]);
-        }
-    }
-    /**
-     * Terminate all worker threads.
-     */
-    terminateWorkers() {
-        for (const worker of this.workers) {
-            if (worker.w) {
-                logger$i.trace("terminating worker");
-                worker.w.terminate();
-                if (worker.terminationTimerHandle) {
-                    worker.terminationTimerHandle.clear();
-                    worker.terminationTimerHandle = null;
-                }
-                if (worker.currentWorkItem) {
-                    worker.currentWorkItem.reject(Error("explicitly 
terminated"));
-                    worker.currentWorkItem = null;
-                }
-                worker.w = null;
-            }
-        }
-    }
-    stop() {
-        this.terminateWorkers();
-        this.stopped = true;
-    }
-    /**
-     * Start a worker (if not started) and set as busy.
-     */
-    wake(ws, work) {
-        if (this.stopped) {
-            logger$i.trace("cryptoApi is stopped");
-            return;
-        }
-        if (ws.currentWorkItem !== null) {
-            throw Error("assertion failed");
-        }
-        ws.currentWorkItem = work;
-        this.numBusy++;
-        let worker;
-        if (!ws.w) {
-            worker = this.workerFactory.startWorker();
-            worker.onmessage = (m) => this.handleWorkerMessage(ws, m);
-            worker.onerror = (e) => this.handleWorkerError(ws, e);
-            ws.w = worker;
-        }
-        else {
-            worker = ws.w;
-        }
-        const msg = {
-            args: work.args,
-            id: work.rpcId,
-            operation: work.operation,
-        };
-        this.resetWorkerTimeout(ws);
-        work.startTime = performanceNow();
-        after(0, () => worker.postMessage(msg));
-    }
-    resetWorkerTimeout(ws) {
-        if (ws.terminationTimerHandle !== null) {
-            ws.terminationTimerHandle.clear();
-            ws.terminationTimerHandle = null;
-        }
-        const destroy = () => {
-            // terminate worker if it's idle
-            if (ws.w && ws.currentWorkItem === null) {
-                ws.w.terminate();
-                ws.w = null;
-            }
-        };
-        ws.terminationTimerHandle = after(15 * 1000, destroy);
-        //ws.terminationTimerHandle.unref();
-    }
-    handleWorkerError(ws, e) {
-        if (ws.currentWorkItem) {
-            logger$i.error(`error in worker during 
${ws.currentWorkItem.operation}`, e);
-        }
-        else {
-            logger$i.error("error in worker", e);
-        }
-        logger$i.error(e.message);
-        try {
-            if (ws.w) {
-                ws.w.terminate();
-                ws.w = null;
-            }
-        }
-        catch (e) {
-            logger$i.error(e);
-        }
-        if (ws.currentWorkItem !== null) {
-            ws.currentWorkItem.reject(e);
-            ws.currentWorkItem = null;
-            this.numBusy--;
-        }
-        this.findWork(ws);
-    }
-    findWork(ws) {
-        // try to find more work for this worker
-        for (let i = 0; i < NUM_PRIO; i++) {
-            const q = this.workQueues[NUM_PRIO - i - 1];
-            if (q.length !== 0) {
-                const work = q.shift();
-                if (!work) {
-                    continue;
-                }
-                this.wake(ws, work);
-                return;
-            }
-        }
-    }
-    handleWorkerMessage(ws, msg) {
-        const id = msg.data.id;
-        if (typeof id !== "number") {
-            console.error("rpc id must be number");
-            return;
-        }
-        const currentWorkItem = ws.currentWorkItem;
-        ws.currentWorkItem = null;
-        this.numBusy--;
-        this.findWork(ws);
-        if (!currentWorkItem) {
-            console.error("unsolicited response from worker");
-            return;
-        }
-        if (id !== currentWorkItem.rpcId) {
-            console.error(`RPC with id ${id} has no registry entry`);
-            return;
-        }
-        currentWorkItem.resolve(msg.data.result);
-    }
-    doRpc(operation, priority, ...args) {
-        const p = new Promise((resolve, reject) => {
-            const rpcId = this.nextRpcId++;
-            const workItem = {
-                operation,
-                args,
-                resolve,
-                reject,
-                rpcId,
-                startTime: 0,
-            };
-            if (this.numBusy === this.workers.length) {
-                const q = this.workQueues[priority];
-                if (!q) {
-                    throw Error("assertion failed");
-                }
-                this.workQueues[priority].push(workItem);
-                return;
-            }
-            for (const ws of this.workers) {
-                if (ws.currentWorkItem !== null) {
-                    continue;
-                }
-                this.wake(ws, workItem);
-                return;
-            }
-            throw Error("assertion failed");
-        });
-        return p;
-    }
-    createPlanchet(req) {
-        return this.doRpc("createPlanchet", 1, req);
-    }
-    createTipPlanchet(req) {
-        return this.doRpc("createTipPlanchet", 1, req);
-    }
-    signTrackTransaction(req) {
-        return this.doRpc("signTrackTransaction", 1, req);
-    }
-    hashString(str) {
-        return this.doRpc("hashString", 1, str);
-    }
-    hashEncoded(encodedBytes) {
-        return this.doRpc("hashEncoded", 1, encodedBytes);
-    }
-    isValidDenom(denom, masterPub) {
-        return this.doRpc("isValidDenom", 2, denom, masterPub);
-    }
-    isValidWireFee(type, wf, masterPub) {
-        return this.doRpc("isValidWireFee", 2, type, wf, masterPub);
-    }
-    isValidPaymentSignature(sig, contractHash, merchantPub) {
-        return this.doRpc("isValidPaymentSignature", 1, sig, contractHash, 
merchantPub);
-    }
-    signDepositPermission(depositInfo) {
-        return this.doRpc("signDepositPermission", 3, depositInfo);
-    }
-    createEddsaKeypair() {
-        return this.doRpc("createEddsaKeypair", 1);
-    }
-    rsaUnblind(sig, bk, pk) {
-        return this.doRpc("rsaUnblind", 4, sig, bk, pk);
-    }
-    rsaVerify(hm, sig, pk) {
-        return this.doRpc("rsaVerify", 4, hm, sig, pk);
-    }
-    isValidWireAccount(paytoUri, sig, masterPub) {
-        return this.doRpc("isValidWireAccount", 4, paytoUri, sig, masterPub);
-    }
-    isValidContractTermsSignature(contractTermsHash, sig, merchantPub) {
-        return this.doRpc("isValidContractTermsSignature", 4, 
contractTermsHash, sig, merchantPub);
-    }
-    createRecoupRequest(coin) {
-        return this.doRpc("createRecoupRequest", 1, coin);
-    }
-    deriveRefreshSession(req) {
-        return this.doRpc("deriveRefreshSession", 4, req);
-    }
-    signCoinLink(oldCoinPriv, newDenomHash, oldCoinPub, transferPub, coinEv) {
-        return this.doRpc("signCoinLink", 4, oldCoinPriv, newDenomHash, 
oldCoinPub, transferPub, coinEv);
-    }
-    benchmark(repetitions) {
-        return this.doRpc("benchmark", 1, repetitions);
-    }
-    makeSyncSignature(req) {
-        return this.doRpc("makeSyncSignature", 3, req);
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var PendingOperationType;
-(function (PendingOperationType) {
-    PendingOperationType["ExchangeUpdate"] = "exchange-update";
-    PendingOperationType["ExchangeCheckRefresh"] = "exchange-check-refresh";
-    PendingOperationType["Pay"] = "pay";
-    PendingOperationType["ProposalChoice"] = "proposal-choice";
-    PendingOperationType["ProposalDownload"] = "proposal-download";
-    PendingOperationType["Refresh"] = "refresh";
-    PendingOperationType["Reserve"] = "reserve";
-    PendingOperationType["Recoup"] = "recoup";
-    PendingOperationType["RefundQuery"] = "refund-query";
-    PendingOperationType["TipPickup"] = "tip-pickup";
-    PendingOperationType["Withdraw"] = "withdraw";
-    PendingOperationType["Deposit"] = "deposit";
-})(PendingOperationType || (PendingOperationType = {}));
-var ReserveType;
-(function (ReserveType) {
-    /**
-     * Manually created.
-     */
-    ReserveType["Manual"] = "manual";
-    /**
-     * Withdrawn from a bank that has "tight" Taler integration
-     */
-    ReserveType["TalerBankWithdraw"] = "taler-bank-withdraw";
-})(ReserveType || (ReserveType = {}));
-
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var WalletApiOperation;
-(function (WalletApiOperation) {
-    WalletApiOperation["InitWallet"] = "initWallet";
-    WalletApiOperation["WithdrawTestkudos"] = "withdrawTestkudos";
-    WalletApiOperation["WithdrawTestBalance"] = "withdrawTestBalance";
-    WalletApiOperation["PreparePayForUri"] = "preparePayForUri";
-    WalletApiOperation["RunIntegrationTest"] = "runIntegrationTest";
-    WalletApiOperation["TestPay"] = "testPay";
-    WalletApiOperation["AddExchange"] = "addExchange";
-    WalletApiOperation["GetTransactions"] = "getTransactions";
-    WalletApiOperation["ListExchanges"] = "listExchanges";
-    WalletApiOperation["GetWithdrawalDetailsForUri"] = 
"getWithdrawalDetailsForUri";
-    WalletApiOperation["GetWithdrawalDetailsForAmount"] = 
"getWithdrawalDetailsForAmount";
-    WalletApiOperation["AcceptManualWithdrawal"] = "acceptManualWithdrawal";
-    WalletApiOperation["GetBalances"] = "getBalances";
-    WalletApiOperation["GetPendingOperations"] = "getPendingOperations";
-    WalletApiOperation["SetExchangeTosAccepted"] = "setExchangeTosAccepted";
-    WalletApiOperation["ApplyRefund"] = "applyRefund";
-    WalletApiOperation["AcceptBankIntegratedWithdrawal"] = 
"acceptBankIntegratedWithdrawal";
-    WalletApiOperation["GetExchangeTos"] = "getExchangeTos";
-    WalletApiOperation["RetryPendingNow"] = "retryPendingNow";
-    WalletApiOperation["AbortFailedPayWithRefund"] = 
"abortFailedPayWithRefund";
-    WalletApiOperation["ConfirmPay"] = "confirmPay";
-    WalletApiOperation["DumpCoins"] = "dumpCoins";
-    WalletApiOperation["SetCoinSuspended"] = "setCoinSuspended";
-    WalletApiOperation["ForceRefresh"] = "forceRefresh";
-    WalletApiOperation["PrepareTip"] = "prepareTip";
-    WalletApiOperation["AcceptTip"] = "acceptTip";
-    WalletApiOperation["ExportBackup"] = "exportBackup";
-    WalletApiOperation["AddBackupProvider"] = "addBackupProvider";
-    WalletApiOperation["RunBackupCycle"] = "runBackupCycle";
-    WalletApiOperation["ExportBackupRecovery"] = "exportBackupRecovery";
-    WalletApiOperation["ImportBackupRecovery"] = "importBackupRecovery";
-    WalletApiOperation["GetBackupInfo"] = "getBackupInfo";
-    WalletApiOperation["TrackDepositGroup"] = "trackDepositGroup";
-    WalletApiOperation["DeleteTransaction"] = "deleteTransaction";
-    WalletApiOperation["RetryTransaction"] = "retryTransaction";
-    WalletApiOperation["GetCoins"] = "getCoins";
-    WalletApiOperation["ListCurrencies"] = "listCurrencies";
-    WalletApiOperation["CreateDepositGroup"] = "createDepositGroup";
-    WalletApiOperation["SetWalletDeviceId"] = "setWalletDeviceId";
-    WalletApiOperation["ExportBackupPlain"] = "exportBackupPlain";
-})(WalletApiOperation || (WalletApiOperation = {}));
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Helpers for invariants.
- */
-function checkDbInvariant(b, m) {
-    if (!b) {
-        if (m) {
-            throw Error(`BUG: database invariant failed (${m})`);
-        }
-        else {
-            throw Error("BUG: database invariant failed");
-        }
-    }
-}
-function checkLogicInvariant(b, m) {
-    if (!b) {
-        if (m) {
-            throw Error(`BUG: logic invariant failed (${m})`);
-        }
-        else {
-            throw Error("BUG: logic invariant failed");
-        }
-    }
-}
-
-// DEFLATE is a complex format; to read this code, you should probably check 
the RFC first:
-
-// aliases for shorter compressed code (most minifers don't do this)
-var u8 = Uint8Array, u16 = Uint16Array, u32 = Uint32Array;
-// fixed length extra bits
-var fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 
4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);
-// fixed distance extra bits
-// see fleb note
-var fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 
9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);
-// code length index map
-var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 
1, 15]);
-// get base, reverse index map from extra bits
-var freb = function (eb, start) {
-    var b = new u16(31);
-    for (var i = 0; i < 31; ++i) {
-        b[i] = start += 1 << eb[i - 1];
-    }
-    // numbers here are at max 18 bits
-    var r = new u32(b[30]);
-    for (var i = 1; i < 30; ++i) {
-        for (var j = b[i]; j < b[i + 1]; ++j) {
-            r[j] = ((j - b[i]) << 5) | i;
-        }
-    }
-    return [b, r];
-};
-var _a = freb(fleb, 2), fl = _a[0], revfl = _a[1];
-// we can ignore the fact that the other numbers are wrong; they never happen 
anyway
-fl[28] = 258, revfl[258] = 28;
-var _b = freb(fdeb, 0), fd = _b[0], revfd = _b[1];
-// map of value to reverse (assuming 16 bits)
-var rev = new u16(32768);
-for (var i = 0; i < 32768; ++i) {
-    // reverse table algorithm from SO
-    var x = ((i & 0xAAAA) >>> 1) | ((i & 0x5555) << 1);
-    x = ((x & 0xCCCC) >>> 2) | ((x & 0x3333) << 2);
-    x = ((x & 0xF0F0) >>> 4) | ((x & 0x0F0F) << 4);
-    rev[i] = (((x & 0xFF00) >>> 8) | ((x & 0x00FF) << 8)) >>> 1;
-}
-// create huffman tree from u8 "map": index -> code length for code index
-// mb (max bits) must be at most 15
-// TODO: optimize/split up?
-var hMap = (function (cd, mb, r) {
-    var s = cd.length;
-    // index
-    var i = 0;
-    // u16 "map": index -> # of codes with bit length = index
-    var l = new u16(mb);
-    // length of cd must be 288 (total # of codes)
-    for (; i < s; ++i)
-        ++l[cd[i] - 1];
-    // u16 "map": index -> minimum code for bit length = index
-    var le = new u16(mb);
-    for (i = 0; i < mb; ++i) {
-        le[i] = (le[i - 1] + l[i - 1]) << 1;
-    }
-    var co;
-    if (r) {
-        // u16 "map": index -> number of actual bits, symbol for code
-        co = new u16(1 << mb);
-        // bits to remove for reverser
-        var rvb = 15 - mb;
-        for (i = 0; i < s; ++i) {
-            // ignore 0 lengths
-            if (cd[i]) {
-                // num encoding both symbol and bits read
-                var sv = (i << 4) | cd[i];
-                // free bits
-                var r_1 = mb - cd[i];
-                // start value
-                var v = le[cd[i] - 1]++ << r_1;
-                // m is end value
-                for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {
-                    // every 16 bit value starting with the code yields the 
same result
-                    co[rev[v] >>> rvb] = sv;
-                }
-            }
-        }
-    }
-    else {
-        co = new u16(s);
-        for (i = 0; i < s; ++i)
-            co[i] = rev[le[cd[i] - 1]++] >>> (15 - cd[i]);
-    }
-    return co;
-});
-// fixed length tree
-var flt = new u8(288);
-for (var i = 0; i < 144; ++i)
-    flt[i] = 8;
-for (var i = 144; i < 256; ++i)
-    flt[i] = 9;
-for (var i = 256; i < 280; ++i)
-    flt[i] = 7;
-for (var i = 280; i < 288; ++i)
-    flt[i] = 8;
-// fixed distance tree
-var fdt = new u8(32);
-for (var i = 0; i < 32; ++i)
-    fdt[i] = 5;
-// fixed length map
-var flm = /*#__PURE__*/ hMap(flt, 9, 0), flrm = /*#__PURE__*/ hMap(flt, 9, 1);
-// fixed distance map
-var fdm = /*#__PURE__*/ hMap(fdt, 5, 0), fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);
-// find max of array
-var max = function (a) {
-    var m = a[0];
-    for (var i = 1; i < a.length; ++i) {
-        if (a[i] > m)
-            m = a[i];
-    }
-    return m;
-};
-// read d, starting at bit p and mask with m
-var bits = function (d, p, m) {
-    var o = (p / 8) | 0;
-    return ((d[o] | (d[o + 1] << 8)) >>> (p & 7)) & m;
-};
-// read d, starting at bit p continuing for at least 16 bits
-var bits16 = function (d, p) {
-    var o = (p / 8) | 0;
-    return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >>> (p & 7));
-};
-// get end of byte
-var shft = function (p) { return ((p / 8) | 0) + (p & 7 && 1); };
-// typed array slice - allows garbage collector to free original reference,
-// while being more compatible than .slice
-var slc = function (v, s, e) {
-    if (s == null || s < 0)
-        s = 0;
-    if (e == null || e > v.length)
-        e = v.length;
-    // can't use .constructor in case user-supplied
-    var n = new (v instanceof u16 ? u16 : v instanceof u32 ? u32 : u8)(e - s);
-    n.set(v.subarray(s, e));
-    return n;
-};
-// expands raw DEFLATE data
-var inflt = function (dat, buf, st) {
-    // source length
-    var sl = dat.length;
-    if (!sl || (st && !st.l && sl < 5))
-        return buf || new u8(0);
-    // have to estimate size
-    var noBuf = !buf || st;
-    // no state
-    var noSt = !st || st.i;
-    if (!st)
-        st = {};
-    // Assumes roughly 33% compression ratio average
-    if (!buf)
-        buf = new u8(sl * 3);
-    // ensure buffer can fit at least l elements
-    var cbuf = function (l) {
-        var bl = buf.length;
-        // need to increase size to fit
-        if (l > bl) {
-            // Double or set to necessary, whichever is greater
-            var nbuf = new u8(Math.max(bl * 2, l));
-            nbuf.set(buf);
-            buf = nbuf;
-        }
-    };
-    //  last chunk         bitpos           bytes
-    var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = 
st.d, lbt = st.m, dbt = st.n;
-    // total bits
-    var tbts = sl * 8;
-    do {
-        if (!lm) {
-            // BFINAL - this is only 1 when last chunk is next
-            st.f = final = bits(dat, pos, 1);
-            // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman
-            var type = bits(dat, pos + 1, 3);
-            pos += 3;
-            if (!type) {
-                // go to end of byte boundary
-                var s = shft(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = 
s + l;
-                if (t > sl) {
-                    if (noSt)
-                        throw 'unexpected EOF';
-                    break;
-                }
-                // ensure size
-                if (noBuf)
-                    cbuf(bt + l);
-                // Copy over uncompressed data
-                buf.set(dat.subarray(s, t), bt);
-                // Get new bitpos, update byte count
-                st.b = bt += l, st.p = pos = t * 8;
-                continue;
-            }
-            else if (type == 1)
-                lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
-            else if (type == 2) {
-                //  literal                            lengths
-                var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 
10, 15) + 4;
-                var tl = hLit + bits(dat, pos + 5, 31) + 1;
-                pos += 14;
-                // length+distance tree
-                var ldt = new u8(tl);
-                // code length tree
-                var clt = new u8(19);
-                for (var i = 0; i < hcLen; ++i) {
-                    // use index map to get real code
-                    clt[clim[i]] = bits(dat, pos + i * 3, 7);
-                }
-                pos += hcLen * 3;
-                // code lengths bits
-                var clb = max(clt), clbmsk = (1 << clb) - 1;
-                if (!noSt && pos + tl * (clb + 7) > tbts)
-                    break;
-                // code lengths map
-                var clm = hMap(clt, clb, 1);
-                for (var i = 0; i < tl;) {
-                    var r = clm[bits(dat, pos, clbmsk)];
-                    // bits read
-                    pos += r & 15;
-                    // symbol
-                    var s = r >>> 4;
-                    // code length to copy
-                    if (s < 16) {
-                        ldt[i++] = s;
-                    }
-                    else {
-                        //  copy   count
-                        var c = 0, n = 0;
-                        if (s == 16)
-                            n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 
1];
-                        else if (s == 17)
-                            n = 3 + bits(dat, pos, 7), pos += 3;
-                        else if (s == 18)
-                            n = 11 + bits(dat, pos, 127), pos += 7;
-                        while (n--)
-                            ldt[i++] = c;
-                    }
-                }
-                //    length tree                 distance tree
-                var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
-                // max length bits
-                lbt = max(lt);
-                // max dist bits
-                dbt = max(dt);
-                lm = hMap(lt, lbt, 1);
-                dm = hMap(dt, dbt, 1);
-            }
-            else
-                throw 'invalid block type';
-            if (pos > tbts)
-                throw 'unexpected EOF';
-        }
-        // Make sure the buffer can hold this + the largest possible addition
-        // Maximum chunk size (practically, theoretically infinite) is 2^17;
-        if (noBuf)
-            cbuf(bt + 131072);
-        var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
-        var mxa = lbt + dbt + 18;
-        while (noSt || pos + mxa < tbts) {
-            // bits read, code
-            var c = lm[bits16(dat, pos) & lms], sym = c >>> 4;
-            pos += c & 15;
-            if (pos > tbts)
-                throw 'unexpected EOF';
-            if (!c)
-                throw 'invalid length/literal';
-            if (sym < 256)
-                buf[bt++] = sym;
-            else if (sym == 256) {
-                lm = null;
-                break;
-            }
-            else {
-                var add = sym - 254;
-                // no extra bits needed if less
-                if (sym > 264) {
-                    // index
-                    var i = sym - 257, b = fleb[i];
-                    add = bits(dat, pos, (1 << b) - 1) + fl[i];
-                    pos += b;
-                }
-                // dist
-                var d = dm[bits16(dat, pos) & dms], dsym = d >>> 4;
-                if (!d)
-                    throw 'invalid distance';
-                pos += d & 15;
-                var dt = fd[dsym];
-                if (dsym > 3) {
-                    var b = fdeb[dsym];
-                    dt += bits16(dat, pos) & ((1 << b) - 1), pos += b;
-                }
-                if (pos > tbts)
-                    throw 'unexpected EOF';
-                if (noBuf)
-                    cbuf(bt + 131072);
-                var end = bt + add;
-                for (; bt < end; bt += 4) {
-                    buf[bt] = buf[bt - dt];
-                    buf[bt + 1] = buf[bt + 1 - dt];
-                    buf[bt + 2] = buf[bt + 2 - dt];
-                    buf[bt + 3] = buf[bt + 3 - dt];
-                }
-                bt = end;
-            }
-        }
-        st.l = lm, st.p = pos, st.b = bt;
-        if (lm)
-            final = 1, st.m = lbt, st.d = dm, st.n = dbt;
-    } while (!final);
-    return bt == buf.length ? buf : slc(buf, 0, bt);
-};
-// starting at p, write the minimum number of bits that can hold v to d
-var wbits = function (d, p, v) {
-    v <<= p & 7;
-    var o = (p / 8) | 0;
-    d[o] |= v;
-    d[o + 1] |= v >>> 8;
-};
-// starting at p, write the minimum number of bits (>8) that can hold v to d
-var wbits16 = function (d, p, v) {
-    v <<= p & 7;
-    var o = (p / 8) | 0;
-    d[o] |= v;
-    d[o + 1] |= v >>> 8;
-    d[o + 2] |= v >>> 16;
-};
-// creates code lengths from a frequency table
-var hTree = function (d, mb) {
-    // Need extra info to make a tree
-    var t = [];
-    for (var i = 0; i < d.length; ++i) {
-        if (d[i])
-            t.push({ s: i, f: d[i] });
-    }
-    var s = t.length;
-    var t2 = t.slice();
-    if (!s)
-        return [et, 0];
-    if (s == 1) {
-        var v = new u8(t[0].s + 1);
-        v[t[0].s] = 1;
-        return [v, 1];
-    }
-    t.sort(function (a, b) { return a.f - b.f; });
-    // after i2 reaches last ind, will be stopped
-    // freq must be greater than largest possible number of symbols
-    t.push({ s: -1, f: 25001 });
-    var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;
-    t[0] = { s: -1, f: l.f + r.f, l: l, r: r };
-    // efficient algorithm from UZIP.js
-    // i0 is lookbehind, i2 is lookahead - after processing two low-freq
-    // symbols that combined have high freq, will start processing i2 
(high-freq,
-    // non-composite) symbols instead
-    // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/
-    while (i1 != s - 1) {
-        l = t[t[i0].f < t[i2].f ? i0++ : i2++];
-        r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];
-        t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };
-    }
-    var maxSym = t2[0].s;
-    for (var i = 1; i < s; ++i) {
-        if (t2[i].s > maxSym)
-            maxSym = t2[i].s;
-    }
-    // code lengths
-    var tr = new u16(maxSym + 1);
-    // max bits in tree
-    var mbt = ln(t[i1 - 1], tr, 0);
-    if (mbt > mb) {
-        // more algorithms from UZIP.js
-        // TODO: find out how this code works (debt)
-        //  ind    debt
-        var i = 0, dt = 0;
-        //    left            cost
-        var lft = mbt - mb, cst = 1 << lft;
-        t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });
-        for (; i < s; ++i) {
-            var i2_1 = t2[i].s;
-            if (tr[i2_1] > mb) {
-                dt += cst - (1 << (mbt - tr[i2_1]));
-                tr[i2_1] = mb;
-            }
-            else
-                break;
-        }
-        dt >>>= lft;
-        while (dt > 0) {
-            var i2_2 = t2[i].s;
-            if (tr[i2_2] < mb)
-                dt -= 1 << (mb - tr[i2_2]++ - 1);
-            else
-                ++i;
-        }
-        for (; i >= 0 && dt; --i) {
-            var i2_3 = t2[i].s;
-            if (tr[i2_3] == mb) {
-                --tr[i2_3];
-                ++dt;
-            }
-        }
-        mbt = mb;
-    }
-    return [new u8(tr), mbt];
-};
-// get the max length and assign length codes
-var ln = function (n, l, d) {
-    return n.s == -1
-        ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))
-        : (l[n.s] = d);
-};
-// length codes generation
-var lc = function (c) {
-    var s = c.length;
-    // Note that the semicolon was intentional
-    while (s && !c[--s])
-        ;
-    var cl = new u16(++s);
-    //  ind      num         streak
-    var cli = 0, cln = c[0], cls = 1;
-    var w = function (v) { cl[cli++] = v; };
-    for (var i = 1; i <= s; ++i) {
-        if (c[i] == cln && i != s)
-            ++cls;
-        else {
-            if (!cln && cls > 2) {
-                for (; cls > 138; cls -= 138)
-                    w(32754);
-                if (cls > 2) {
-                    w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) 
| 12305);
-                    cls = 0;
-                }
-            }
-            else if (cls > 3) {
-                w(cln), --cls;
-                for (; cls > 6; cls -= 6)
-                    w(8304);
-                if (cls > 2)
-                    w(((cls - 3) << 5) | 8208), cls = 0;
-            }
-            while (cls--)
-                w(cln);
-            cls = 1;
-            cln = c[i];
-        }
-    }
-    return [cl.subarray(0, cli), s];
-};
-// calculate the length of output from tree, code lengths
-var clen = function (cf, cl) {
-    var l = 0;
-    for (var i = 0; i < cl.length; ++i)
-        l += cf[i] * cl[i];
-    return l;
-};
-// writes a fixed block
-// returns the new bit pos
-var wfblk = function (out, pos, dat) {
-    // no need to write 00 as type: TypedArray defaults to 0
-    var s = dat.length;
-    var o = shft(pos + 2);
-    out[o] = s & 255;
-    out[o + 1] = s >>> 8;
-    out[o + 2] = out[o] ^ 255;
-    out[o + 3] = out[o + 1] ^ 255;
-    for (var i = 0; i < s; ++i)
-        out[o + i + 4] = dat[i];
-    return (o + 4 + s) * 8;
-};
-// writes a block
-var wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
-    wbits(out, p++, final);
-    ++lf[256];
-    var _a = hTree(lf, 15), dlt = _a[0], mlb = _a[1];
-    var _b = hTree(df, 15), ddt = _b[0], mdb = _b[1];
-    var _c = lc(dlt), lclt = _c[0], nlc = _c[1];
-    var _d = lc(ddt), lcdt = _d[0], ndc = _d[1];
-    var lcfreq = new u16(19);
-    for (var i = 0; i < lclt.length; ++i)
-        lcfreq[lclt[i] & 31]++;
-    for (var i = 0; i < lcdt.length; ++i)
-        lcfreq[lcdt[i] & 31]++;
-    var _e = hTree(lcfreq, 7), lct = _e[0], mlcb = _e[1];
-    var nlcc = 19;
-    for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
-        ;
-    var flen = (bl + 5) << 3;
-    var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
-    var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + 
clen(lcfreq, lct) + (2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18]);
-    if (flen <= ftlen && flen <= dtlen)
-        return wfblk(out, p, dat.subarray(bs, bs + bl));
-    var lm, ll, dm, dl;
-    wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
-    if (dtlen < ftlen) {
-        lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
-        var llm = hMap(lct, mlcb, 0);
-        wbits(out, p, nlc - 257);
-        wbits(out, p + 5, ndc - 1);
-        wbits(out, p + 10, nlcc - 4);
-        p += 14;
-        for (var i = 0; i < nlcc; ++i)
-            wbits(out, p + 3 * i, lct[clim[i]]);
-        p += 3 * nlcc;
-        var lcts = [lclt, lcdt];
-        for (var it = 0; it < 2; ++it) {
-            var clct = lcts[it];
-            for (var i = 0; i < clct.length; ++i) {
-                var len = clct[i] & 31;
-                wbits(out, p, llm[len]), p += lct[len];
-                if (len > 15)
-                    wbits(out, p, (clct[i] >>> 5) & 127), p += clct[i] >>> 12;
-            }
-        }
-    }
-    else {
-        lm = flm, ll = flt, dm = fdm, dl = fdt;
-    }
-    for (var i = 0; i < li; ++i) {
-        if (syms[i] > 255) {
-            var len = (syms[i] >>> 18) & 31;
-            wbits16(out, p, lm[len + 257]), p += ll[len + 257];
-            if (len > 7)
-                wbits(out, p, (syms[i] >>> 23) & 31), p += fleb[len];
-            var dst = syms[i] & 31;
-            wbits16(out, p, dm[dst]), p += dl[dst];
-            if (dst > 3)
-                wbits16(out, p, (syms[i] >>> 5) & 8191), p += fdeb[dst];
-        }
-        else {
-            wbits16(out, p, lm[syms[i]]), p += ll[syms[i]];
-        }
-    }
-    wbits16(out, p, lm[256]);
-    return p + ll[256];
-};
-// deflate options (nice << 13) | chain
-var deo = /*#__PURE__*/ new u32([65540, 131080, 131088, 131104, 262176, 
1048704, 1048832, 2114560, 2117632]);
-// empty
-var et = /*#__PURE__*/ new u8(0);
-// compresses data into a raw DEFLATE buffer
-var dflt = function (dat, lvl, plvl, pre, post, lst) {
-    var s = dat.length;
-    var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);
-    // writing to this writes to the output buffer
-    var w = o.subarray(pre, o.length - post);
-    var pos = 0;
-    if (!lvl || s < 8) {
-        for (var i = 0; i <= s; i += 65535) {
-            // end
-            var e = i + 65535;
-            if (e < s) {
-                // write full block
-                pos = wfblk(w, pos, dat.subarray(i, e));
-            }
-            else {
-                // write final block
-                w[i] = lst;
-                pos = wfblk(w, pos, dat.subarray(i, s));
-            }
-        }
-    }
-    else {
-        var opt = deo[lvl - 1];
-        var n = opt >>> 13, c = opt & 8191;
-        var msk_1 = (1 << plvl) - 1;
-        //    prev 2-byte val map    curr 2-byte val map
-        var prev = new u16(32768), head = new u16(msk_1 + 1);
-        var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
-        var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ 
(dat[i + 2] << bs2_1)) & msk_1; };
-        // 24576 is an arbitrary number of maximum symbols per block
-        // 424 buffer for last block
-        var syms = new u32(25000);
-        // length/literal freq   distance freq
-        var lf = new u16(288), df = new u16(32);
-        //  l/lcnt  exbits  index  l/lind  waitdx  bitpos
-        var lc_1 = 0, eb = 0, i = 0, li = 0, wi = 0, bs = 0;
-        for (; i < s; ++i) {
-            // hash value
-            var hv = hsh(i);
-            // index mod 32768
-            var imod = i & 32767;
-            // previous index with this value
-            var pimod = head[hv];
-            prev[imod] = pimod;
-            head[hv] = imod;
-            // We always should modify head and prev, but only add symbols if
-            // this data is not yet processed ("wait" for wait index)
-            if (wi <= i) {
-                // bytes remaining
-                var rem = s - i;
-                if ((lc_1 > 7000 || li > 24576) && rem > 423) {
-                    pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, 
pos);
-                    li = lc_1 = eb = 0, bs = i;
-                    for (var j = 0; j < 286; ++j)
-                        lf[j] = 0;
-                    for (var j = 0; j < 30; ++j)
-                        df[j] = 0;
-                }
-                //  len    dist   chain
-                var l = 2, d = 0, ch_1 = c, dif = (imod - pimod) & 32767;
-                if (rem > 2 && hv == hsh(i - dif)) {
-                    var maxn = Math.min(n, rem) - 1;
-                    var maxd = Math.min(32767, i);
-                    // max possible length
-                    // not capped at dif because decompressors implement 
"rolling" index population
-                    var ml = Math.min(258, rem);
-                    while (dif <= maxd && --ch_1 && imod != pimod) {
-                        if (dat[i + l] == dat[i + l - dif]) {
-                            var nl = 0;
-                            for (; nl < ml && dat[i + nl] == dat[i + nl - 
dif]; ++nl)
-                                ;
-                            if (nl > l) {
-                                l = nl, d = dif;
-                                // break out early when we reach "nice" (we 
are satisfied enough)
-                                if (nl > maxn)
-                                    break;
-                                // now, find the rarest 2-byte sequence within 
this
-                                // length of literals and search for that 
instead.
-                                // Much faster than just using the start
-                                var mmd = Math.min(dif, nl - 2);
-                                var md = 0;
-                                for (var j = 0; j < mmd; ++j) {
-                                    var ti = (i - dif + j + 32768) & 32767;
-                                    var pti = prev[ti];
-                                    var cd = (ti - pti + 32768) & 32767;
-                                    if (cd > md)
-                                        md = cd, pimod = ti;
-                                }
-                            }
-                        }
-                        // check the previous match
-                        imod = pimod, pimod = prev[imod];
-                        dif += (imod - pimod + 32768) & 32767;
-                    }
-                }
-                // d will be nonzero only when a match was found
-                if (d) {
-                    // store both dist and len data in one Uint32
-                    // Make sure this is recognized as a len/dist with 28th 
bit (2^28)
-                    syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];
-                    var lin = revfl[l] & 31, din = revfd[d] & 31;
-                    eb += fleb[lin] + fdeb[din];
-                    ++lf[257 + lin];
-                    ++df[din];
-                    wi = i + l;
-                    ++lc_1;
-                }
-                else {
-                    syms[li++] = dat[i];
-                    ++lf[dat[i]];
-                }
-            }
-        }
-        pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
-        // this is the easiest way to avoid needing to maintain state
-        if (!lst && pos & 7)
-            pos = wfblk(w, pos + 1, et);
-    }
-    return slc(o, 0, pre + shft(pos) + post);
-};
-// CRC32 table
-var crct = /*#__PURE__*/ (function () {
-    var t = new u32(256);
-    for (var i = 0; i < 256; ++i) {
-        var c = i, k = 9;
-        while (--k)
-            c = ((c & 1) && 0xEDB88320) ^ (c >>> 1);
-        t[i] = c;
-    }
-    return t;
-})();
-// CRC32
-var crc = function () {
-    var c = 0xFFFFFFFF;
-    return {
-        p: function (d) {
-            // closures have awful performance
-            var cr = c;
-            for (var i = 0; i < d.length; ++i)
-                cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);
-            c = cr;
-        },
-        d: function () { return c ^ 0xFFFFFFFF; }
-    };
-};
-// deflate with opts
-var dopt = function (dat, opt, pre, post, st) {
-    return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? 
Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : (12 + 
opt.mem), pre, post, !st);
-};
-// write bytes
-var wbytes = function (d, b, v) {
-    for (; v; ++b)
-        d[b] = v, v >>>= 8;
-};
-// gzip header
-var gzh = function (c, o) {
-    var fn = o.filename;
-    c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 
: 0, c[9] = 3; // assume Unix
-    if (o.mtime != 0)
-        wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));
-    if (fn) {
-        c[3] = 8;
-        for (var i = 0; i <= fn.length; ++i)
-            c[i + 10] = fn.charCodeAt(i);
-    }
-};
-// gzip footer: -8 to -4 = CRC, -4 to -0 is length
-// gzip start
-var gzs = function (d) {
-    if (d[0] != 31 || d[1] != 139 || d[2] != 8)
-        throw 'invalid gzip data';
-    var flg = d[3];
-    var st = 10;
-    if (flg & 4)
-        st += d[10] | (d[11] << 8) + 2;
-    for (var zs = (flg >> 3 & 1) + (flg >> 4 & 1); zs > 0; zs -= !d[st++])
-        ;
-    return st + (flg & 2);
-};
-// gzip length
-var gzl = function (d) {
-    var l = d.length;
-    return (d[l - 4] | d[l - 3] << 8 | d[l - 2] << 16) + (2 * (d[l - 1] << 
23));
-};
-// gzip header length
-var gzhl = function (o) { return 10 + ((o.filename && (o.filename.length + 1)) 
|| 0); };
-/**
- * Compresses data with GZIP
- * @param data The data to compress
- * @param opts The compression options
- * @returns The gzipped version of the data
- */
-function gzipSync(data, opts) {
-    if (opts === void 0) { opts = {}; }
-    var c = crc(), l = data.length;
-    c.p(data);
-    var d = dopt(data, opts, gzhl(opts), 8), s = d.length;
-    return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;
-}
-/**
- * Expands GZIP data
- * @param data The data to decompress
- * @param out Where to write the data. GZIP already encodes the output size, 
so providing this doesn't save memory.
- * @returns The decompressed version of the data
- */
-function gunzipSync(data, out) {
-    return inflt(data.subarray(gzs(data), -8), out || new u8(gzl(data)));
-}
-// text encoder
-typeof TextEncoder != 'undefined' && new TextEncoder();
-// text decoder
-var td = typeof TextDecoder != 'undefined' && new TextDecoder();
-// text decoder stream
-var tds = 0;
-try {
-    td.decode(et, { stream: true });
-    tds = 1;
-}
-catch (e) { }
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const defaultRetryPolicy = {
-    backoffBase: 1.5,
-    backoffDelta: { d_ms: 200 },
-};
-function updateRetryInfoTimeout(r, p = defaultRetryPolicy) {
-    const now = getTimestampNow();
-    if (now.t_ms === "never") {
-        throw Error("assertion failed");
-    }
-    if (p.backoffDelta.d_ms === "forever") {
-        r.nextRetry = { t_ms: "never" };
-        return;
-    }
-    const t = now.t_ms + p.backoffDelta.d_ms * Math.pow(p.backoffBase, 
r.retryCounter);
-    r.nextRetry = { t_ms: t };
-}
-function getRetryDuration(r, p = defaultRetryPolicy) {
-    if (!r) {
-        // If we don't have any retry info, run immediately.
-        return { d_ms: 0 };
-    }
-    if (p.backoffDelta.d_ms === "forever") {
-        return { d_ms: "forever" };
-    }
-    const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
-    return { d_ms: t };
-}
-function initRetryInfo(active = true, p = defaultRetryPolicy) {
-    const now = getTimestampNow();
-    const info = {
-        firstTry: now,
-        active: true,
-        nextRetry: now,
-        retryCounter: 0,
-    };
-    updateRetryInfoTimeout(info, p);
-    return info;
-}
-
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Account for the fees of spending a coin.
- */
-function tallyFees(tally, wireFeesPerExchange, wireFeeAmortization, 
exchangeBaseUrl, feeDeposit) {
-    var _a;
-    const currency = tally.amountPayRemaining.currency;
-    let amountWireFeeLimitRemaining = tally.amountWireFeeLimitRemaining;
-    let amountDepositFeeLimitRemaining = tally.amountDepositFeeLimitRemaining;
-    let customerDepositFees = tally.customerDepositFees;
-    let customerWireFees = tally.customerWireFees;
-    let amountPayRemaining = tally.amountPayRemaining;
-    const wireFeeCoveredForExchange = new Set(tally.wireFeeCoveredForExchange);
-    if (!tally.wireFeeCoveredForExchange.has(exchangeBaseUrl)) {
-        const wf = (_a = wireFeesPerExchange[exchangeBaseUrl]) !== null && _a 
!== void 0 ? _a : Amounts.getZero(currency);
-        const wfForgiven = Amounts.min(amountWireFeeLimitRemaining, wf);
-        amountWireFeeLimitRemaining = Amounts.sub(amountWireFeeLimitRemaining, 
wfForgiven).amount;
-        // The remaining, amortized amount needs to be paid by the
-        // wallet or covered by the deposit fee allowance.
-        let wfRemaining = Amounts.divide(Amounts.sub(wf, wfForgiven).amount, 
wireFeeAmortization);
-        // This is the amount forgiven via the deposit fee allowance.
-        const wfDepositForgiven = Amounts.min(amountDepositFeeLimitRemaining, 
wfRemaining);
-        amountDepositFeeLimitRemaining = 
Amounts.sub(amountDepositFeeLimitRemaining, wfDepositForgiven).amount;
-        wfRemaining = Amounts.sub(wfRemaining, wfDepositForgiven).amount;
-        customerWireFees = Amounts.add(customerWireFees, wfRemaining).amount;
-        amountPayRemaining = Amounts.add(amountPayRemaining, 
wfRemaining).amount;
-        wireFeeCoveredForExchange.add(exchangeBaseUrl);
-    }
-    const dfForgiven = Amounts.min(feeDeposit, amountDepositFeeLimitRemaining);
-    amountDepositFeeLimitRemaining = 
Amounts.sub(amountDepositFeeLimitRemaining, dfForgiven).amount;
-    // How much does the user spend on deposit fees for this coin?
-    const dfRemaining = Amounts.sub(feeDeposit, dfForgiven).amount;
-    customerDepositFees = Amounts.add(customerDepositFees, dfRemaining).amount;
-    amountPayRemaining = Amounts.add(amountPayRemaining, dfRemaining).amount;
-    return {
-        amountDepositFeeLimitRemaining,
-        amountPayRemaining,
-        amountWireFeeLimitRemaining,
-        customerDepositFees,
-        customerWireFees,
-        wireFeeCoveredForExchange,
-    };
-}
-/**
- * Given a list of candidate coins, select coins to spend under the merchant's
- * constraints.
- *
- * The prevPayCoins can be specified to "repair" a coin selection
- * by adding additional coins, after a broken (e.g. double-spent) coin
- * has been removed from the selection.
- *
- * This function is only exported for the sake of unit tests.
- */
-function selectPayCoins(req) {
-    var _a;
-    const { candidates, contractTermsAmount, depositFeeLimit, wireFeeLimit, 
wireFeeAmortization, } = req;
-    if (candidates.candidateCoins.length === 0) {
-        return undefined;
-    }
-    const coinPubs = [];
-    const coinContributions = [];
-    const currency = contractTermsAmount.currency;
-    let tally = {
-        amountPayRemaining: contractTermsAmount,
-        amountWireFeeLimitRemaining: wireFeeLimit,
-        amountDepositFeeLimitRemaining: depositFeeLimit,
-        customerDepositFees: Amounts.getZero(currency),
-        customerWireFees: Amounts.getZero(currency),
-        wireFeeCoveredForExchange: new Set(),
-    };
-    const prevPayCoins = (_a = req.prevPayCoins) !== null && _a !== void 0 ? 
_a : [];
-    // Look at existing pay coin selection and tally up
-    for (const prev of prevPayCoins) {
-        tally = tallyFees(tally, candidates.wireFeesPerExchange, 
wireFeeAmortization, prev.exchangeBaseUrl, prev.feeDeposit);
-        tally.amountPayRemaining = Amounts.sub(tally.amountPayRemaining, 
prev.contribution).amount;
-        coinPubs.push(prev.coinPub);
-        coinContributions.push(prev.contribution);
-    }
-    const prevCoinPubs = new Set(prevPayCoins.map((x) => x.coinPub));
-    // Sort by available amount (descending),  deposit fee (ascending) and
-    // denomPub (ascending) if deposit fee is the same
-    // (to guarantee deterministic results)
-    const candidateCoins = [...candidates.candidateCoins].sort((o1, o2) => 
-Amounts.cmp(o1.availableAmount, o2.availableAmount) ||
-        Amounts.cmp(o1.feeDeposit, o2.feeDeposit) ||
-        strcmp(o1.denomPub, o2.denomPub));
-    // FIXME:  Here, we should select coins in a smarter way.
-    // Instead of always spending the next-largest coin,
-    // we should try to find the smallest coin that covers the
-    // amount.
-    for (const aci of candidateCoins) {
-        // Don't use this coin if depositing it is more expensive than
-        // the amount it would give the merchant.
-        if (Amounts.cmp(aci.feeDeposit, aci.availableAmount) > 0) {
-            continue;
-        }
-        if (Amounts.isZero(tally.amountPayRemaining)) {
-            // We have spent enough!
-            break;
-        }
-        // The same coin can't contribute twice to the same payment,
-        // by a fundamental, intentional limitation of the protocol.
-        if (prevCoinPubs.has(aci.coinPub)) {
-            continue;
-        }
-        tally = tallyFees(tally, candidates.wireFeesPerExchange, 
wireFeeAmortization, aci.exchangeBaseUrl, aci.feeDeposit);
-        let coinSpend = Amounts.max(Amounts.min(tally.amountPayRemaining, 
aci.availableAmount), aci.feeDeposit);
-        tally.amountPayRemaining = Amounts.sub(tally.amountPayRemaining, 
coinSpend).amount;
-        coinPubs.push(aci.coinPub);
-        coinContributions.push(coinSpend);
-    }
-    if (Amounts.isZero(tally.amountPayRemaining)) {
-        return {
-            paymentAmount: contractTermsAmount,
-            coinContributions,
-            coinPubs,
-            customerDepositFees: tally.customerDepositFees,
-            customerWireFees: tally.customerWireFees,
-        };
-    }
-    return undefined;
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-function assertUnreachable(x) {
-    throw new Error("Didn't expect to get here");
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019-2021 Taler Systems SA
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Logger for this file.
- */
-const logger$h = new Logger("withdraw.ts");
-/**
- * Check if a denom is withdrawable based on the expiration time
- * and revocation state.
- */
-function isWithdrawableDenom(d) {
-    const now = getTimestampNow();
-    const started = timestampCmp(now, d.stampStart) >= 0;
-    let lastPossibleWithdraw;
-    {
-        lastPossibleWithdraw = 
timestampSubtractDuraction(d.stampExpireWithdraw, durationFromSpec({ minutes: 5 
}));
-    }
-    const remaining = getDurationRemaining(lastPossibleWithdraw, now);
-    const stillOkay = remaining.d_ms !== 0;
-    return started && stillOkay && !d.isRevoked;
-}
-/**
- * Get a list of denominations (with repetitions possible)
- * whose total value is as close as possible to the available
- * amount, but never larger.
- */
-function selectWithdrawalDenominations(amountAvailable, denoms) {
-    let remaining = Amounts.copy(amountAvailable);
-    const selectedDenoms = [];
-    let totalCoinValue = Amounts.getZero(amountAvailable.currency);
-    let totalWithdrawCost = Amounts.getZero(amountAvailable.currency);
-    denoms = denoms.filter(isWithdrawableDenom);
-    denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value));
-    for (const d of denoms) {
-        let count = 0;
-        const cost = Amounts.add(d.value, d.feeWithdraw).amount;
-        for (;;) {
-            if (Amounts.cmp(remaining, cost) < 0) {
-                break;
-            }
-            remaining = Amounts.sub(remaining, cost).amount;
-            count++;
-        }
-        if (count > 0) {
-            totalCoinValue = Amounts.add(totalCoinValue, Amounts.mult(d.value, 
count).amount).amount;
-            totalWithdrawCost = Amounts.add(totalWithdrawCost, 
Amounts.mult(cost, count).amount).amount;
-            selectedDenoms.push({
-                count,
-                denom: d,
-            });
-        }
-        if (Amounts.isZero(remaining)) {
-            break;
-        }
-    }
-    return {
-        selectedDenoms,
-        totalCoinValue,
-        totalWithdrawCost,
-    };
-}
-/**
- * Get information about a withdrawal from
- * a taler://withdraw URI by asking the bank.
- */
-async function getBankWithdrawalInfo(ws, talerWithdrawUri) {
-    const uriResult = parseWithdrawUri(talerWithdrawUri);
-    if (!uriResult) {
-        throw Error(`can't parse URL ${talerWithdrawUri}`);
-    }
-    const configReqUrl = new URL$1("config", 
uriResult.bankIntegrationApiBaseUrl);
-    const configResp = await ws.http.get(configReqUrl.href);
-    const config = await readSuccessResponseJsonOrThrow(configResp, 
codecForTalerConfigResponse());
-    const versionRes = compare(WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, 
config.version);
-    if ((versionRes === null || versionRes === void 0 ? void 0 : 
versionRes.compatible) != true) {
-        const opErr = 
makeErrorDetails(TalerErrorCode.WALLET_BANK_INTEGRATION_PROTOCOL_VERSION_INCOMPATIBLE,
 "bank integration protocol version not compatible with wallet", {
-            exchangeProtocolVersion: config.version,
-            walletProtocolVersion: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
-        });
-        throw new OperationFailedError(opErr);
-    }
-    const reqUrl = new 
URL$1(`withdrawal-operation/${uriResult.withdrawalOperationId}`, 
uriResult.bankIntegrationApiBaseUrl);
-    const resp = await ws.http.get(reqUrl.href);
-    const status = await readSuccessResponseJsonOrThrow(resp, 
codecForWithdrawOperationStatusResponse());
-    return {
-        amount: Amounts.parseOrThrow(status.amount),
-        confirmTransferUrl: status.confirm_transfer_url,
-        extractedStatusUrl: reqUrl.href,
-        selectionDone: status.selection_done,
-        senderWire: status.sender_wire,
-        suggestedExchange: status.suggested_exchange,
-        transferDone: status.transfer_done,
-        wireTypes: status.wire_types,
-    };
-}
-/**
- * Return denominations that can potentially used for a withdrawal.
- */
-async function getCandidateWithdrawalDenoms(ws, exchangeBaseUrl) {
-    return await ws.db
-        .mktx((x) => ({ denominations: x.denominations }))
-        .runReadOnly(async (tx) => {
-        return tx.denominations.indexes.byExchangeBaseUrl
-            .iter(exchangeBaseUrl)
-            .filter((d) => {
-            return ((d.status === DenominationStatus.Unverified ||
-                d.status === DenominationStatus.VerifiedGood) &&
-                !d.isRevoked);
-        });
-    });
-}
-/**
- * Generate a planchet for a coin index in a withdrawal group.
- * Does not actually withdraw the coin yet.
- *
- * Split up so that we can parallelize the crypto, but serialize
- * the exchange requests per reserve.
- */
-async function processPlanchetGenerate(ws, withdrawalGroupId, coinIdx) {
-    const withdrawalGroup = await ws.db
-        .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
-        .runReadOnly(async (tx) => {
-        return await tx.withdrawalGroups.get(withdrawalGroupId);
-    });
-    if (!withdrawalGroup) {
-        return;
-    }
-    let planchet = await ws.db
-        .mktx((x) => ({
-        planchets: x.planchets,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.planchets.indexes.byGroupAndIndex.get([
-            withdrawalGroupId,
-            coinIdx,
-        ]);
-    });
-    if (!planchet) {
-        let ci = 0;
-        let denomPubHash;
-        for (let di = 0; di < withdrawalGroup.denomsSel.selectedDenoms.length; 
di++) {
-            const d = withdrawalGroup.denomsSel.selectedDenoms[di];
-            if (coinIdx >= ci && coinIdx < ci + d.count) {
-                denomPubHash = d.denomPubHash;
-                break;
-            }
-            ci += d.count;
-        }
-        if (!denomPubHash) {
-            throw Error("invariant violated");
-        }
-        const { denom, reserve } = await ws.db
-            .mktx((x) => ({
-            reserves: x.reserves,
-            denominations: x.denominations,
-        }))
-            .runReadOnly(async (tx) => {
-            const denom = await tx.denominations.get([
-                withdrawalGroup.exchangeBaseUrl,
-                denomPubHash,
-            ]);
-            if (!denom) {
-                throw Error("invariant violated");
-            }
-            const reserve = await tx.reserves.get(withdrawalGroup.reservePub);
-            if (!reserve) {
-                throw Error("invariant violated");
-            }
-            return { denom, reserve };
-        });
-        const r = await ws.cryptoApi.createPlanchet({
-            denomPub: denom.denomPub,
-            feeWithdraw: denom.feeWithdraw,
-            reservePriv: reserve.reservePriv,
-            reservePub: reserve.reservePub,
-            value: denom.value,
-            coinIndex: coinIdx,
-            secretSeed: withdrawalGroup.secretSeed,
-        });
-        const newPlanchet = {
-            blindingKey: r.blindingKey,
-            coinEv: r.coinEv,
-            coinEvHash: r.coinEvHash,
-            coinIdx,
-            coinPriv: r.coinPriv,
-            coinPub: r.coinPub,
-            coinValue: r.coinValue,
-            denomPub: r.denomPub,
-            denomPubHash: r.denomPubHash,
-            isFromTip: false,
-            reservePub: r.reservePub,
-            withdrawalDone: false,
-            withdrawSig: r.withdrawSig,
-            withdrawalGroupId: withdrawalGroupId,
-            lastError: undefined,
-        };
-        await ws.db
-            .mktx((x) => ({ planchets: x.planchets }))
-            .runReadWrite(async (tx) => {
-            const p = await tx.planchets.indexes.byGroupAndIndex.get([
-                withdrawalGroupId,
-                coinIdx,
-            ]);
-            if (p) {
-                planchet = p;
-                return;
-            }
-            await tx.planchets.put(newPlanchet);
-            planchet = newPlanchet;
-        });
-    }
-}
-/**
- * Send the withdrawal request for a generated planchet to the exchange.
- *
- * The verification of the response is done asynchronously to enable 
parallelism.
- */
-async function processPlanchetExchangeRequest(ws, withdrawalGroupId, coinIdx) {
-    const d = await ws.db
-        .mktx((x) => ({
-        withdrawalGroups: x.withdrawalGroups,
-        planchets: x.planchets,
-        exchanges: x.exchanges,
-        denominations: x.denominations,
-    }))
-        .runReadOnly(async (tx) => {
-        const withdrawalGroup = await 
tx.withdrawalGroups.get(withdrawalGroupId);
-        if (!withdrawalGroup) {
-            return;
-        }
-        let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
-            withdrawalGroupId,
-            coinIdx,
-        ]);
-        if (!planchet) {
-            return;
-        }
-        if (planchet.withdrawalDone) {
-            logger$h.warn("processPlanchet: planchet already withdrawn");
-            return;
-        }
-        const exchange = await 
tx.exchanges.get(withdrawalGroup.exchangeBaseUrl);
-        if (!exchange) {
-            logger$h.error("db inconsistent: exchange for planchet not found");
-            return;
-        }
-        const denom = await tx.denominations.get([
-            withdrawalGroup.exchangeBaseUrl,
-            planchet.denomPubHash,
-        ]);
-        if (!denom) {
-            console.error("db inconsistent: denom for planchet not found");
-            return;
-        }
-        logger$h.trace(`processing planchet #${coinIdx} in withdrawal 
${withdrawalGroupId}`);
-        const reqBody = {
-            denom_pub_hash: planchet.denomPubHash,
-            reserve_pub: planchet.reservePub,
-            reserve_sig: planchet.withdrawSig,
-            coin_ev: planchet.coinEv,
-        };
-        const reqUrl = new URL$1(`reserves/${planchet.reservePub}/withdraw`, 
exchange.baseUrl).href;
-        return { reqUrl, reqBody };
-    });
-    if (!d) {
-        return;
-    }
-    const { reqUrl, reqBody } = d;
-    try {
-        const resp = await ws.http.postJson(reqUrl, reqBody);
-        const r = await readSuccessResponseJsonOrThrow(resp, 
codecForWithdrawResponse());
-        return r;
-    }
-    catch (e) {
-        logger$h.trace("withdrawal request failed", e);
-        logger$h.trace(e);
-        if (!(e instanceof OperationFailedError)) {
-            throw e;
-        }
-        const errDetails = e.operationError;
-        await ws.db
-            .mktx((x) => ({ planchets: x.planchets }))
-            .runReadWrite(async (tx) => {
-            let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
-                withdrawalGroupId,
-                coinIdx,
-            ]);
-            if (!planchet) {
-                return;
-            }
-            planchet.lastError = errDetails;
-            await tx.planchets.put(planchet);
-        });
-        return;
-    }
-}
-async function processPlanchetVerifyAndStoreCoin(ws, withdrawalGroupId, 
coinIdx, resp) {
-    const d = await ws.db
-        .mktx((x) => ({
-        withdrawalGroups: x.withdrawalGroups,
-        planchets: x.planchets,
-    }))
-        .runReadOnly(async (tx) => {
-        const withdrawalGroup = await 
tx.withdrawalGroups.get(withdrawalGroupId);
-        if (!withdrawalGroup) {
-            return;
-        }
-        let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
-            withdrawalGroupId,
-            coinIdx,
-        ]);
-        if (!planchet) {
-            return;
-        }
-        if (planchet.withdrawalDone) {
-            logger$h.warn("processPlanchet: planchet already withdrawn");
-            return;
-        }
-        return { planchet, exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl };
-    });
-    if (!d) {
-        return;
-    }
-    const { planchet, exchangeBaseUrl } = d;
-    const denomSig = await ws.cryptoApi.rsaUnblind(resp.ev_sig, 
planchet.blindingKey, planchet.denomPub);
-    const isValid = await ws.cryptoApi.rsaVerify(planchet.coinPub, denomSig, 
planchet.denomPub);
-    if (!isValid) {
-        await ws.db
-            .mktx((x) => ({ planchets: x.planchets }))
-            .runReadWrite(async (tx) => {
-            let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
-                withdrawalGroupId,
-                coinIdx,
-            ]);
-            if (!planchet) {
-                return;
-            }
-            planchet.lastError = 
makeErrorDetails(TalerErrorCode.WALLET_EXCHANGE_COIN_SIGNATURE_INVALID, 
"invalid signature from the exchange after unblinding", {});
-            await tx.planchets.put(planchet);
-        });
-        return;
-    }
-    const coin = {
-        blindingKey: planchet.blindingKey,
-        coinPriv: planchet.coinPriv,
-        coinPub: planchet.coinPub,
-        currentAmount: planchet.coinValue,
-        denomPub: planchet.denomPub,
-        denomPubHash: planchet.denomPubHash,
-        denomSig,
-        coinEvHash: planchet.coinEvHash,
-        exchangeBaseUrl: exchangeBaseUrl,
-        status: CoinStatus.Fresh,
-        coinSource: {
-            type: CoinSourceType.Withdraw,
-            coinIndex: coinIdx,
-            reservePub: planchet.reservePub,
-            withdrawalGroupId: withdrawalGroupId,
-        },
-        suspended: false,
-    };
-    const planchetCoinPub = planchet.coinPub;
-    const firstSuccess = await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        withdrawalGroups: x.withdrawalGroups,
-        reserves: x.reserves,
-        planchets: x.planchets,
-    }))
-        .runReadWrite(async (tx) => {
-        const ws = await tx.withdrawalGroups.get(withdrawalGroupId);
-        if (!ws) {
-            return false;
-        }
-        const p = await tx.planchets.get(planchetCoinPub);
-        if (!p || p.withdrawalDone) {
-            return false;
-        }
-        p.withdrawalDone = true;
-        await tx.planchets.put(p);
-        await tx.coins.add(coin);
-        return true;
-    });
-    if (firstSuccess) {
-        ws.notify({
-            type: NotificationType.CoinWithdrawn,
-        });
-    }
-}
-function denomSelectionInfoToState(dsi) {
-    return {
-        selectedDenoms: dsi.selectedDenoms.map((x) => {
-            return {
-                count: x.count,
-                denomPubHash: x.denom.denomPubHash,
-            };
-        }),
-        totalCoinValue: dsi.totalCoinValue,
-        totalWithdrawCost: dsi.totalWithdrawCost,
-    };
-}
-/**
- * Make sure that denominations that currently can be used for withdrawal
- * are validated, and the result of validation is stored in the database.
- */
-async function updateWithdrawalDenoms(ws, exchangeBaseUrl) {
-    const exchangeDetails = await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-    }))
-        .runReadOnly(async (tx) => {
-        return ws.exchangeOps.getExchangeDetails(tx, exchangeBaseUrl);
-    });
-    if (!exchangeDetails) {
-        logger$h.error("exchange details not available");
-        throw Error(`exchange ${exchangeBaseUrl} details not available`);
-    }
-    // First do a pass where the validity of candidate denominations
-    // is checked and the result is stored in the database.
-    const denominations = await getCandidateWithdrawalDenoms(ws, 
exchangeBaseUrl);
-    for (const denom of denominations) {
-        if (denom.status === DenominationStatus.Unverified) {
-            const valid = await ws.cryptoApi.isValidDenom(denom, 
exchangeDetails.masterPublicKey);
-            if (!valid) {
-                logger$h.warn(`Signature check for denomination 
h=${denom.denomPubHash} failed`);
-                denom.status = DenominationStatus.VerifiedBad;
-            }
-            else {
-                denom.status = DenominationStatus.VerifiedGood;
-            }
-            await ws.db
-                .mktx((x) => ({ denominations: x.denominations }))
-                .runReadWrite(async (tx) => {
-                await tx.denominations.put(denom);
-            });
-        }
-    }
-    // FIXME:  This debug info should either be made conditional on some flag
-    // or put into some wallet-core API.
-    const nextDenominations = await getCandidateWithdrawalDenoms(ws, 
exchangeBaseUrl);
-    logger$h.trace(`updated withdrawable denominations for 
"${exchangeBaseUrl}, n=${nextDenominations.length}"`);
-    const now = getTimestampNow();
-    for (const denom of nextDenominations) {
-        const startDelay = getDurationRemaining(denom.stampStart, now);
-        const lastPossibleWithdraw = 
timestampSubtractDuraction(denom.stampExpireWithdraw, { d_ms: 50 * 1000 });
-        const remaining = getDurationRemaining(lastPossibleWithdraw, now);
-        logger$h.trace(`Denom ${denom.denomPubHash} ${denom.status} revoked 
${denom.isRevoked} offered ${denom.isOffered} remaining ${remaining.d_ms / 
1000}sec withdrawDelay ${startDelay.d_ms / 1000}sec`);
-    }
-}
-async function incrementWithdrawalRetry(ws, withdrawalGroupId, err) {
-    await ws.db
-        .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
-        .runReadWrite(async (tx) => {
-        const wsr = await tx.withdrawalGroups.get(withdrawalGroupId);
-        if (!wsr) {
-            return;
-        }
-        wsr.retryInfo.retryCounter++;
-        updateRetryInfoTimeout(wsr.retryInfo);
-        wsr.lastError = err;
-        await tx.withdrawalGroups.put(wsr);
-    });
-    if (err) {
-        ws.notify({ type: NotificationType.WithdrawOperationError, error: err 
});
-    }
-}
-async function processWithdrawGroup(ws, withdrawalGroupId, forceNow = false) {
-    const onOpErr = (e) => incrementWithdrawalRetry(ws, withdrawalGroupId, e);
-    await guardOperationException(() => processWithdrawGroupImpl(ws, 
withdrawalGroupId, forceNow), onOpErr);
-}
-async function resetWithdrawalGroupRetry(ws, withdrawalGroupId) {
-    await ws.db
-        .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
-        .runReadWrite(async (tx) => {
-        const x = await tx.withdrawalGroups.get(withdrawalGroupId);
-        if (x) {
-            x.retryInfo = initRetryInfo();
-            await tx.withdrawalGroups.put(x);
-        }
-    });
-}
-async function processWithdrawGroupImpl(ws, withdrawalGroupId, forceNow) {
-    logger$h.trace("processing withdraw group", withdrawalGroupId);
-    if (forceNow) {
-        await resetWithdrawalGroupRetry(ws, withdrawalGroupId);
-    }
-    const withdrawalGroup = await ws.db
-        .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
-        .runReadOnly(async (tx) => {
-        return tx.withdrawalGroups.get(withdrawalGroupId);
-    });
-    if (!withdrawalGroup) {
-        logger$h.trace("withdraw session doesn't exist");
-        return;
-    }
-    await ws.exchangeOps.updateExchangeFromUrl(ws, 
withdrawalGroup.exchangeBaseUrl);
-    const numTotalCoins = withdrawalGroup.denomsSel.selectedDenoms
-        .map((x) => x.count)
-        .reduce((a, b) => a + b);
-    let work = [];
-    for (let i = 0; i < numTotalCoins; i++) {
-        work.push(processPlanchetGenerate(ws, withdrawalGroupId, i));
-    }
-    // Generate coins concurrently (parallelism only happens in the crypto API 
workers)
-    await Promise.all(work);
-    work = [];
-    for (let coinIdx = 0; coinIdx < numTotalCoins; coinIdx++) {
-        const resp = await processPlanchetExchangeRequest(ws, 
withdrawalGroupId, coinIdx);
-        if (!resp) {
-            continue;
-        }
-        work.push(processPlanchetVerifyAndStoreCoin(ws, withdrawalGroupId, 
coinIdx, resp));
-    }
-    await Promise.all(work);
-    let numFinished = 0;
-    let finishedForFirstTime = false;
-    let errorsPerCoin = {};
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        withdrawalGroups: x.withdrawalGroups,
-        reserves: x.reserves,
-        planchets: x.planchets,
-    }))
-        .runReadWrite(async (tx) => {
-        const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
-        if (!wg) {
-            return;
-        }
-        await tx.planchets.indexes.byGroup
-            .iter(withdrawalGroupId)
-            .forEach((x) => {
-            if (x.withdrawalDone) {
-                numFinished++;
-            }
-            if (x.lastError) {
-                errorsPerCoin[x.coinIdx] = x.lastError;
-            }
-        });
-        logger$h.trace(`now withdrawn ${numFinished} of ${numTotalCoins} 
coins`);
-        if (wg.timestampFinish === undefined && numFinished === numTotalCoins) 
{
-            finishedForFirstTime = true;
-            wg.timestampFinish = getTimestampNow();
-            wg.lastError = undefined;
-            wg.retryInfo = initRetryInfo(false);
-        }
-        await tx.withdrawalGroups.put(wg);
-    });
-    if (numFinished != numTotalCoins) {
-        throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_WITHDRAWAL_GROUP_INCOMPLETE,
 `withdrawal did not finish (${numFinished} / ${numTotalCoins} coins 
withdrawn)`, {
-            errorsPerCoin,
-        });
-    }
-    if (finishedForFirstTime) {
-        ws.notify({
-            type: NotificationType.WithdrawGroupFinished,
-            reservePub: withdrawalGroup.reservePub,
-        });
-    }
-}
-async function getExchangeWithdrawalInfo(ws, baseUrl, amount) {
-    const { exchange, exchangeDetails, } = await 
ws.exchangeOps.updateExchangeFromUrl(ws, baseUrl);
-    await updateWithdrawalDenoms(ws, baseUrl);
-    const denoms = await getCandidateWithdrawalDenoms(ws, baseUrl);
-    const selectedDenoms = selectWithdrawalDenominations(amount, denoms);
-    const exchangeWireAccounts = [];
-    for (const account of exchangeDetails.wireInfo.accounts) {
-        exchangeWireAccounts.push(account.payto_uri);
-    }
-    const { isTrusted, isAudited } = await ws.exchangeOps.getExchangeTrust(ws, 
exchange);
-    let earliestDepositExpiration = 
selectedDenoms.selectedDenoms[0].denom.stampExpireDeposit;
-    for (let i = 1; i < selectedDenoms.selectedDenoms.length; i++) {
-        const expireDeposit = 
selectedDenoms.selectedDenoms[i].denom.stampExpireDeposit;
-        if (expireDeposit.t_ms < earliestDepositExpiration.t_ms) {
-            earliestDepositExpiration = expireDeposit;
-        }
-    }
-    const possibleDenoms = await ws.db
-        .mktx((x) => ({ denominations: x.denominations }))
-        .runReadOnly(async (tx) => {
-        return tx.denominations.indexes.byExchangeBaseUrl
-            .iter()
-            .filter((d) => d.isOffered);
-    });
-    let versionMatch;
-    if (exchangeDetails.protocolVersion) {
-        versionMatch = compare(WALLET_EXCHANGE_PROTOCOL_VERSION, 
exchangeDetails.protocolVersion);
-        if (versionMatch &&
-            !versionMatch.compatible &&
-            versionMatch.currentCmp === -1) {
-            console.warn(`wallet's support for exchange protocol version 
${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
-                `(exchange has ${exchangeDetails.protocolVersion}), checking 
for updates`);
-        }
-    }
-    let tosAccepted = false;
-    if (exchangeDetails.termsOfServiceLastEtag) {
-        if (exchangeDetails.termsOfServiceAcceptedEtag ===
-            exchangeDetails.termsOfServiceLastEtag) {
-            tosAccepted = true;
-        }
-    }
-    const withdrawFee = Amounts.sub(selectedDenoms.totalWithdrawCost, 
selectedDenoms.totalCoinValue).amount;
-    const ret = {
-        earliestDepositExpiration,
-        exchangeInfo: exchange,
-        exchangeDetails,
-        exchangeWireAccounts,
-        exchangeVersion: exchangeDetails.protocolVersion || "unknown",
-        isAudited,
-        isTrusted,
-        numOfferedDenoms: possibleDenoms.length,
-        overhead: Amounts.sub(amount, selectedDenoms.totalWithdrawCost).amount,
-        selectedDenoms,
-        // FIXME: delete this field / replace by something we can display to 
the user
-        trustedAuditorPubs: [],
-        versionMatch,
-        walletVersion: WALLET_EXCHANGE_PROTOCOL_VERSION,
-        withdrawFee,
-        termsOfServiceAccepted: tosAccepted,
-    };
-    return ret;
-}
-async function getWithdrawalDetailsForUri(ws, talerWithdrawUri) {
-    logger$h.trace(`getting withdrawal details for URI ${talerWithdrawUri}`);
-    const info = await getBankWithdrawalInfo(ws, talerWithdrawUri);
-    logger$h.trace(`got bank info`);
-    if (info.suggestedExchange) {
-        // FIXME: right now the exchange gets permanently added,
-        // we might want to only temporarily add it.
-        try {
-            await ws.exchangeOps.updateExchangeFromUrl(ws, 
info.suggestedExchange);
-        }
-        catch (e) {
-            // We still continued if it failed, as other exchanges might be 
available.
-            // We don't want to fail if the bank-suggested exchange is 
broken/offline.
-            logger$h.trace(`querying bank-suggested exchange 
(${info.suggestedExchange}) failed`);
-        }
-    }
-    const exchanges = [];
-    await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-    }))
-        .runReadOnly(async (tx) => {
-        const exchangeRecords = await tx.exchanges.iter().toArray();
-        for (const r of exchangeRecords) {
-            const details = await ws.exchangeOps.getExchangeDetails(tx, 
r.baseUrl);
-            if (details) {
-                exchanges.push({
-                    exchangeBaseUrl: details.exchangeBaseUrl,
-                    currency: details.currency,
-                    paytoUris: details.wireInfo.accounts.map((x) => 
x.payto_uri),
-                });
-            }
-        }
-    });
-    return {
-        amount: Amounts.stringify(info.amount),
-        defaultExchangeBaseUrl: info.suggestedExchange,
-        possibleExchanges: exchanges,
-    };
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$g = new Logger("reserves.ts");
-async function resetReserveRetry(ws, reservePub) {
-    await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadWrite(async (tx) => {
-        const x = await tx.reserves.get(reservePub);
-        if (x) {
-            x.retryInfo = initRetryInfo();
-            await tx.reserves.put(x);
-        }
-    });
-}
-/**
- * Create a reserve, but do not flag it as confirmed yet.
- *
- * Adds the corresponding exchange as a trusted exchange if it is neither
- * audited nor trusted already.
- */
-async function createReserve(ws, req) {
-    const keypair = await ws.cryptoApi.createEddsaKeypair();
-    const now = getTimestampNow();
-    const canonExchange = canonicalizeBaseUrl(req.exchange);
-    let reserveStatus;
-    if (req.bankWithdrawStatusUrl) {
-        reserveStatus = ReserveRecordStatus.REGISTERING_BANK;
-    }
-    else {
-        reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
-    }
-    let bankInfo;
-    if (req.bankWithdrawStatusUrl) {
-        if (!req.exchangePaytoUri) {
-            throw Error("Exchange payto URI must be specified for a 
bank-integrated withdrawal");
-        }
-        bankInfo = {
-            statusUrl: req.bankWithdrawStatusUrl,
-            exchangePaytoUri: req.exchangePaytoUri,
-        };
-    }
-    const initialWithdrawalGroupId = encodeCrock(getRandomBytes(32));
-    await updateWithdrawalDenoms(ws, canonExchange);
-    const denoms = await getCandidateWithdrawalDenoms(ws, canonExchange);
-    const denomSelInfo = selectWithdrawalDenominations(req.amount, denoms);
-    const initialDenomSel = denomSelectionInfoToState(denomSelInfo);
-    const reserveRecord = {
-        instructedAmount: req.amount,
-        initialWithdrawalGroupId,
-        initialDenomSel,
-        initialWithdrawalStarted: false,
-        timestampCreated: now,
-        exchangeBaseUrl: canonExchange,
-        reservePriv: keypair.priv,
-        reservePub: keypair.pub,
-        senderWire: req.senderWire,
-        timestampBankConfirmed: undefined,
-        timestampReserveInfoPosted: undefined,
-        bankInfo,
-        reserveStatus,
-        lastSuccessfulStatusQuery: undefined,
-        retryInfo: initRetryInfo(),
-        lastError: undefined,
-        currency: req.amount.currency,
-        requestedQuery: false,
-    };
-    const exchangeInfo = await updateExchangeFromUrl(ws, req.exchange);
-    const exchangeDetails = exchangeInfo.exchangeDetails;
-    if (!exchangeDetails) {
-        logger$g.trace(exchangeDetails);
-        throw Error("exchange not updated");
-    }
-    const { isAudited, isTrusted } = await getExchangeTrust(ws, 
exchangeInfo.exchange);
-    const resp = await ws.db
-        .mktx((x) => ({
-        exchangeTrust: x.exchangeTrust,
-        reserves: x.reserves,
-        bankWithdrawUris: x.bankWithdrawUris,
-    }))
-        .runReadWrite(async (tx) => {
-        var _a;
-        // Check if we have already created a reserve for that 
bankWithdrawStatusUrl
-        if ((_a = reserveRecord.bankInfo) === null || _a === void 0 ? void 0 : 
_a.statusUrl) {
-            const bwi = await 
tx.bankWithdrawUris.get(reserveRecord.bankInfo.statusUrl);
-            if (bwi) {
-                const otherReserve = await tx.reserves.get(bwi.reservePub);
-                if (otherReserve) {
-                    logger$g.trace("returning existing reserve for 
bankWithdrawStatusUri");
-                    return {
-                        exchange: otherReserve.exchangeBaseUrl,
-                        reservePub: otherReserve.reservePub,
-                    };
-                }
-            }
-            await tx.bankWithdrawUris.put({
-                reservePub: reserveRecord.reservePub,
-                talerWithdrawUri: reserveRecord.bankInfo.statusUrl,
-            });
-        }
-        if (!isAudited && !isTrusted) {
-            await tx.exchangeTrust.put({
-                currency: reserveRecord.currency,
-                exchangeBaseUrl: reserveRecord.exchangeBaseUrl,
-                exchangeMasterPub: exchangeDetails.masterPublicKey,
-                uids: [encodeCrock(getRandomBytes(32))],
-            });
-        }
-        await tx.reserves.put(reserveRecord);
-        const r = {
-            exchange: canonExchange,
-            reservePub: keypair.pub,
-        };
-        return r;
-    });
-    if (reserveRecord.reservePub === resp.reservePub) {
-        // Only emit notification when a new reserve was created.
-        ws.notify({
-            type: NotificationType.ReserveCreated,
-            reservePub: reserveRecord.reservePub,
-        });
-    }
-    // Asynchronously process the reserve, but return
-    // to the caller already.
-    processReserve(ws, resp.reservePub, true).catch((e) => {
-        logger$g.error("Processing reserve (after createReserve) failed:", e);
-    });
-    return resp;
-}
-/**
- * First fetch information required to withdraw from the reserve,
- * then deplete the reserve, withdrawing coins until it is empty.
- *
- * The returned promise resolves once the reserve is set to the
- * state DORMANT.
- */
-async function processReserve(ws, reservePub, forceNow = false) {
-    return ws.memoProcessReserve.memo(reservePub, async () => {
-        const onOpError = (err) => incrementReserveRetry(ws, reservePub, err);
-        await guardOperationException(() => processReserveImpl(ws, reservePub, 
forceNow), onOpError);
-    });
-}
-async function registerReserveWithBank(ws, reservePub) {
-    const reserve = await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadOnly(async (tx) => {
-        return await tx.reserves.get(reservePub);
-    });
-    switch (reserve === null || reserve === void 0 ? void 0 : 
reserve.reserveStatus) {
-        case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-        case ReserveRecordStatus.REGISTERING_BANK:
-            break;
-        default:
-            return;
-    }
-    const bankInfo = reserve.bankInfo;
-    if (!bankInfo) {
-        return;
-    }
-    const bankStatusUrl = bankInfo.statusUrl;
-    const httpResp = await ws.http.postJson(bankStatusUrl, {
-        reserve_pub: reservePub,
-        selected_exchange: bankInfo.exchangePaytoUri,
-    }, {
-        timeout: getReserveRequestTimeout(reserve),
-    });
-    await readSuccessResponseJsonOrThrow(httpResp, 
codecForBankWithdrawalOperationPostResponse());
-    await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadWrite(async (tx) => {
-        const r = await tx.reserves.get(reservePub);
-        if (!r) {
-            return;
-        }
-        switch (r.reserveStatus) {
-            case ReserveRecordStatus.REGISTERING_BANK:
-            case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-                break;
-            default:
-                return;
-        }
-        r.timestampReserveInfoPosted = getTimestampNow();
-        r.reserveStatus = ReserveRecordStatus.WAIT_CONFIRM_BANK;
-        if (!r.bankInfo) {
-            throw Error("invariant failed");
-        }
-        r.retryInfo = initRetryInfo();
-        await tx.reserves.put(r);
-    });
-    ws.notify({ type: NotificationType.ReserveRegisteredWithBank });
-    return processReserveBankStatus(ws, reservePub);
-}
-async function processReserveBankStatus(ws, reservePub) {
-    const onOpError = (err) => incrementReserveRetry(ws, reservePub, err);
-    await guardOperationException(() => processReserveBankStatusImpl(ws, 
reservePub), onOpError);
-}
-function getReserveRequestTimeout(r) {
-    return durationMax({ d_ms: 60000 }, durationMin({ d_ms: 5000 }, 
getRetryDuration(r.retryInfo)));
-}
-async function processReserveBankStatusImpl(ws, reservePub) {
-    var _a;
-    const reserve = await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.reserves.get(reservePub);
-    });
-    switch (reserve === null || reserve === void 0 ? void 0 : 
reserve.reserveStatus) {
-        case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-        case ReserveRecordStatus.REGISTERING_BANK:
-            break;
-        default:
-            return;
-    }
-    const bankStatusUrl = (_a = reserve.bankInfo) === null || _a === void 0 ? 
void 0 : _a.statusUrl;
-    if (!bankStatusUrl) {
-        return;
-    }
-    const statusResp = await ws.http.get(bankStatusUrl, {
-        timeout: getReserveRequestTimeout(reserve),
-    });
-    const status = await readSuccessResponseJsonOrThrow(statusResp, 
codecForWithdrawOperationStatusResponse());
-    if (status.aborted) {
-        logger$g.trace("bank aborted the withdrawal");
-        await ws.db
-            .mktx((x) => ({
-            reserves: x.reserves,
-        }))
-            .runReadWrite(async (tx) => {
-            const r = await tx.reserves.get(reservePub);
-            if (!r) {
-                return;
-            }
-            switch (r.reserveStatus) {
-                case ReserveRecordStatus.REGISTERING_BANK:
-                case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-                    break;
-                default:
-                    return;
-            }
-            const now = getTimestampNow();
-            r.timestampBankConfirmed = now;
-            r.reserveStatus = ReserveRecordStatus.BANK_ABORTED;
-            r.retryInfo = initRetryInfo();
-            await tx.reserves.put(r);
-        });
-        return;
-    }
-    if (status.selection_done) {
-        if (reserve.reserveStatus === ReserveRecordStatus.REGISTERING_BANK) {
-            await registerReserveWithBank(ws, reservePub);
-            return await processReserveBankStatus(ws, reservePub);
-        }
-    }
-    else {
-        await registerReserveWithBank(ws, reservePub);
-        return await processReserveBankStatus(ws, reservePub);
-    }
-    await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadWrite(async (tx) => {
-        const r = await tx.reserves.get(reservePub);
-        if (!r) {
-            return;
-        }
-        if (status.transfer_done) {
-            switch (r.reserveStatus) {
-                case ReserveRecordStatus.REGISTERING_BANK:
-                case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-                    break;
-                default:
-                    return;
-            }
-            const now = getTimestampNow();
-            r.timestampBankConfirmed = now;
-            r.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
-            r.retryInfo = initRetryInfo();
-        }
-        else {
-            switch (r.reserveStatus) {
-                case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-                    break;
-                default:
-                    return;
-            }
-            if (r.bankInfo) {
-                r.bankInfo.confirmUrl = status.confirm_transfer_url;
-            }
-        }
-        await tx.reserves.put(r);
-    });
-}
-async function incrementReserveRetry(ws, reservePub, err) {
-    await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadWrite(async (tx) => {
-        const r = await tx.reserves.get(reservePub);
-        if (!r) {
-            return;
-        }
-        if (!r.retryInfo) {
-            return;
-        }
-        r.retryInfo.retryCounter++;
-        updateRetryInfoTimeout(r.retryInfo);
-        r.lastError = err;
-        await tx.reserves.put(r);
-    });
-    if (err) {
-        ws.notify({
-            type: NotificationType.ReserveOperationError,
-            error: err,
-        });
-    }
-}
-/**
- * Update the information about a reserve that is stored in the wallet
- * by querying the reserve's exchange.
- *
- * If the reserve have funds that are not allocated in a withdrawal group yet
- * and are big enough to withdraw with available denominations,
- * create a new withdrawal group for the remaining amount.
- */
-async function updateReserve(ws, reservePub) {
-    const reserve = await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.reserves.get(reservePub);
-    });
-    if (!reserve) {
-        throw Error("reserve not in db");
-    }
-    if (reserve.reserveStatus !== ReserveRecordStatus.QUERYING_STATUS) {
-        return { ready: true };
-    }
-    const resp = await ws.http.get(new URL$1(`reserves/${reservePub}`, 
reserve.exchangeBaseUrl).href, {
-        timeout: getReserveRequestTimeout(reserve),
-    });
-    const result = await readSuccessResponseJsonOrErrorCode(resp, 
codecForReserveStatus());
-    if (result.isError) {
-        if (resp.status === 404 &&
-            result.talerErrorResponse.code ===
-                TalerErrorCode.EXCHANGE_RESERVES_GET_STATUS_UNKNOWN) {
-            ws.notify({
-                type: NotificationType.ReserveNotYetFound,
-                reservePub,
-            });
-            await incrementReserveRetry(ws, reservePub, undefined);
-            return { ready: false };
-        }
-        else {
-            throwUnexpectedRequestError(resp, result.talerErrorResponse);
-        }
-    }
-    const reserveInfo = result.response;
-    const balance = Amounts.parseOrThrow(reserveInfo.balance);
-    const currency = balance.currency;
-    await updateWithdrawalDenoms(ws, reserve.exchangeBaseUrl);
-    const denoms = await getCandidateWithdrawalDenoms(ws, 
reserve.exchangeBaseUrl);
-    const newWithdrawalGroup = await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        planchets: x.planchets,
-        withdrawalGroups: x.withdrawalGroups,
-        reserves: x.reserves,
-    }))
-        .runReadWrite(async (tx) => {
-        const newReserve = await tx.reserves.get(reserve.reservePub);
-        if (!newReserve) {
-            return;
-        }
-        let amountReservePlus = Amounts.getZero(currency);
-        let amountReserveMinus = Amounts.getZero(currency);
-        // Subtract withdrawal groups for this reserve from the available 
amount.
-        await tx.withdrawalGroups.indexes.byReservePub
-            .iter(reservePub)
-            .forEach((wg) => {
-            const cost = wg.denomsSel.totalWithdrawCost;
-            amountReserveMinus = Amounts.add(amountReserveMinus, cost).amount;
-        });
-        for (const entry of reserveInfo.history) {
-            switch (entry.type) {
-                case ReserveTransactionType.Credit:
-                    amountReservePlus = Amounts.add(amountReservePlus, 
Amounts.parseOrThrow(entry.amount)).amount;
-                    break;
-                case ReserveTransactionType.Recoup:
-                    amountReservePlus = Amounts.add(amountReservePlus, 
Amounts.parseOrThrow(entry.amount)).amount;
-                    break;
-                case ReserveTransactionType.Closing:
-                    amountReserveMinus = Amounts.add(amountReserveMinus, 
Amounts.parseOrThrow(entry.amount)).amount;
-                    break;
-                case ReserveTransactionType.Withdraw: {
-                    // Now we check if the withdrawal transaction
-                    // is part of any withdrawal known to this wallet.
-                    const planchet = await 
tx.planchets.indexes.byCoinEvHash.get(entry.h_coin_envelope);
-                    if (planchet) {
-                        // Amount is already accounted in some withdrawal 
session
-                        break;
-                    }
-                    const coin = await 
tx.coins.indexes.byCoinEvHash.get(entry.h_coin_envelope);
-                    if (coin) {
-                        // Amount is already accounted in some withdrawal 
session
-                        break;
-                    }
-                    // Amount has been claimed by some withdrawal we don't 
know about
-                    amountReserveMinus = Amounts.add(amountReserveMinus, 
Amounts.parseOrThrow(entry.amount)).amount;
-                    break;
-                }
-            }
-        }
-        const remainingAmount = Amounts.sub(amountReservePlus, 
amountReserveMinus)
-            .amount;
-        const denomSelInfo = selectWithdrawalDenominations(remainingAmount, 
denoms);
-        logger$g.trace(`Remaining unclaimed amount in reseve is 
${Amounts.stringify(remainingAmount)} and can be withdrawn with 
${denomSelInfo.selectedDenoms.length} coins`);
-        if (denomSelInfo.selectedDenoms.length === 0) {
-            newReserve.reserveStatus = ReserveRecordStatus.DORMANT;
-            newReserve.lastError = undefined;
-            newReserve.retryInfo = initRetryInfo(false);
-            await tx.reserves.put(newReserve);
-            return;
-        }
-        let withdrawalGroupId;
-        if (!newReserve.initialWithdrawalStarted) {
-            withdrawalGroupId = newReserve.initialWithdrawalGroupId;
-            newReserve.initialWithdrawalStarted = true;
-        }
-        else {
-            withdrawalGroupId = encodeCrock(randomBytes(32));
-        }
-        const withdrawalRecord = {
-            withdrawalGroupId: withdrawalGroupId,
-            exchangeBaseUrl: reserve.exchangeBaseUrl,
-            reservePub: reserve.reservePub,
-            rawWithdrawalAmount: remainingAmount,
-            timestampStart: getTimestampNow(),
-            retryInfo: initRetryInfo(),
-            lastError: undefined,
-            denomsSel: denomSelectionInfoToState(denomSelInfo),
-            secretSeed: encodeCrock(getRandomBytes(64)),
-            denomSelUid: encodeCrock(getRandomBytes(32)),
-        };
-        newReserve.lastError = undefined;
-        newReserve.retryInfo = initRetryInfo(false);
-        newReserve.reserveStatus = ReserveRecordStatus.DORMANT;
-        await tx.reserves.put(newReserve);
-        await tx.withdrawalGroups.put(withdrawalRecord);
-        return withdrawalRecord;
-    });
-    if (newWithdrawalGroup) {
-        logger$g.trace("processing new withdraw group");
-        ws.notify({
-            type: NotificationType.WithdrawGroupCreated,
-            withdrawalGroupId: newWithdrawalGroup.withdrawalGroupId,
-        });
-        await processWithdrawGroup(ws, newWithdrawalGroup.withdrawalGroupId);
-    }
-    return { ready: true };
-}
-async function processReserveImpl(ws, reservePub, forceNow = false) {
-    const reserve = await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.reserves.get(reservePub);
-    });
-    if (!reserve) {
-        logger$g.trace("not processing reserve: reserve does not exist");
-        return;
-    }
-    if (!forceNow) {
-        const now = getTimestampNow();
-        if (reserve.retryInfo.nextRetry.t_ms > now.t_ms) {
-            logger$g.trace("processReserve retry not due yet");
-            return;
-        }
-    }
-    else {
-        await resetReserveRetry(ws, reservePub);
-    }
-    logger$g.trace(`Processing reserve ${reservePub} with status 
${reserve.reserveStatus}`);
-    switch (reserve.reserveStatus) {
-        case ReserveRecordStatus.REGISTERING_BANK:
-            await processReserveBankStatus(ws, reservePub);
-            return await processReserveImpl(ws, reservePub, true);
-        case ReserveRecordStatus.QUERYING_STATUS:
-            const res = await updateReserve(ws, reservePub);
-            if (res.ready) {
-                return await processReserveImpl(ws, reservePub, true);
-            }
-            break;
-        case ReserveRecordStatus.DORMANT:
-            // nothing to do
-            break;
-        case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-            await processReserveBankStatus(ws, reservePub);
-            break;
-        case ReserveRecordStatus.BANK_ABORTED:
-            break;
-        default:
-            console.warn("unknown reserve record status:", 
reserve.reserveStatus);
-            assertUnreachable(reserve.reserveStatus);
-            break;
-    }
-}
-async function createTalerWithdrawReserve(ws, talerWithdrawUri, 
selectedExchange) {
-    await updateExchangeFromUrl(ws, selectedExchange);
-    const withdrawInfo = await getBankWithdrawalInfo(ws, talerWithdrawUri);
-    const exchangePaytoUri = await getExchangePaytoUri(ws, selectedExchange, 
withdrawInfo.wireTypes);
-    const reserve = await createReserve(ws, {
-        amount: withdrawInfo.amount,
-        bankWithdrawStatusUrl: withdrawInfo.extractedStatusUrl,
-        exchange: selectedExchange,
-        senderWire: withdrawInfo.senderWire,
-        exchangePaytoUri: exchangePaytoUri,
-    });
-    // We do this here, as the reserve should be registered before we return,
-    // so that we can redirect the user to the bank's status page.
-    await processReserveBankStatus(ws, reserve.reservePub);
-    const processedReserve = await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.reserves.get(reserve.reservePub);
-    });
-    if ((processedReserve === null || processedReserve === void 0 ? void 0 : 
processedReserve.reserveStatus) === ReserveRecordStatus.BANK_ABORTED) {
-        throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
 "withdrawal aborted by bank", {});
-    }
-    return {
-        reservePub: reserve.reservePub,
-        confirmTransferUrl: withdrawInfo.confirmTransferUrl,
-    };
-}
-/**
- * Get payto URIs needed to fund a reserve.
- */
-async function getFundingPaytoUris(tx, reservePub) {
-    var _a, _b;
-    const r = await tx.reserves.get(reservePub);
-    if (!r) {
-        logger$g.error(`reserve ${reservePub} not found (DB corrupted?)`);
-        return [];
-    }
-    const exchangeDetails = await getExchangeDetails(tx, r.exchangeBaseUrl);
-    if (!exchangeDetails) {
-        logger$g.error(`exchange ${r.exchangeBaseUrl} not found (DB 
corrupted?)`);
-        return [];
-    }
-    const plainPaytoUris = (_b = (_a = exchangeDetails.wireInfo) === null || 
_a === void 0 ? void 0 : _a.accounts.map((x) => x.payto_uri)) !== null && _b 
!== void 0 ? _b : [];
-    if (!plainPaytoUris) {
-        logger$g.error(`exchange ${r.exchangeBaseUrl} has no wire info`);
-        return [];
-    }
-    return plainPaytoUris.map((x) => addPaytoQueryParams(x, {
-        amount: Amounts.stringify(r.instructedAmount),
-        message: `Taler Withdrawal ${r.reservePub}`,
-    }));
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019-2020 Taler Systems SA
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$f = new Logger("operations/recoup.ts");
-async function incrementRecoupRetry(ws, recoupGroupId, err) {
-    await ws.db
-        .mktx((x) => ({
-        recoupGroups: x.recoupGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const r = await tx.recoupGroups.get(recoupGroupId);
-        if (!r) {
-            return;
-        }
-        if (!r.retryInfo) {
-            return;
-        }
-        r.retryInfo.retryCounter++;
-        updateRetryInfoTimeout(r.retryInfo);
-        r.lastError = err;
-        await tx.recoupGroups.put(r);
-    });
-    if (err) {
-        ws.notify({ type: NotificationType.RecoupOperationError, error: err });
-    }
-}
-async function putGroupAsFinished(ws, tx, recoupGroup, coinIdx) {
-    logger$f.trace(`setting coin ${coinIdx} of ${recoupGroup.coinPubs.length} 
as finished`);
-    if (recoupGroup.timestampFinished) {
-        return;
-    }
-    recoupGroup.recoupFinishedPerCoin[coinIdx] = true;
-    let allFinished = true;
-    for (const b of recoupGroup.recoupFinishedPerCoin) {
-        if (!b) {
-            allFinished = false;
-        }
-    }
-    if (allFinished) {
-        logger$f.trace("all recoups of recoup group are finished");
-        recoupGroup.timestampFinished = getTimestampNow();
-        recoupGroup.retryInfo = initRetryInfo(false);
-        recoupGroup.lastError = undefined;
-        if (recoupGroup.scheduleRefreshCoins.length > 0) {
-            const refreshGroupId = await createRefreshGroup(ws, tx, 
recoupGroup.scheduleRefreshCoins.map((x) => ({ coinPub: x })), 
RefreshReason.Recoup);
-            processRefreshGroup(ws, refreshGroupId.refreshGroupId).then((e) => 
{
-                console.error("error while refreshing after recoup", e);
-            });
-        }
-    }
-    await tx.recoupGroups.put(recoupGroup);
-}
-async function recoupTipCoin(ws, recoupGroupId, coinIdx, coin) {
-    // We can't really recoup a coin we got via tipping.
-    // Thus we just put the coin to sleep.
-    // FIXME: somehow report this to the user
-    await ws.db
-        .mktx((x) => ({
-        recoupGroups: x.recoupGroups,
-        denominations: WalletStoresV1.denominations,
-        refreshGroups: WalletStoresV1.refreshGroups,
-        coins: WalletStoresV1.coins,
-    }))
-        .runReadWrite(async (tx) => {
-        const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
-        if (!recoupGroup) {
-            return;
-        }
-        if (recoupGroup.recoupFinishedPerCoin[coinIdx]) {
-            return;
-        }
-        await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
-    });
-}
-async function recoupWithdrawCoin(ws, recoupGroupId, coinIdx, coin, cs) {
-    const reservePub = cs.reservePub;
-    const reserve = await ws.db
-        .mktx((x) => ({
-        reserves: x.reserves,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.reserves.get(reservePub);
-    });
-    if (!reserve) {
-        // FIXME:  We should at least emit some pending operation / warning 
for this?
-        return;
-    }
-    ws.notify({
-        type: NotificationType.RecoupStarted,
-    });
-    const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
-    const reqUrl = new URL$1(`/coins/${coin.coinPub}/recoup`, 
coin.exchangeBaseUrl);
-    const resp = await ws.http.postJson(reqUrl.href, recoupRequest, {
-        timeout: getReserveRequestTimeout(reserve),
-    });
-    const recoupConfirmation = await readSuccessResponseJsonOrThrow(resp, 
codecForRecoupConfirmation());
-    if (recoupConfirmation.reserve_pub !== reservePub) {
-        throw Error(`Coin's reserve doesn't match reserve on recoup`);
-    }
-    // FIXME: verify that our expectations about the amount match
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        denominations: x.denominations,
-        reserves: x.reserves,
-        recoupGroups: x.recoupGroups,
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
-        if (!recoupGroup) {
-            return;
-        }
-        if (recoupGroup.recoupFinishedPerCoin[coinIdx]) {
-            return;
-        }
-        const updatedCoin = await tx.coins.get(coin.coinPub);
-        if (!updatedCoin) {
-            return;
-        }
-        const updatedReserve = await tx.reserves.get(reserve.reservePub);
-        if (!updatedReserve) {
-            return;
-        }
-        updatedCoin.status = CoinStatus.Dormant;
-        const currency = updatedCoin.currentAmount.currency;
-        updatedCoin.currentAmount = Amounts.getZero(currency);
-        if (updatedReserve.reserveStatus === ReserveRecordStatus.DORMANT) {
-            updatedReserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
-            updatedReserve.retryInfo = initRetryInfo();
-        }
-        else {
-            updatedReserve.requestedQuery = true;
-            updatedReserve.retryInfo = initRetryInfo();
-        }
-        await tx.coins.put(updatedCoin);
-        await tx.reserves.put(updatedReserve);
-        await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
-    });
-    ws.notify({
-        type: NotificationType.RecoupFinished,
-    });
-}
-async function recoupRefreshCoin(ws, recoupGroupId, coinIdx, coin, cs) {
-    ws.notify({
-        type: NotificationType.RecoupStarted,
-    });
-    const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
-    const reqUrl = new URL$1(`/coins/${coin.coinPub}/recoup`, 
coin.exchangeBaseUrl);
-    logger$f.trace(`making recoup request for ${coin.coinPub}`);
-    const resp = await ws.http.postJson(reqUrl.href, recoupRequest);
-    const recoupConfirmation = await readSuccessResponseJsonOrThrow(resp, 
codecForRecoupConfirmation());
-    if (recoupConfirmation.old_coin_pub != cs.oldCoinPub) {
-        throw Error(`Coin's oldCoinPub doesn't match reserve on recoup`);
-    }
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        denominations: x.denominations,
-        reserves: x.reserves,
-        recoupGroups: x.recoupGroups,
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
-        if (!recoupGroup) {
-            return;
-        }
-        if (recoupGroup.recoupFinishedPerCoin[coinIdx]) {
-            return;
-        }
-        const oldCoin = await tx.coins.get(cs.oldCoinPub);
-        const revokedCoin = await tx.coins.get(coin.coinPub);
-        if (!revokedCoin) {
-            logger$f.warn("revoked coin for recoup not found");
-            return;
-        }
-        if (!oldCoin) {
-            logger$f.warn("refresh old coin for recoup not found");
-            return;
-        }
-        revokedCoin.status = CoinStatus.Dormant;
-        oldCoin.currentAmount = Amounts.add(oldCoin.currentAmount, 
recoupGroup.oldAmountPerCoin[coinIdx]).amount;
-        logger$f.trace("recoup: setting old coin amount to", 
Amounts.stringify(oldCoin.currentAmount));
-        recoupGroup.scheduleRefreshCoins.push(oldCoin.coinPub);
-        await tx.coins.put(revokedCoin);
-        await tx.coins.put(oldCoin);
-        await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
-    });
-}
-async function resetRecoupGroupRetry(ws, recoupGroupId) {
-    await ws.db
-        .mktx((x) => ({
-        recoupGroups: x.recoupGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const x = await tx.recoupGroups.get(recoupGroupId);
-        if (x) {
-            x.retryInfo = initRetryInfo();
-            await tx.recoupGroups.put(x);
-        }
-    });
-}
-async function processRecoupGroup(ws, recoupGroupId, forceNow = false) {
-    await ws.memoProcessRecoup.memo(recoupGroupId, async () => {
-        const onOpErr = (e) => incrementRecoupRetry(ws, recoupGroupId, e);
-        return await guardOperationException(async () => await 
processRecoupGroupImpl(ws, recoupGroupId, forceNow), onOpErr);
-    });
-}
-async function processRecoupGroupImpl(ws, recoupGroupId, forceNow = false) {
-    if (forceNow) {
-        await resetRecoupGroupRetry(ws, recoupGroupId);
-    }
-    const recoupGroup = await ws.db
-        .mktx((x) => ({
-        recoupGroups: x.recoupGroups,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.recoupGroups.get(recoupGroupId);
-    });
-    if (!recoupGroup) {
-        return;
-    }
-    if (recoupGroup.timestampFinished) {
-        logger$f.trace("recoup group finished");
-        return;
-    }
-    const ps = recoupGroup.coinPubs.map((x, i) => processRecoup(ws, 
recoupGroupId, i));
-    await Promise.all(ps);
-    const reserveSet = new Set();
-    for (let i = 0; i < recoupGroup.coinPubs.length; i++) {
-        const coinPub = recoupGroup.coinPubs[i];
-        const coin = await ws.db
-            .mktx((x) => ({
-            coins: x.coins,
-        }))
-            .runReadOnly(async (tx) => {
-            return tx.coins.get(coinPub);
-        });
-        if (!coin) {
-            throw Error(`Coin ${coinPub} not found, can't request recoup`);
-        }
-        if (coin.coinSource.type === CoinSourceType.Withdraw) {
-            reserveSet.add(coin.coinSource.reservePub);
-        }
-    }
-    for (const r of reserveSet.values()) {
-        processReserve(ws, r).catch((e) => {
-            logger$f.error(`processing reserve ${r} after recoup failed`);
-        });
-    }
-}
-async function createRecoupGroup(ws, tx, coinPubs) {
-    const recoupGroupId = encodeCrock(getRandomBytes(32));
-    const recoupGroup = {
-        recoupGroupId,
-        coinPubs: coinPubs,
-        lastError: undefined,
-        timestampFinished: undefined,
-        timestampStarted: getTimestampNow(),
-        retryInfo: initRetryInfo(),
-        recoupFinishedPerCoin: coinPubs.map(() => false),
-        // Will be populated later
-        oldAmountPerCoin: [],
-        scheduleRefreshCoins: [],
-    };
-    for (let coinIdx = 0; coinIdx < coinPubs.length; coinIdx++) {
-        const coinPub = coinPubs[coinIdx];
-        const coin = await tx.coins.get(coinPub);
-        if (!coin) {
-            await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
-            continue;
-        }
-        if (Amounts.isZero(coin.currentAmount)) {
-            await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
-            continue;
-        }
-        recoupGroup.oldAmountPerCoin[coinIdx] = coin.currentAmount;
-        coin.currentAmount = Amounts.getZero(coin.currentAmount.currency);
-        await tx.coins.put(coin);
-    }
-    await tx.recoupGroups.put(recoupGroup);
-    return recoupGroupId;
-}
-async function processRecoup(ws, recoupGroupId, coinIdx) {
-    const coin = await ws.db
-        .mktx((x) => ({
-        recoupGroups: x.recoupGroups,
-        coins: x.coins,
-    }))
-        .runReadOnly(async (tx) => {
-        const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
-        if (!recoupGroup) {
-            return;
-        }
-        if (recoupGroup.timestampFinished) {
-            return;
-        }
-        if (recoupGroup.recoupFinishedPerCoin[coinIdx]) {
-            return;
-        }
-        const coinPub = recoupGroup.coinPubs[coinIdx];
-        const coin = await tx.coins.get(coinPub);
-        if (!coin) {
-            throw Error(`Coin ${coinPub} not found, can't request payback`);
-        }
-        return coin;
-    });
-    if (!coin) {
-        return;
-    }
-    const cs = coin.coinSource;
-    switch (cs.type) {
-        case CoinSourceType.Tip:
-            return recoupTipCoin(ws, recoupGroupId, coinIdx);
-        case CoinSourceType.Refresh:
-            return recoupRefreshCoin(ws, recoupGroupId, coinIdx, coin, cs);
-        case CoinSourceType.Withdraw:
-            return recoupWithdrawCoin(ws, recoupGroupId, coinIdx, coin, cs);
-        default:
-            throw Error("unknown coin source type");
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$e = new Logger("exchanges.ts");
-function denominationRecordFromKeys(exchangeBaseUrl, exchangeMasterPub, 
denomIn) {
-    const denomPubHash = encodeCrock(hash(decodeCrock(denomIn.denom_pub)));
-    const d = {
-        denomPub: denomIn.denom_pub,
-        denomPubHash,
-        exchangeBaseUrl,
-        exchangeMasterPub,
-        feeDeposit: Amounts.parseOrThrow(denomIn.fee_deposit),
-        feeRefresh: Amounts.parseOrThrow(denomIn.fee_refresh),
-        feeRefund: Amounts.parseOrThrow(denomIn.fee_refund),
-        feeWithdraw: Amounts.parseOrThrow(denomIn.fee_withdraw),
-        isOffered: true,
-        isRevoked: false,
-        masterSig: denomIn.master_sig,
-        stampExpireDeposit: denomIn.stamp_expire_deposit,
-        stampExpireLegal: denomIn.stamp_expire_legal,
-        stampExpireWithdraw: denomIn.stamp_expire_withdraw,
-        stampStart: denomIn.stamp_start,
-        status: DenominationStatus.Unverified,
-        value: Amounts.parseOrThrow(denomIn.value),
-    };
-    return d;
-}
-async function handleExchangeUpdateError(ws, baseUrl, err) {
-    await ws.db
-        .mktx((x) => ({ exchanges: x.exchanges }))
-        .runReadOnly(async (tx) => {
-        const exchange = await tx.exchanges.get(baseUrl);
-        if (!exchange) {
-            return;
-        }
-        exchange.retryInfo.retryCounter++;
-        updateRetryInfoTimeout(exchange.retryInfo);
-        exchange.lastError = err;
-    });
-    if (err) {
-        ws.notify({ type: NotificationType.ExchangeOperationError, error: err 
});
-    }
-}
-function getExchangeRequestTimeout(e) {
-    return { d_ms: 5000 };
-}
-async function downloadExchangeWithTermsOfService(exchangeBaseUrl, http, 
timeout) {
-    const reqUrl = new URL$1("terms", exchangeBaseUrl);
-    reqUrl.searchParams.set("cacheBreaker", 
WALLET_CACHE_BREAKER_CLIENT_VERSION);
-    const headers = {
-        Accept: "text/plain",
-    };
-    const resp = await http.get(reqUrl.href, {
-        headers,
-        timeout,
-    });
-    const tosText = await readSuccessResponseTextOrThrow(resp);
-    const tosEtag = resp.headers.get("etag") || "unknown";
-    return { tosText, tosEtag };
-}
-async function getExchangeDetails(tx, exchangeBaseUrl) {
-    const r = await tx.exchanges.get(exchangeBaseUrl);
-    if (!r) {
-        return;
-    }
-    const dp = r.detailsPointer;
-    if (!dp) {
-        return;
-    }
-    const { currency, masterPublicKey } = dp;
-    return await tx.exchangeDetails.get([r.baseUrl, currency, 
masterPublicKey]);
-}
-getExchangeDetails.makeContext = (db) => db.mktx((x) => ({
-    exchanges: x.exchanges,
-    exchangeDetails: x.exchangeDetails,
-}));
-async function acceptExchangeTermsOfService(ws, exchangeBaseUrl, etag) {
-    await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-    }))
-        .runReadWrite(async (tx) => {
-        const d = await getExchangeDetails(tx, exchangeBaseUrl);
-        if (d) {
-            d.termsOfServiceAcceptedEtag = etag;
-            await tx.exchangeDetails.put(d);
-        }
-    });
-}
-async function validateWireInfo(wireInfo, masterPublicKey, cryptoApi) {
-    for (const a of wireInfo.accounts) {
-        logger$e.trace("validating exchange acct");
-        const isValid = await cryptoApi.isValidWireAccount(a.payto_uri, 
a.master_sig, masterPublicKey);
-        if (!isValid) {
-            throw Error("exchange acct signature invalid");
-        }
-    }
-    const feesForType = {};
-    for (const wireMethod of Object.keys(wireInfo.fees)) {
-        const feeList = [];
-        for (const x of wireInfo.fees[wireMethod]) {
-            const startStamp = x.start_date;
-            const endStamp = x.end_date;
-            const fee = {
-                closingFee: Amounts.parseOrThrow(x.closing_fee),
-                endStamp,
-                sig: x.sig,
-                startStamp,
-                wireFee: Amounts.parseOrThrow(x.wire_fee),
-            };
-            const isValid = await cryptoApi.isValidWireFee(wireMethod, fee, 
masterPublicKey);
-            if (!isValid) {
-                throw Error("exchange wire fee signature invalid");
-            }
-            feeList.push(fee);
-        }
-        feesForType[wireMethod] = feeList;
-    }
-    return {
-        accounts: wireInfo.accounts,
-        feesForType,
-    };
-}
-/**
- * Fetch wire information for an exchange.
- *
- * @param exchangeBaseUrl Exchange base URL, assumed to be already normalized.
- */
-async function downloadExchangeWithWireInfo(exchangeBaseUrl, http, timeout) {
-    const reqUrl = new URL$1("wire", exchangeBaseUrl);
-    reqUrl.searchParams.set("cacheBreaker", 
WALLET_CACHE_BREAKER_CLIENT_VERSION);
-    const resp = await http.get(reqUrl.href, {
-        timeout,
-    });
-    const wireInfo = await readSuccessResponseJsonOrThrow(resp, 
codecForExchangeWireJson());
-    return wireInfo;
-}
-async function updateExchangeFromUrl(ws, baseUrl, forceNow = false) {
-    const onOpErr = (e) => handleExchangeUpdateError(ws, baseUrl, e);
-    return await guardOperationException(() => updateExchangeFromUrlImpl(ws, 
baseUrl, forceNow), onOpErr);
-}
-async function provideExchangeRecord(ws, baseUrl, now) {
-    return await ws.db
-        .mktx((x) => ({ exchanges: x.exchanges }))
-        .runReadWrite(async (tx) => {
-        let r = await tx.exchanges.get(baseUrl);
-        if (!r) {
-            r = {
-                permanent: true,
-                baseUrl: baseUrl,
-                retryInfo: initRetryInfo(false),
-                detailsPointer: undefined,
-                lastUpdate: undefined,
-                nextUpdate: now,
-                nextRefreshCheck: now,
-            };
-            await tx.exchanges.put(r);
-        }
-        return r;
-    });
-}
-/**
- * Download and validate an exchange's /keys data.
- */
-async function downloadKeysInfo(baseUrl, http, timeout) {
-    var _a;
-    const keysUrl = new URL$1("keys", baseUrl);
-    keysUrl.searchParams.set("cacheBreaker", 
WALLET_CACHE_BREAKER_CLIENT_VERSION);
-    const resp = await http.get(keysUrl.href, {
-        timeout,
-    });
-    const exchangeKeysJson = await readSuccessResponseJsonOrThrow(resp, 
codecForExchangeKeysJson());
-    logger$e.info("received /keys response");
-    logger$e.trace(j2s(exchangeKeysJson));
-    if (exchangeKeysJson.denoms.length === 0) {
-        const opErr = 
makeErrorDetails(TalerErrorCode.WALLET_EXCHANGE_DENOMINATIONS_INSUFFICIENT, 
"exchange doesn't offer any denominations", {
-            exchangeBaseUrl: baseUrl,
-        });
-        throw new OperationFailedError(opErr);
-    }
-    const protocolVersion = exchangeKeysJson.version;
-    const versionRes = compare(WALLET_EXCHANGE_PROTOCOL_VERSION, 
protocolVersion);
-    if ((versionRes === null || versionRes === void 0 ? void 0 : 
versionRes.compatible) != true) {
-        const opErr = 
makeErrorDetails(TalerErrorCode.WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE, 
"exchange protocol version not compatible with wallet", {
-            exchangeProtocolVersion: protocolVersion,
-            walletProtocolVersion: WALLET_EXCHANGE_PROTOCOL_VERSION,
-        });
-        throw new OperationFailedError(opErr);
-    }
-    const currency = 
Amounts.parseOrThrow(exchangeKeysJson.denoms[0].value).currency.toUpperCase();
-    return {
-        masterPublicKey: exchangeKeysJson.master_public_key,
-        currency,
-        auditors: exchangeKeysJson.auditors,
-        currentDenominations: exchangeKeysJson.denoms.map((d) => 
denominationRecordFromKeys(baseUrl, exchangeKeysJson.master_public_key, d)),
-        protocolVersion: exchangeKeysJson.version,
-        signingKeys: exchangeKeysJson.signkeys,
-        reserveClosingDelay: exchangeKeysJson.reserve_closing_delay,
-        expiry: getExpiryTimestamp(resp, {
-            minDuration: durationFromSpec({ hours: 1 }),
-        }),
-        recoup: (_a = exchangeKeysJson.recoup) !== null && _a !== void 0 ? _a 
: [],
-    };
-}
-/**
- * Update or add exchange DB entry by fetching the /keys and /wire information.
- * Optionally link the reserve entry to the new or existing
- * exchange entry in then DB.
- */
-async function updateExchangeFromUrlImpl(ws, baseUrl, forceNow = false) {
-    logger$e.trace(`updating exchange info for ${baseUrl}`);
-    const now = getTimestampNow();
-    baseUrl = canonicalizeBaseUrl(baseUrl);
-    const r = await provideExchangeRecord(ws, baseUrl, now);
-    if (!forceNow && r && !isTimestampExpired(r.nextUpdate)) {
-        const res = await ws.db
-            .mktx((x) => ({
-            exchanges: x.exchanges,
-            exchangeDetails: x.exchangeDetails,
-        }))
-            .runReadOnly(async (tx) => {
-            const exchange = await tx.exchanges.get(baseUrl);
-            if (!exchange) {
-                return;
-            }
-            const exchangeDetails = await getExchangeDetails(tx, baseUrl);
-            if (!exchangeDetails) {
-                return;
-            }
-            return { exchange, exchangeDetails };
-        });
-        if (res) {
-            logger$e.info("using existing exchange info");
-            return res;
-        }
-    }
-    logger$e.info("updating exchange /keys info");
-    const timeout = getExchangeRequestTimeout();
-    const keysInfo = await downloadKeysInfo(baseUrl, ws.http, timeout);
-    const wireInfoDownload = await downloadExchangeWithWireInfo(baseUrl, 
ws.http, timeout);
-    const wireInfo = await validateWireInfo(wireInfoDownload, 
keysInfo.masterPublicKey, ws.cryptoApi);
-    const tosDownload = await downloadExchangeWithTermsOfService(baseUrl, 
ws.http, timeout);
-    let recoupGroupId = undefined;
-    const updated = await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-        denominations: x.denominations,
-        coins: x.coins,
-        refreshGroups: x.refreshGroups,
-        recoupGroups: x.recoupGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const r = await tx.exchanges.get(baseUrl);
-        if (!r) {
-            logger$e.warn(`exchange ${baseUrl} no longer present`);
-            return;
-        }
-        let details = await getExchangeDetails(tx, r.baseUrl);
-        // FIXME: validate signing keys and merge with old set
-        details = {
-            auditors: keysInfo.auditors,
-            currency: keysInfo.currency,
-            masterPublicKey: keysInfo.masterPublicKey,
-            protocolVersion: keysInfo.protocolVersion,
-            signingKeys: keysInfo.signingKeys,
-            reserveClosingDelay: keysInfo.reserveClosingDelay,
-            exchangeBaseUrl: r.baseUrl,
-            wireInfo,
-            termsOfServiceText: tosDownload.tosText,
-            termsOfServiceAcceptedEtag: undefined,
-            termsOfServiceLastEtag: tosDownload.tosEtag,
-            termsOfServiceAcceptedTimestamp: getTimestampNow(),
-        };
-        // FIXME: only update if pointer got updated
-        r.lastError = undefined;
-        r.retryInfo = initRetryInfo(false);
-        r.lastUpdate = getTimestampNow();
-        (r.nextUpdate = keysInfo.expiry),
-            // New denominations might be available.
-            (r.nextRefreshCheck = getTimestampNow());
-        r.detailsPointer = {
-            currency: details.currency,
-            masterPublicKey: details.masterPublicKey,
-            // FIXME: only change if pointer really changed
-            updateClock: getTimestampNow(),
-        };
-        await tx.exchanges.put(r);
-        await tx.exchangeDetails.put(details);
-        for (const currentDenom of keysInfo.currentDenominations) {
-            const oldDenom = await tx.denominations.get([
-                baseUrl,
-                currentDenom.denomPubHash,
-            ]);
-            if (oldDenom) ;
-            else {
-                await tx.denominations.put(currentDenom);
-            }
-        }
-        // Handle recoup
-        const recoupDenomList = keysInfo.recoup;
-        const newlyRevokedCoinPubs = [];
-        logger$e.trace("recoup list from exchange", recoupDenomList);
-        for (const recoupInfo of recoupDenomList) {
-            const oldDenom = await tx.denominations.get([
-                r.baseUrl,
-                recoupInfo.h_denom_pub,
-            ]);
-            if (!oldDenom) {
-                // We never even knew about the revoked denomination, all good.
-                continue;
-            }
-            if (oldDenom.isRevoked) {
-                // We already marked the denomination as revoked,
-                // this implies we revoked all coins
-                logger$e.trace("denom already revoked");
-                continue;
-            }
-            logger$e.trace("revoking denom", recoupInfo.h_denom_pub);
-            oldDenom.isRevoked = true;
-            await tx.denominations.put(oldDenom);
-            const affectedCoins = await tx.coins.indexes.byDenomPubHash
-                .iter(recoupInfo.h_denom_pub)
-                .toArray();
-            for (const ac of affectedCoins) {
-                newlyRevokedCoinPubs.push(ac.coinPub);
-            }
-        }
-        if (newlyRevokedCoinPubs.length != 0) {
-            logger$e.trace("recouping coins", newlyRevokedCoinPubs);
-            recoupGroupId = await createRecoupGroup(ws, tx, 
newlyRevokedCoinPubs);
-        }
-        return {
-            exchange: r,
-            exchangeDetails: details,
-        };
-    });
-    if (recoupGroupId) {
-        // Asynchronously start recoup.  This doesn't need to finish
-        // for the exchange update to be considered finished.
-        processRecoupGroup(ws, recoupGroupId).catch((e) => {
-            logger$e.error("error while recouping coins:", e);
-        });
-    }
-    if (!updated) {
-        throw Error("something went wrong with updating the exchange");
-    }
-    return {
-        exchange: updated.exchange,
-        exchangeDetails: updated.exchangeDetails,
-    };
-}
-async function getExchangePaytoUri(ws, exchangeBaseUrl, supportedTargetTypes) {
-    var _a;
-    // We do the update here, since the exchange might not even exist
-    // yet in our database.
-    const details = await getExchangeDetails
-        .makeContext(ws.db)
-        .runReadOnly(async (tx) => {
-        return getExchangeDetails(tx, exchangeBaseUrl);
-    });
-    const accounts = (_a = details === null || details === void 0 ? void 0 : 
details.wireInfo.accounts) !== null && _a !== void 0 ? _a : [];
-    for (const account of accounts) {
-        const res = parsePaytoUri(account.payto_uri);
-        if (!res) {
-            continue;
-        }
-        if (supportedTargetTypes.includes(res.targetType)) {
-            return account.payto_uri;
-        }
-    }
-    throw Error("no matching exchange account found");
-}
-/**
- * Check if and how an exchange is trusted and/or audited.
- */
-async function getExchangeTrust(ws, exchangeInfo) {
-    let isTrusted = false;
-    let isAudited = false;
-    return await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-        exchangesTrustStore: x.exchangeTrust,
-        auditorTrust: x.auditorTrust,
-    }))
-        .runReadOnly(async (tx) => {
-        const exchangeDetails = await getExchangeDetails(tx, 
exchangeInfo.baseUrl);
-        if (!exchangeDetails) {
-            throw Error(`exchange ${exchangeInfo.baseUrl} details not 
available`);
-        }
-        const exchangeTrustRecord = await 
tx.exchangesTrustStore.indexes.byExchangeMasterPub.get(exchangeDetails.masterPublicKey);
-        if (exchangeTrustRecord &&
-            exchangeTrustRecord.uids.length > 0 &&
-            exchangeTrustRecord.currency === exchangeDetails.currency) {
-            isTrusted = true;
-        }
-        for (const auditor of exchangeDetails.auditors) {
-            const auditorTrustRecord = await 
tx.auditorTrust.indexes.byAuditorPub.get(auditor.auditor_pub);
-            if (auditorTrustRecord && auditorTrustRecord.uids.length > 0) {
-                isAudited = true;
-                break;
-            }
-        }
-        return { isTrusted, isAudited };
-    });
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const EXCHANGE_COINS_LOCK = "exchange-coins-lock";
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$d = new Logger("refresh.ts");
-/**
- * Get the amount that we lose when refreshing a coin of the given denomination
- * with a certain amount left.
- *
- * If the amount left is zero, then the refresh cost
- * is also considered to be zero.  If a refresh isn't possible (e.g. due to 
lack of
- * the right denominations), then the cost is the full amount left.
- *
- * Considers refresh fees, withdrawal fees after refresh and amounts too small
- * to refresh.
- */
-function getTotalRefreshCost(denoms, refreshedDenom, amountLeft) {
-    const withdrawAmount = Amounts.sub(amountLeft, refreshedDenom.feeRefresh)
-        .amount;
-    const withdrawDenoms = selectWithdrawalDenominations(withdrawAmount, 
denoms);
-    const resultingAmount = 
Amounts.add(Amounts.getZero(withdrawAmount.currency), 
...withdrawDenoms.selectedDenoms.map((d) => Amounts.mult(d.denom.value, 
d.count).amount)).amount;
-    const totalCost = Amounts.sub(amountLeft, resultingAmount).amount;
-    logger$d.trace(`total refresh cost for ${amountToPretty(amountLeft)} is 
${amountToPretty(totalCost)}`);
-    return totalCost;
-}
-/**
- * Create a refresh session for one particular coin inside a refresh group.
- */
-async function refreshCreateSession(ws, refreshGroupId, coinIndex) {
-    logger$d.trace(`creating refresh session for coin ${coinIndex} in refresh 
group ${refreshGroupId}`);
-    const d = await ws.db
-        .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-        coins: x.coins,
-    }))
-        .runReadWrite(async (tx) => {
-        const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
-        if (!refreshGroup) {
-            return;
-        }
-        if (refreshGroup.finishedPerCoin[coinIndex]) {
-            return;
-        }
-        const existingRefreshSession = 
refreshGroup.refreshSessionPerCoin[coinIndex];
-        if (existingRefreshSession) {
-            return;
-        }
-        const oldCoinPub = refreshGroup.oldCoinPubs[coinIndex];
-        const coin = await tx.coins.get(oldCoinPub);
-        if (!coin) {
-            throw Error("Can't refresh, coin not found");
-        }
-        return { refreshGroup, coin };
-    });
-    if (!d) {
-        return;
-    }
-    const { refreshGroup, coin } = d;
-    const { exchange } = await updateExchangeFromUrl(ws, coin.exchangeBaseUrl);
-    if (!exchange) {
-        throw Error("db inconsistent: exchange of coin not found");
-    }
-    const { availableAmount, availableDenoms } = await ws.db
-        .mktx((x) => ({
-        denominations: x.denominations,
-    }))
-        .runReadOnly(async (tx) => {
-        const oldDenom = await tx.denominations.get([
-            exchange.baseUrl,
-            coin.denomPubHash,
-        ]);
-        if (!oldDenom) {
-            throw Error("db inconsistent: denomination for coin not found");
-        }
-        const availableDenoms = await 
tx.denominations.indexes.byExchangeBaseUrl
-            .iter(exchange.baseUrl)
-            .toArray();
-        const availableAmount = 
Amounts.sub(refreshGroup.inputPerCoin[coinIndex], oldDenom.feeRefresh).amount;
-        return { availableAmount, availableDenoms };
-    });
-    const newCoinDenoms = selectWithdrawalDenominations(availableAmount, 
availableDenoms);
-    if (newCoinDenoms.selectedDenoms.length === 0) {
-        logger$d.trace(`not refreshing, available amount 
${amountToPretty(availableAmount)} too small`);
-        await ws.db
-            .mktx((x) => ({
-            coins: x.coins,
-            refreshGroups: x.refreshGroups,
-        }))
-            .runReadWrite(async (tx) => {
-            const rg = await tx.refreshGroups.get(refreshGroupId);
-            if (!rg) {
-                return;
-            }
-            rg.finishedPerCoin[coinIndex] = true;
-            let allDone = true;
-            for (const f of rg.finishedPerCoin) {
-                if (!f) {
-                    allDone = false;
-                    break;
-                }
-            }
-            if (allDone) {
-                rg.timestampFinished = getTimestampNow();
-                rg.retryInfo = initRetryInfo(false);
-            }
-            await tx.refreshGroups.put(rg);
-        });
-        ws.notify({ type: NotificationType.RefreshUnwarranted });
-        return;
-    }
-    const sessionSecretSeed = encodeCrock(getRandomBytes(64));
-    // Store refresh session for this coin in the database.
-    await ws.db
-        .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-        coins: x.coins,
-    }))
-        .runReadWrite(async (tx) => {
-        const rg = await tx.refreshGroups.get(refreshGroupId);
-        if (!rg) {
-            return;
-        }
-        if (rg.refreshSessionPerCoin[coinIndex]) {
-            return;
-        }
-        rg.refreshSessionPerCoin[coinIndex] = {
-            norevealIndex: undefined,
-            sessionSecretSeed: sessionSecretSeed,
-            newDenoms: newCoinDenoms.selectedDenoms.map((x) => ({
-                count: x.count,
-                denomPubHash: x.denom.denomPubHash,
-            })),
-            amountRefreshOutput: newCoinDenoms.totalCoinValue,
-        };
-        await tx.refreshGroups.put(rg);
-    });
-    logger$d.info(`created refresh session for coin #${coinIndex} in 
${refreshGroupId}`);
-    ws.notify({ type: NotificationType.RefreshStarted });
-}
-function getRefreshRequestTimeout(rg) {
-    return { d_ms: 5000 };
-}
-async function refreshMelt(ws, refreshGroupId, coinIndex) {
-    const d = await ws.db
-        .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-        coins: x.coins,
-        denominations: x.denominations,
-    }))
-        .runReadWrite(async (tx) => {
-        const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
-        if (!refreshGroup) {
-            return;
-        }
-        const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex];
-        if (!refreshSession) {
-            return;
-        }
-        if (refreshSession.norevealIndex !== undefined) {
-            return;
-        }
-        const oldCoin = await 
tx.coins.get(refreshGroup.oldCoinPubs[coinIndex]);
-        checkDbInvariant(!!oldCoin, "melt coin doesn't exist");
-        const oldDenom = await tx.denominations.get([
-            oldCoin.exchangeBaseUrl,
-            oldCoin.denomPubHash,
-        ]);
-        checkDbInvariant(!!oldDenom, "denomination for melted coin doesn't 
exist");
-        const newCoinDenoms = [];
-        for (const dh of refreshSession.newDenoms) {
-            const newDenom = await tx.denominations.get([
-                oldCoin.exchangeBaseUrl,
-                dh.denomPubHash,
-            ]);
-            checkDbInvariant(!!newDenom, "new denomination for refresh not in 
database");
-            newCoinDenoms.push({
-                count: dh.count,
-                denomPub: newDenom.denomPub,
-                feeWithdraw: newDenom.feeWithdraw,
-                value: newDenom.value,
-            });
-        }
-        return { newCoinDenoms, oldCoin, oldDenom, refreshGroup, 
refreshSession };
-    });
-    if (!d) {
-        return;
-    }
-    const { newCoinDenoms, oldCoin, oldDenom, refreshGroup, refreshSession } = 
d;
-    const derived = await ws.cryptoApi.deriveRefreshSession({
-        kappa: 3,
-        meltCoinDenomPubHash: oldCoin.denomPubHash,
-        meltCoinPriv: oldCoin.coinPriv,
-        meltCoinPub: oldCoin.coinPub,
-        feeRefresh: oldDenom.feeRefresh,
-        newCoinDenoms,
-        sessionSecretSeed: refreshSession.sessionSecretSeed,
-    });
-    const reqUrl = new URL$1(`coins/${oldCoin.coinPub}/melt`, 
oldCoin.exchangeBaseUrl);
-    const meltReq = {
-        coin_pub: oldCoin.coinPub,
-        confirm_sig: derived.confirmSig,
-        denom_pub_hash: oldCoin.denomPubHash,
-        denom_sig: oldCoin.denomSig,
-        rc: derived.hash,
-        value_with_fee: Amounts.stringify(derived.meltValueWithFee),
-    };
-    logger$d.trace(`melt request for coin:`, meltReq);
-    const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], async () => 
{
-        return await ws.http.postJson(reqUrl.href, meltReq, {
-            timeout: getRefreshRequestTimeout(),
-        });
-    });
-    const meltResponse = await readSuccessResponseJsonOrThrow(resp, 
codecForExchangeMeltResponse());
-    const norevealIndex = meltResponse.noreveal_index;
-    refreshSession.norevealIndex = norevealIndex;
-    await ws.db
-        .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const rg = await tx.refreshGroups.get(refreshGroupId);
-        if (!rg) {
-            return;
-        }
-        if (rg.timestampFinished) {
-            return;
-        }
-        const rs = rg.refreshSessionPerCoin[coinIndex];
-        if (!rs) {
-            return;
-        }
-        if (rs.norevealIndex !== undefined) {
-            return;
-        }
-        rs.norevealIndex = norevealIndex;
-        await tx.refreshGroups.put(rg);
-    });
-    ws.notify({
-        type: NotificationType.RefreshMelted,
-    });
-}
-async function refreshReveal(ws, refreshGroupId, coinIndex) {
-    const d = await ws.db
-        .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-        coins: x.coins,
-        denominations: x.denominations,
-    }))
-        .runReadOnly(async (tx) => {
-        const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
-        if (!refreshGroup) {
-            return;
-        }
-        const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex];
-        if (!refreshSession) {
-            return;
-        }
-        const norevealIndex = refreshSession.norevealIndex;
-        if (norevealIndex === undefined) {
-            throw Error("can't reveal without melting first");
-        }
-        const oldCoin = await 
tx.coins.get(refreshGroup.oldCoinPubs[coinIndex]);
-        checkDbInvariant(!!oldCoin, "melt coin doesn't exist");
-        const oldDenom = await tx.denominations.get([
-            oldCoin.exchangeBaseUrl,
-            oldCoin.denomPubHash,
-        ]);
-        checkDbInvariant(!!oldDenom, "denomination for melted coin doesn't 
exist");
-        const newCoinDenoms = [];
-        for (const dh of refreshSession.newDenoms) {
-            const newDenom = await tx.denominations.get([
-                oldCoin.exchangeBaseUrl,
-                dh.denomPubHash,
-            ]);
-            checkDbInvariant(!!newDenom, "new denomination for refresh not in 
database");
-            newCoinDenoms.push({
-                count: dh.count,
-                denomPub: newDenom.denomPub,
-                feeWithdraw: newDenom.feeWithdraw,
-                value: newDenom.value,
-            });
-        }
-        return {
-            oldCoin,
-            oldDenom,
-            newCoinDenoms,
-            refreshSession,
-            refreshGroup,
-            norevealIndex,
-        };
-    });
-    if (!d) {
-        return;
-    }
-    const { oldCoin, oldDenom, newCoinDenoms, refreshSession, refreshGroup, 
norevealIndex, } = d;
-    const derived = await ws.cryptoApi.deriveRefreshSession({
-        kappa: 3,
-        meltCoinDenomPubHash: oldCoin.denomPubHash,
-        meltCoinPriv: oldCoin.coinPriv,
-        meltCoinPub: oldCoin.coinPub,
-        feeRefresh: oldDenom.feeRefresh,
-        newCoinDenoms,
-        sessionSecretSeed: refreshSession.sessionSecretSeed,
-    });
-    const privs = Array.from(derived.transferPrivs);
-    privs.splice(norevealIndex, 1);
-    const planchets = derived.planchetsForGammas[norevealIndex];
-    if (!planchets) {
-        throw Error("refresh index error");
-    }
-    const evs = planchets.map((x) => x.coinEv);
-    const newDenomsFlat = [];
-    const linkSigs = [];
-    for (let i = 0; i < refreshSession.newDenoms.length; i++) {
-        const dsel = refreshSession.newDenoms[i];
-        for (let j = 0; j < dsel.count; j++) {
-            const newCoinIndex = linkSigs.length;
-            const linkSig = await ws.cryptoApi.signCoinLink(oldCoin.coinPriv, 
dsel.denomPubHash, oldCoin.coinPub, derived.transferPubs[norevealIndex], 
planchets[newCoinIndex].coinEv);
-            linkSigs.push(linkSig);
-            newDenomsFlat.push(dsel.denomPubHash);
-        }
-    }
-    const req = {
-        coin_evs: evs,
-        new_denoms_h: newDenomsFlat,
-        rc: derived.hash,
-        transfer_privs: privs,
-        transfer_pub: derived.transferPubs[norevealIndex],
-        link_sigs: linkSigs,
-    };
-    const reqUrl = new URL$1(`refreshes/${derived.hash}/reveal`, 
oldCoin.exchangeBaseUrl);
-    const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], async () => 
{
-        return await ws.http.postJson(reqUrl.href, req, {
-            timeout: getRefreshRequestTimeout(),
-        });
-    });
-    const reveal = await readSuccessResponseJsonOrThrow(resp, 
codecForExchangeRevealResponse());
-    const coins = [];
-    for (let i = 0; i < refreshSession.newDenoms.length; i++) {
-        for (let j = 0; j < refreshSession.newDenoms[i].count; j++) {
-            const newCoinIndex = coins.length;
-            // FIXME: Look up in earlier transaction!
-            const denom = await ws.db
-                .mktx((x) => ({
-                denominations: x.denominations,
-            }))
-                .runReadOnly(async (tx) => {
-                return tx.denominations.get([
-                    oldCoin.exchangeBaseUrl,
-                    refreshSession.newDenoms[i].denomPubHash,
-                ]);
-            });
-            if (!denom) {
-                console.error("denom not found");
-                continue;
-            }
-            const pc = derived.planchetsForGammas[norevealIndex][newCoinIndex];
-            const denomSig = await 
ws.cryptoApi.rsaUnblind(reveal.ev_sigs[newCoinIndex].ev_sig, pc.blindingKey, 
denom.denomPub);
-            const coin = {
-                blindingKey: pc.blindingKey,
-                coinPriv: pc.privateKey,
-                coinPub: pc.publicKey,
-                currentAmount: denom.value,
-                denomPub: denom.denomPub,
-                denomPubHash: denom.denomPubHash,
-                denomSig,
-                exchangeBaseUrl: oldCoin.exchangeBaseUrl,
-                status: CoinStatus.Fresh,
-                coinSource: {
-                    type: CoinSourceType.Refresh,
-                    oldCoinPub: refreshGroup.oldCoinPubs[coinIndex],
-                },
-                suspended: false,
-                coinEvHash: pc.coinEv,
-            };
-            coins.push(coin);
-        }
-    }
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const rg = await tx.refreshGroups.get(refreshGroupId);
-        if (!rg) {
-            logger$d.warn("no refresh session found");
-            return;
-        }
-        const rs = rg.refreshSessionPerCoin[coinIndex];
-        if (!rs) {
-            return;
-        }
-        rg.finishedPerCoin[coinIndex] = true;
-        let allDone = true;
-        for (const f of rg.finishedPerCoin) {
-            if (!f) {
-                allDone = false;
-                break;
-            }
-        }
-        if (allDone) {
-            rg.timestampFinished = getTimestampNow();
-            rg.retryInfo = initRetryInfo(false);
-        }
-        for (const coin of coins) {
-            await tx.coins.put(coin);
-        }
-        await tx.refreshGroups.put(rg);
-    });
-    logger$d.trace("refresh finished (end of reveal)");
-    ws.notify({
-        type: NotificationType.RefreshRevealed,
-    });
-}
-async function incrementRefreshRetry(ws, refreshGroupId, err) {
-    await ws.db
-        .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const r = await tx.refreshGroups.get(refreshGroupId);
-        if (!r) {
-            return;
-        }
-        if (!r.retryInfo) {
-            return;
-        }
-        r.retryInfo.retryCounter++;
-        updateRetryInfoTimeout(r.retryInfo);
-        r.lastError = err;
-        await tx.refreshGroups.put(r);
-    });
-    if (err) {
-        ws.notify({ type: NotificationType.RefreshOperationError, error: err 
});
-    }
-}
-/**
- * Actually process a refresh group that has been created.
- */
-async function processRefreshGroup(ws, refreshGroupId, forceNow = false) {
-    await ws.memoProcessRefresh.memo(refreshGroupId, async () => {
-        const onOpErr = (e) => incrementRefreshRetry(ws, refreshGroupId, e);
-        return await guardOperationException(async () => await 
processRefreshGroupImpl(ws, refreshGroupId, forceNow), onOpErr);
-    });
-}
-async function resetRefreshGroupRetry(ws, refreshGroupId) {
-    await ws.db
-        .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const x = await tx.refreshGroups.get(refreshGroupId);
-        if (x) {
-            x.retryInfo = initRetryInfo();
-            await tx.refreshGroups.put(x);
-        }
-    });
-}
-async function processRefreshGroupImpl(ws, refreshGroupId, forceNow) {
-    if (forceNow) {
-        await resetRefreshGroupRetry(ws, refreshGroupId);
-    }
-    const refreshGroup = await ws.db
-        .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.refreshGroups.get(refreshGroupId);
-    });
-    if (!refreshGroup) {
-        return;
-    }
-    if (refreshGroup.timestampFinished) {
-        return;
-    }
-    // Process refresh sessions of the group in parallel.
-    const ps = refreshGroup.oldCoinPubs.map((x, i) => 
processRefreshSession(ws, refreshGroupId, i));
-    await Promise.all(ps);
-    logger$d.trace("refresh finished");
-}
-async function processRefreshSession(ws, refreshGroupId, coinIndex) {
-    logger$d.trace(`processing refresh session for coin ${coinIndex} of group 
${refreshGroupId}`);
-    let refreshGroup = await ws.db
-        .mktx((x) => ({ refreshGroups: x.refreshGroups }))
-        .runReadOnly(async (tx) => {
-        return tx.refreshGroups.get(refreshGroupId);
-    });
-    if (!refreshGroup) {
-        return;
-    }
-    if (refreshGroup.finishedPerCoin[coinIndex]) {
-        return;
-    }
-    if (!refreshGroup.refreshSessionPerCoin[coinIndex]) {
-        await refreshCreateSession(ws, refreshGroupId, coinIndex);
-        refreshGroup = await ws.db
-            .mktx((x) => ({ refreshGroups: x.refreshGroups }))
-            .runReadOnly(async (tx) => {
-            return tx.refreshGroups.get(refreshGroupId);
-        });
-        if (!refreshGroup) {
-            return;
-        }
-    }
-    const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex];
-    if (!refreshSession) {
-        if (!refreshGroup.finishedPerCoin[coinIndex]) {
-            throw Error("BUG: refresh session was not created and coin not 
marked as finished");
-        }
-        return;
-    }
-    if (refreshSession.norevealIndex === undefined) {
-        await refreshMelt(ws, refreshGroupId, coinIndex);
-    }
-    await refreshReveal(ws, refreshGroupId, coinIndex);
-}
-/**
- * Create a refresh group for a list of coins.
- *
- * Refreshes the remaining amount on the coin, effectively capturing the 
remaining
- * value in the refresh group.
- *
- * The caller must ensure that
- * the remaining amount was updated correctly before the coin was deposited or
- * credited.
- *
- * The caller must also ensure that the coins that should be refreshed exist
- * in the current database transaction.
- */
-async function createRefreshGroup(ws, tx, oldCoinPubs, reason) {
-    const refreshGroupId = encodeCrock(getRandomBytes(32));
-    const inputPerCoin = [];
-    const estimatedOutputPerCoin = [];
-    const denomsPerExchange = {};
-    const getDenoms = async (exchangeBaseUrl) => {
-        if (denomsPerExchange[exchangeBaseUrl]) {
-            return denomsPerExchange[exchangeBaseUrl];
-        }
-        const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
-            .iter(exchangeBaseUrl)
-            .filter((x) => {
-            return isWithdrawableDenom(x);
-        });
-        denomsPerExchange[exchangeBaseUrl] = allDenoms;
-        return allDenoms;
-    };
-    for (const ocp of oldCoinPubs) {
-        const coin = await tx.coins.get(ocp.coinPub);
-        checkDbInvariant(!!coin, "coin must be in database");
-        const denom = await tx.denominations.get([
-            coin.exchangeBaseUrl,
-            coin.denomPubHash,
-        ]);
-        checkDbInvariant(!!denom, "denomination for existing coin must be in 
database");
-        const refreshAmount = coin.currentAmount;
-        inputPerCoin.push(refreshAmount);
-        coin.currentAmount = Amounts.getZero(refreshAmount.currency);
-        coin.status = CoinStatus.Dormant;
-        await tx.coins.put(coin);
-        const denoms = await getDenoms(coin.exchangeBaseUrl);
-        const cost = getTotalRefreshCost(denoms, denom, refreshAmount);
-        const output = Amounts.sub(refreshAmount, cost).amount;
-        estimatedOutputPerCoin.push(output);
-    }
-    const refreshGroup = {
-        timestampFinished: undefined,
-        finishedPerCoin: oldCoinPubs.map((x) => false),
-        lastError: undefined,
-        lastErrorPerCoin: {},
-        oldCoinPubs: oldCoinPubs.map((x) => x.coinPub),
-        reason,
-        refreshGroupId,
-        refreshSessionPerCoin: oldCoinPubs.map((x) => undefined),
-        retryInfo: initRetryInfo(),
-        inputPerCoin,
-        estimatedOutputPerCoin,
-        timestampCreated: getTimestampNow(),
-    };
-    if (oldCoinPubs.length == 0) {
-        logger$d.warn("created refresh group with zero coins");
-        refreshGroup.timestampFinished = getTimestampNow();
-    }
-    await tx.refreshGroups.put(refreshGroup);
-    logger$d.trace(`created refresh group ${refreshGroupId}`);
-    processRefreshGroup(ws, refreshGroupId).catch((e) => {
-        logger$d.warn(`processing refresh group ${refreshGroupId} failed`);
-    });
-    return {
-        refreshGroupId,
-    };
-}
-/**
- * Timestamp after which the wallet would do the next check for an 
auto-refresh.
- */
-function getAutoRefreshCheckThreshold(d) {
-    const delta = timestampDifference(d.stampExpireWithdraw, 
d.stampExpireDeposit);
-    const deltaDiv = durationMul(delta, 0.75);
-    return timestampAddDuration(d.stampExpireWithdraw, deltaDiv);
-}
-/**
- * Timestamp after which the wallet would do an auto-refresh.
- */
-function getAutoRefreshExecuteThreshold(d) {
-    const delta = timestampDifference(d.stampExpireWithdraw, 
d.stampExpireDeposit);
-    const deltaDiv = durationMul(delta, 0.5);
-    return timestampAddDuration(d.stampExpireWithdraw, deltaDiv);
-}
-async function autoRefresh(ws, exchangeBaseUrl) {
-    logger$d.info(`doing auto-refresh check for '${exchangeBaseUrl}'`);
-    await updateExchangeFromUrl(ws, exchangeBaseUrl, true);
-    let minCheckThreshold = timestampAddDuration(getTimestampNow(), 
durationFromSpec({ days: 1 }));
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        denominations: x.denominations,
-        refreshGroups: x.refreshGroups,
-        exchanges: x.exchanges,
-    }))
-        .runReadWrite(async (tx) => {
-        const exchange = await tx.exchanges.get(exchangeBaseUrl);
-        if (!exchange) {
-            return;
-        }
-        const coins = await tx.coins.indexes.byBaseUrl
-            .iter(exchangeBaseUrl)
-            .toArray();
-        const refreshCoins = [];
-        for (const coin of coins) {
-            if (coin.status !== CoinStatus.Fresh) {
-                continue;
-            }
-            if (coin.suspended) {
-                continue;
-            }
-            const denom = await tx.denominations.get([
-                exchangeBaseUrl,
-                coin.denomPubHash,
-            ]);
-            if (!denom) {
-                logger$d.warn("denomination not in database");
-                continue;
-            }
-            const executeThreshold = getAutoRefreshExecuteThreshold(denom);
-            if (isTimestampExpired(executeThreshold)) {
-                refreshCoins.push(coin);
-            }
-            else {
-                const checkThreshold = getAutoRefreshCheckThreshold(denom);
-                minCheckThreshold = timestampMin(minCheckThreshold, 
checkThreshold);
-            }
-        }
-        if (refreshCoins.length > 0) {
-            await createRefreshGroup(ws, tx, refreshCoins, 
RefreshReason.Scheduled);
-        }
-        logger$d.info(`current wallet time: 
${timestampToIsoString(getTimestampNow())}`);
-        logger$d.info(`next refresh check at 
${timestampToIsoString(minCheckThreshold)}`);
-        exchange.nextRefreshCheck = minCheckThreshold;
-        await tx.exchanges.put(exchange);
-    });
-}
-
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-var ContractTermsUtil;
-(function (ContractTermsUtil) {
-    /**
-     * Scrub all forgettable members from an object.
-     */
-    function scrub(anyJson) {
-        return forgetAllImpl(anyJson, [], () => true);
-    }
-    ContractTermsUtil.scrub = scrub;
-    /**
-     * Recursively forget all forgettable members of an object,
-     * where the path matches a predicate.
-     */
-    function forgetAll(anyJson, pred) {
-        return forgetAllImpl(anyJson, [], pred);
-    }
-    ContractTermsUtil.forgetAll = forgetAll;
-    function forgetAllImpl(anyJson, path, pred) {
-        const dup = JSON.parse(JSON.stringify(anyJson));
-        if (Array.isArray(dup)) {
-            for (let i = 0; i < dup.length; i++) {
-                dup[i] = forgetAllImpl(dup[i], [...path, `${i}`], pred);
-            }
-        }
-        else if (typeof dup === "object" && dup != null) {
-            if (typeof dup.$forgettable === "object") {
-                for (const x of Object.keys(dup.$forgettable)) {
-                    if (!pred([...path, x])) {
-                        continue;
-                    }
-                    if (!dup.$forgotten) {
-                        dup.$forgotten = {};
-                    }
-                    if (!dup.$forgotten[x]) {
-                        const membValCanon = 
stringToBytes(canonicalJson(scrub(dup[x])) + "\0");
-                        const membSalt = stringToBytes(dup.$forgettable[x] + 
"\0");
-                        const h = kdf(64, membValCanon, membSalt, new 
Uint8Array([]));
-                        dup.$forgotten[x] = encodeCrock(h);
-                    }
-                    delete dup[x];
-                    delete dup.$forgettable[x];
-                }
-                if (Object.keys(dup.$forgettable).length === 0) {
-                    delete dup.$forgettable;
-                }
-            }
-            for (const x of Object.keys(dup)) {
-                if (x.startsWith("$")) {
-                    continue;
-                }
-                dup[x] = forgetAllImpl(dup[x], [...path, x], pred);
-            }
-        }
-        return dup;
-    }
-    /**
-     * Generate a salt for all members marked as forgettable,
-     * but which don't have an actual salt yet.
-     */
-    function saltForgettable(anyJson) {
-        const dup = JSON.parse(JSON.stringify(anyJson));
-        if (Array.isArray(dup)) {
-            for (let i = 0; i < dup.length; i++) {
-                dup[i] = saltForgettable(dup[i]);
-            }
-        }
-        else if (typeof dup === "object" && dup !== null) {
-            if (typeof dup.$forgettable === "object") {
-                for (const k of Object.keys(dup.$forgettable)) {
-                    if (dup.$forgettable[k] === true) {
-                        dup.$forgettable[k] = encodeCrock(getRandomBytes(32));
-                    }
-                }
-            }
-            for (const x of Object.keys(dup)) {
-                if (x.startsWith("$")) {
-                    continue;
-                }
-                dup[x] = saltForgettable(dup[x]);
-            }
-        }
-        return dup;
-    }
-    ContractTermsUtil.saltForgettable = saltForgettable;
-    const nameRegex = /^[0-9A-Za-z_]+$/;
-    /**
-     * Check that the given JSON object is well-formed with regards
-     * to forgettable fields and other restrictions for forgettable JSON.
-     */
-    function validateForgettable(anyJson) {
-        var _a;
-        if (typeof anyJson === "string") {
-            return true;
-        }
-        if (typeof anyJson === "number") {
-            return (Number.isInteger(anyJson) &&
-                anyJson >= Number.MIN_SAFE_INTEGER &&
-                anyJson <= Number.MAX_SAFE_INTEGER);
-        }
-        if (typeof anyJson === "boolean") {
-            return true;
-        }
-        if (anyJson === null) {
-            return true;
-        }
-        if (Array.isArray(anyJson)) {
-            return anyJson.every((x) => validateForgettable(x));
-        }
-        if (typeof anyJson === "object") {
-            for (const k of Object.keys(anyJson)) {
-                if (k.match(nameRegex)) {
-                    if (validateForgettable(anyJson[k])) {
-                        continue;
-                    }
-                    else {
-                        return false;
-                    }
-                }
-                if (k === "$forgettable") {
-                    const fga = anyJson.$forgettable;
-                    if (!fga || typeof fga !== "object") {
-                        return false;
-                    }
-                    for (const fk of Object.keys(fga)) {
-                        if (!fk.match(nameRegex)) {
-                            return false;
-                        }
-                        if (!(fk in anyJson)) {
-                            return false;
-                        }
-                        const fv = anyJson.$forgettable[fk];
-                        if (typeof fv !== "string") {
-                            return false;
-                        }
-                    }
-                }
-                else if (k === "$forgotten") {
-                    const fgo = anyJson.$forgotten;
-                    if (!fgo || typeof fgo !== "object") {
-                        return false;
-                    }
-                    for (const fk of Object.keys(fgo)) {
-                        if (!fk.match(nameRegex)) {
-                            return false;
-                        }
-                        // Check that the value has actually been forgotten.
-                        if (fk in anyJson) {
-                            return false;
-                        }
-                        const fv = anyJson.$forgotten[fk];
-                        if (typeof fv !== "string") {
-                            return false;
-                        }
-                        try {
-                            const decFv = decodeCrock(fv);
-                            if (decFv.length != 64) {
-                                return false;
-                            }
-                        }
-                        catch (e) {
-                            return false;
-                        }
-                        // Check that salt has been deleted after forgetting.
-                        if (((_a = anyJson.$forgettable) === null || _a === 
void 0 ? void 0 : _a[k]) !== undefined) {
-                            return false;
-                        }
-                    }
-                }
-                else {
-                    return false;
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-    ContractTermsUtil.validateForgettable = validateForgettable;
-    /**
-     * Check that no forgettable information has been forgotten.
-     *
-     * Must only be called on an object already validated with 
validateForgettable.
-     */
-    function validateNothingForgotten(contractTerms) {
-        throw Error("not implemented yet");
-    }
-    ContractTermsUtil.validateNothingForgotten = validateNothingForgotten;
-    /**
-     * Hash a contract terms object.  Forgettable fields
-     * are scrubbed and JSON canonicalization is applied
-     * before hashing.
-     */
-    function hashContractTerms(contractTerms) {
-        const cleaned = scrub(contractTerms);
-        const canon = canonicalJson(cleaned) + "\0";
-        return encodeCrock(hash(stringToBytes(canon)));
-    }
-    ContractTermsUtil.hashContractTerms = hashContractTerms;
-})(ContractTermsUtil || (ContractTermsUtil = {}));
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Logger.
- */
-const logger$c = new Logger("pay.ts");
-/**
- * Compute the total cost of a payment to the customer.
- *
- * This includes the amount taken by the merchant, fees (wire/deposit) 
contributed
- * by the customer, refreshing fees, fees for withdraw-after-refresh and 
"trimmings"
- * of coins that are too small to spend.
- */
-async function getTotalPaymentCost(ws, pcs) {
-    return ws.db
-        .mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
-        .runReadOnly(async (tx) => {
-        const costs = [];
-        for (let i = 0; i < pcs.coinPubs.length; i++) {
-            const coin = await tx.coins.get(pcs.coinPubs[i]);
-            if (!coin) {
-                throw Error("can't calculate payment cost, coin not found");
-            }
-            const denom = await tx.denominations.get([
-                coin.exchangeBaseUrl,
-                coin.denomPubHash,
-            ]);
-            if (!denom) {
-                throw Error("can't calculate payment cost, denomination for 
coin not found");
-            }
-            const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
-                .iter()
-                .toArray();
-            const amountLeft = Amounts.sub(denom.value, 
pcs.coinContributions[i])
-                .amount;
-            const refreshCost = getTotalRefreshCost(allDenoms, denom, 
amountLeft);
-            costs.push(pcs.coinContributions[i]);
-            costs.push(refreshCost);
-        }
-        return Amounts.sum(costs).amount;
-    });
-}
-/**
- * Get the amount that will be deposited on the merchant's bank
- * account, not considering aggregation.
- */
-async function getEffectiveDepositAmount(ws, wireType, pcs) {
-    const amt = [];
-    const fees = [];
-    const exchangeSet = new Set();
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        denominations: x.denominations,
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-    }))
-        .runReadOnly(async (tx) => {
-        var _a;
-        for (let i = 0; i < pcs.coinPubs.length; i++) {
-            const coin = await tx.coins.get(pcs.coinPubs[i]);
-            if (!coin) {
-                throw Error("can't calculate deposit amountt, coin not found");
-            }
-            const denom = await tx.denominations.get([
-                coin.exchangeBaseUrl,
-                coin.denomPubHash,
-            ]);
-            if (!denom) {
-                throw Error("can't find denomination to calculate deposit 
amount");
-            }
-            amt.push(pcs.coinContributions[i]);
-            fees.push(denom.feeDeposit);
-            exchangeSet.add(coin.exchangeBaseUrl);
-        }
-        for (const exchangeUrl of exchangeSet.values()) {
-            const exchangeDetails = await getExchangeDetails(tx, exchangeUrl);
-            if (!exchangeDetails) {
-                continue;
-            }
-            const fee = (_a = 
exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
-                return timestampIsBetween(getTimestampNow(), x.startStamp, 
x.endStamp);
-            })) === null || _a === void 0 ? void 0 : _a.wireFee;
-            if (fee) {
-                fees.push(fee);
-            }
-        }
-    });
-    return Amounts.sub(Amounts.sum(amt).amount, 
Amounts.sum(fees).amount).amount;
-}
-function isSpendableCoin(coin, denom) {
-    if (coin.suspended) {
-        return false;
-    }
-    if (coin.status !== CoinStatus.Fresh) {
-        return false;
-    }
-    if (isTimestampExpired(denom.stampExpireDeposit)) {
-        return false;
-    }
-    return true;
-}
-/**
- * Get candidate coins.  From these candidate coins,
- * the actual contributions will be computed later.
- *
- * The resulting candidate coin list is sorted deterministically.
- *
- * TODO: Exclude more coins:
- * - when we already have a coin with more remaining amount than
- *   the payment amount, coins with even higher amounts can be skipped.
- */
-async function getCandidatePayCoins(ws, req) {
-    const candidateCoins = [];
-    const wireFeesPerExchange = {};
-    await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-        denominations: x.denominations,
-        coins: x.coins,
-    }))
-        .runReadOnly(async (tx) => {
-        const exchanges = await tx.exchanges.iter().toArray();
-        for (const exchange of exchanges) {
-            let isOkay = false;
-            const exchangeDetails = await getExchangeDetails(tx, 
exchange.baseUrl);
-            if (!exchangeDetails) {
-                continue;
-            }
-            const exchangeFees = exchangeDetails.wireInfo;
-            if (!exchangeFees) {
-                continue;
-            }
-            // is the exchange explicitly allowed?
-            for (const allowedExchange of req.allowedExchanges) {
-                if (allowedExchange.exchangePub === 
exchangeDetails.masterPublicKey) {
-                    isOkay = true;
-                    break;
-                }
-            }
-            // is the exchange allowed because of one of its auditors?
-            if (!isOkay) {
-                for (const allowedAuditor of req.allowedAuditors) {
-                    for (const auditor of exchangeDetails.auditors) {
-                        if (auditor.auditor_pub === allowedAuditor.auditorPub) 
{
-                            isOkay = true;
-                            break;
-                        }
-                    }
-                    if (isOkay) {
-                        break;
-                    }
-                }
-            }
-            if (!isOkay) {
-                continue;
-            }
-            const coins = await tx.coins.indexes.byBaseUrl
-                .iter(exchange.baseUrl)
-                .toArray();
-            if (!coins || coins.length === 0) {
-                continue;
-            }
-            // Denomination of the first coin, we assume that all other
-            // coins have the same currency
-            const firstDenom = await tx.denominations.get([
-                exchange.baseUrl,
-                coins[0].denomPubHash,
-            ]);
-            if (!firstDenom) {
-                throw Error("db inconsistent");
-            }
-            const currency = firstDenom.value.currency;
-            for (const coin of coins) {
-                const denom = await tx.denominations.get([
-                    exchange.baseUrl,
-                    coin.denomPubHash,
-                ]);
-                if (!denom) {
-                    throw Error("db inconsistent");
-                }
-                if (denom.value.currency !== currency) {
-                    logger$c.warn(`same pubkey for different currencies at 
exchange ${exchange.baseUrl}`);
-                    continue;
-                }
-                if (!isSpendableCoin(coin, denom)) {
-                    continue;
-                }
-                candidateCoins.push({
-                    availableAmount: coin.currentAmount,
-                    coinPub: coin.coinPub,
-                    denomPub: coin.denomPub,
-                    feeDeposit: denom.feeDeposit,
-                    exchangeBaseUrl: denom.exchangeBaseUrl,
-                });
-            }
-            let wireFee;
-            for (const fee of exchangeFees.feesForType[req.wireMethod] || []) {
-                if (fee.startStamp <= req.timestamp &&
-                    fee.endStamp >= req.timestamp) {
-                    wireFee = fee.wireFee;
-                    break;
-                }
-            }
-            if (wireFee) {
-                wireFeesPerExchange[exchange.baseUrl] = wireFee;
-            }
-        }
-    });
-    return {
-        candidateCoins,
-        wireFeesPerExchange,
-    };
-}
-async function applyCoinSpend(ws, tx, coinSelection) {
-    for (let i = 0; i < coinSelection.coinPubs.length; i++) {
-        const coin = await tx.coins.get(coinSelection.coinPubs[i]);
-        if (!coin) {
-            throw Error("coin allocated for payment doesn't exist anymore");
-        }
-        if (coin.status !== CoinStatus.Fresh) {
-            // applyCoinSpend was called again, probably
-            // because of a coin re-selection to recover after
-            // accidental double spending.
-            // Ignore coins we already marked as spent.
-            continue;
-        }
-        coin.status = CoinStatus.Dormant;
-        const remaining = Amounts.sub(coin.currentAmount, 
coinSelection.coinContributions[i]);
-        if (remaining.saturated) {
-            throw Error("not enough remaining balance on coin for payment");
-        }
-        coin.currentAmount = remaining.amount;
-        await tx.coins.put(coin);
-    }
-    const refreshCoinPubs = coinSelection.coinPubs.map((x) => ({
-        coinPub: x,
-    }));
-    await createRefreshGroup(ws, tx, refreshCoinPubs, RefreshReason.Pay);
-}
-/**
- * Record all information that is necessary to
- * pay for a proposal in the wallet's database.
- */
-async function recordConfirmPay(ws, proposal, coinSelection, 
coinDepositPermissions, sessionIdOverride) {
-    const d = proposal.download;
-    if (!d) {
-        throw Error("proposal is in invalid state");
-    }
-    let sessionId;
-    if (sessionIdOverride) {
-        sessionId = sessionIdOverride;
-    }
-    else {
-        sessionId = proposal.downloadSessionId;
-    }
-    logger$c.trace(`recording payment on ${proposal.orderId} with session ID 
${sessionId}`);
-    const payCostInfo = await getTotalPaymentCost(ws, coinSelection);
-    const t = {
-        abortStatus: AbortStatus.None,
-        download: d,
-        lastSessionId: sessionId,
-        payCoinSelection: coinSelection,
-        payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
-        totalPayCost: payCostInfo,
-        coinDepositPermissions,
-        timestampAccept: getTimestampNow(),
-        timestampLastRefundStatus: undefined,
-        proposalId: proposal.proposalId,
-        lastPayError: undefined,
-        lastRefundStatusError: undefined,
-        payRetryInfo: initRetryInfo(),
-        refundStatusRetryInfo: initRetryInfo(),
-        refundQueryRequested: false,
-        timestampFirstSuccessfulPay: undefined,
-        autoRefundDeadline: undefined,
-        paymentSubmitPending: true,
-        refunds: {},
-        merchantPaySig: undefined,
-        noncePriv: proposal.noncePriv,
-        noncePub: proposal.noncePub,
-    };
-    await ws.db
-        .mktx((x) => ({
-        proposals: x.proposals,
-        purchases: x.purchases,
-        coins: x.coins,
-        refreshGroups: x.refreshGroups,
-        denominations: x.denominations,
-    }))
-        .runReadWrite(async (tx) => {
-        const p = await tx.proposals.get(proposal.proposalId);
-        if (p) {
-            p.proposalStatus = ProposalStatus.ACCEPTED;
-            delete p.lastError;
-            p.retryInfo = initRetryInfo(false);
-            await tx.proposals.put(p);
-        }
-        await tx.purchases.put(t);
-        await applyCoinSpend(ws, tx, coinSelection);
-    });
-    ws.notify({
-        type: NotificationType.ProposalAccepted,
-        proposalId: proposal.proposalId,
-    });
-    return t;
-}
-async function incrementProposalRetry(ws, proposalId, err) {
-    await ws.db
-        .mktx((x) => ({ proposals: x.proposals }))
-        .runReadWrite(async (tx) => {
-        const pr = await tx.proposals.get(proposalId);
-        if (!pr) {
-            return;
-        }
-        if (!pr.retryInfo) {
-            return;
-        }
-        pr.retryInfo.retryCounter++;
-        updateRetryInfoTimeout(pr.retryInfo);
-        pr.lastError = err;
-        await tx.proposals.put(pr);
-    });
-    if (err) {
-        ws.notify({ type: NotificationType.ProposalOperationError, error: err 
});
-    }
-}
-async function incrementPurchasePayRetry(ws, proposalId, err) {
-    logger$c.warn("incrementing purchase pay retry with error", err);
-    await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
-        .runReadWrite(async (tx) => {
-        const pr = await tx.purchases.get(proposalId);
-        if (!pr) {
-            return;
-        }
-        if (!pr.payRetryInfo) {
-            pr.payRetryInfo = initRetryInfo();
-        }
-        pr.payRetryInfo.retryCounter++;
-        updateRetryInfoTimeout(pr.payRetryInfo);
-        logger$c.trace(`retrying pay in 
${getDurationRemaining(pr.payRetryInfo.nextRetry).d_ms} ms`);
-        pr.lastPayError = err;
-        await tx.purchases.put(pr);
-    });
-    if (err) {
-        ws.notify({ type: NotificationType.PayOperationError, error: err });
-    }
-}
-async function processDownloadProposal(ws, proposalId, forceNow = false) {
-    const onOpErr = (err) => incrementProposalRetry(ws, proposalId, err);
-    await guardOperationException(() => processDownloadProposalImpl(ws, 
proposalId, forceNow), onOpErr);
-}
-async function resetDownloadProposalRetry(ws, proposalId) {
-    await ws.db
-        .mktx((x) => ({ proposals: x.proposals }))
-        .runReadWrite(async (tx) => {
-        const p = await tx.proposals.get(proposalId);
-        if (p) {
-            delete p.retryInfo;
-            await tx.proposals.put(p);
-        }
-    });
-}
-async function failProposalPermanently(ws, proposalId, err) {
-    await ws.db
-        .mktx((x) => ({ proposals: x.proposals }))
-        .runReadWrite(async (tx) => {
-        const p = await tx.proposals.get(proposalId);
-        if (!p) {
-            return;
-        }
-        delete p.retryInfo;
-        p.lastError = err;
-        p.proposalStatus = ProposalStatus.PERMANENTLY_FAILED;
-        await tx.proposals.put(p);
-    });
-}
-function getProposalRequestTimeout(proposal) {
-    return durationMax({ d_ms: 60000 }, durationMin({ d_ms: 5000 }, 
getRetryDuration(proposal.retryInfo)));
-}
-function getPayRequestTimeout(purchase) {
-    return durationMul({ d_ms: 15000 }, 1 + 
purchase.payCoinSelection.coinPubs.length / 5);
-}
-function extractContractData(parsedContractTerms, contractTermsHash, 
merchantSig) {
-    var _a;
-    const amount = Amounts.parseOrThrow(parsedContractTerms.amount);
-    let maxWireFee;
-    if (parsedContractTerms.max_wire_fee) {
-        maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee);
-    }
-    else {
-        maxWireFee = Amounts.getZero(amount.currency);
-    }
-    return {
-        amount,
-        contractTermsHash: contractTermsHash,
-        fulfillmentUrl: (_a = parsedContractTerms.fulfillment_url) !== null && 
_a !== void 0 ? _a : "",
-        merchantBaseUrl: parsedContractTerms.merchant_base_url,
-        merchantPub: parsedContractTerms.merchant_pub,
-        merchantSig,
-        orderId: parsedContractTerms.order_id,
-        summary: parsedContractTerms.summary,
-        autoRefund: parsedContractTerms.auto_refund,
-        maxWireFee,
-        payDeadline: parsedContractTerms.pay_deadline,
-        refundDeadline: parsedContractTerms.refund_deadline,
-        wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1,
-        allowedAuditors: parsedContractTerms.auditors.map((x) => ({
-            auditorBaseUrl: x.url,
-            auditorPub: x.auditor_pub,
-        })),
-        allowedExchanges: parsedContractTerms.exchanges.map((x) => ({
-            exchangeBaseUrl: x.url,
-            exchangePub: x.master_pub,
-        })),
-        timestamp: parsedContractTerms.timestamp,
-        wireMethod: parsedContractTerms.wire_method,
-        wireInfoHash: parsedContractTerms.h_wire,
-        maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee),
-        merchant: parsedContractTerms.merchant,
-        products: parsedContractTerms.products,
-        summaryI18n: parsedContractTerms.summary_i18n,
-    };
-}
-async function processDownloadProposalImpl(ws, proposalId, forceNow) {
-    if (forceNow) {
-        await resetDownloadProposalRetry(ws, proposalId);
-    }
-    const proposal = await ws.db
-        .mktx((x) => ({ proposals: x.proposals }))
-        .runReadOnly(async (tx) => {
-        return tx.proposals.get(proposalId);
-    });
-    if (!proposal) {
-        return;
-    }
-    if (proposal.proposalStatus != ProposalStatus.DOWNLOADING) {
-        return;
-    }
-    const orderClaimUrl = new URL$1(`orders/${proposal.orderId}/claim`, 
proposal.merchantBaseUrl).href;
-    logger$c.trace("downloading contract from '" + orderClaimUrl + "'");
-    const requestBody = {
-        nonce: proposal.noncePub,
-    };
-    if (proposal.claimToken) {
-        requestBody.token = proposal.claimToken;
-    }
-    const httpResponse = await ws.http.postJson(orderClaimUrl, requestBody, {
-        timeout: getProposalRequestTimeout(proposal),
-    });
-    const r = await readSuccessResponseJsonOrErrorCode(httpResponse, 
codecForProposal());
-    if (r.isError) {
-        switch (r.talerErrorResponse.code) {
-            case TalerErrorCode.MERCHANT_POST_ORDERS_ID_CLAIM_ALREADY_CLAIMED:
-                throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED, 
"order already claimed (likely by other wallet)", {
-                    orderId: proposal.orderId,
-                    claimUrl: orderClaimUrl,
-                });
-            default:
-                throwUnexpectedRequestError(httpResponse, 
r.talerErrorResponse);
-        }
-    }
-    const proposalResp = r.response;
-    // The proposalResp contains the contract terms as raw JSON,
-    // as the coded to parse them doesn't necessarily round-trip.
-    // We need this raw JSON to compute the contract terms hash.
-    // FIXME: Do better error handling, check if the
-    // contract terms have all their forgettable information still
-    // present.  The wallet should never accept contract terms
-    // with missing information from the merchant.
-    const isWellFormed = 
ContractTermsUtil.validateForgettable(proposalResp.contract_terms);
-    if (!isWellFormed) {
-        logger$c.trace(`malformed contract terms: 
${j2s(proposalResp.contract_terms)}`);
-        const err = 
makeErrorDetails(TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED, "validation 
for well-formedness failed", {});
-        await failProposalPermanently(ws, proposalId, err);
-        throw new OperationFailedAndReportedError(err);
-    }
-    const contractTermsHash = 
ContractTermsUtil.hashContractTerms(proposalResp.contract_terms);
-    let parsedContractTerms;
-    try {
-        parsedContractTerms = 
codecForContractTerms().decode(proposalResp.contract_terms);
-    }
-    catch (e) {
-        const err = 
makeErrorDetails(TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED, "schema 
validation failed", {});
-        await failProposalPermanently(ws, proposalId, err);
-        throw new OperationFailedAndReportedError(err);
-    }
-    const sigValid = await 
ws.cryptoApi.isValidContractTermsSignature(contractTermsHash, proposalResp.sig, 
parsedContractTerms.merchant_pub);
-    if (!sigValid) {
-        const err = 
makeErrorDetails(TalerErrorCode.WALLET_CONTRACT_TERMS_SIGNATURE_INVALID, 
"merchant's signature on contract terms is invalid", {
-            merchantPub: parsedContractTerms.merchant_pub,
-            orderId: parsedContractTerms.order_id,
-        });
-        await failProposalPermanently(ws, proposalId, err);
-        throw new OperationFailedAndReportedError(err);
-    }
-    const fulfillmentUrl = parsedContractTerms.fulfillment_url;
-    const baseUrlForDownload = proposal.merchantBaseUrl;
-    const baseUrlFromContractTerms = parsedContractTerms.merchant_base_url;
-    if (baseUrlForDownload !== baseUrlFromContractTerms) {
-        const err = 
makeErrorDetails(TalerErrorCode.WALLET_CONTRACT_TERMS_BASE_URL_MISMATCH, 
"merchant base URL mismatch", {
-            baseUrlForDownload,
-            baseUrlFromContractTerms,
-        });
-        await failProposalPermanently(ws, proposalId, err);
-        throw new OperationFailedAndReportedError(err);
-    }
-    const contractData = extractContractData(parsedContractTerms, 
contractTermsHash, proposalResp.sig);
-    await ws.db
-        .mktx((x) => ({ proposals: x.proposals, purchases: x.purchases }))
-        .runReadWrite(async (tx) => {
-        const p = await tx.proposals.get(proposalId);
-        if (!p) {
-            return;
-        }
-        if (p.proposalStatus !== ProposalStatus.DOWNLOADING) {
-            return;
-        }
-        p.download = {
-            contractData,
-            contractTermsRaw: proposalResp.contract_terms,
-        };
-        if (fulfillmentUrl &&
-            (fulfillmentUrl.startsWith("http://";) ||
-                fulfillmentUrl.startsWith("https://";))) {
-            const differentPurchase = await 
tx.purchases.indexes.byFulfillmentUrl.get(fulfillmentUrl);
-            if (differentPurchase) {
-                logger$c.warn("repurchase detected");
-                p.proposalStatus = ProposalStatus.REPURCHASE;
-                p.repurchaseProposalId = differentPurchase.proposalId;
-                await tx.proposals.put(p);
-                return;
-            }
-        }
-        p.proposalStatus = ProposalStatus.PROPOSED;
-        await tx.proposals.put(p);
-    });
-    ws.notify({
-        type: NotificationType.ProposalDownloaded,
-        proposalId: proposal.proposalId,
-    });
-}
-/**
- * Download a proposal and store it in the database.
- * Returns an id for it to retrieve it later.
- *
- * @param sessionId Current session ID, if the proposal is being
- *  downloaded in the context of a session ID.
- */
-async function startDownloadProposal(ws, merchantBaseUrl, orderId, sessionId, 
claimToken) {
-    const oldProposal = await ws.db
-        .mktx((x) => ({ proposals: x.proposals }))
-        .runReadOnly(async (tx) => {
-        return tx.proposals.indexes.byUrlAndOrderId.get([
-            merchantBaseUrl,
-            orderId,
-        ]);
-    });
-    if (oldProposal) {
-        await processDownloadProposal(ws, oldProposal.proposalId);
-        return oldProposal.proposalId;
-    }
-    const { priv, pub } = await ws.cryptoApi.createEddsaKeypair();
-    const proposalId = encodeCrock(getRandomBytes(32));
-    const proposalRecord = {
-        download: undefined,
-        noncePriv: priv,
-        noncePub: pub,
-        claimToken,
-        timestamp: getTimestampNow(),
-        merchantBaseUrl,
-        orderId,
-        proposalId: proposalId,
-        proposalStatus: ProposalStatus.DOWNLOADING,
-        repurchaseProposalId: undefined,
-        retryInfo: initRetryInfo(),
-        lastError: undefined,
-        downloadSessionId: sessionId,
-    };
-    await ws.db
-        .mktx((x) => ({ proposals: x.proposals }))
-        .runReadWrite(async (tx) => {
-        const existingRecord = await tx.proposals.indexes.byUrlAndOrderId.get([
-            merchantBaseUrl,
-            orderId,
-        ]);
-        if (existingRecord) {
-            // Created concurrently
-            return;
-        }
-        await tx.proposals.put(proposalRecord);
-    });
-    await processDownloadProposal(ws, proposalId);
-    return proposalId;
-}
-async function storeFirstPaySuccess(ws, proposalId, sessionId, paySig) {
-    const now = getTimestampNow();
-    await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
-        .runReadWrite(async (tx) => {
-        const purchase = await tx.purchases.get(proposalId);
-        if (!purchase) {
-            logger$c.warn("purchase does not exist anymore");
-            return;
-        }
-        const isFirst = purchase.timestampFirstSuccessfulPay === undefined;
-        if (!isFirst) {
-            logger$c.warn("payment success already stored");
-            return;
-        }
-        purchase.timestampFirstSuccessfulPay = now;
-        purchase.paymentSubmitPending = false;
-        purchase.lastPayError = undefined;
-        purchase.lastSessionId = sessionId;
-        purchase.payRetryInfo = initRetryInfo(false);
-        purchase.merchantPaySig = paySig;
-        if (isFirst) {
-            const ar = purchase.download.contractData.autoRefund;
-            if (ar) {
-                logger$c.info("auto_refund present");
-                purchase.refundQueryRequested = true;
-                purchase.refundStatusRetryInfo = initRetryInfo();
-                purchase.lastRefundStatusError = undefined;
-                purchase.autoRefundDeadline = timestampAddDuration(now, ar);
-            }
-        }
-        await tx.purchases.put(purchase);
-    });
-}
-async function storePayReplaySuccess(ws, proposalId, sessionId) {
-    await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
-        .runReadWrite(async (tx) => {
-        const purchase = await tx.purchases.get(proposalId);
-        if (!purchase) {
-            logger$c.warn("purchase does not exist anymore");
-            return;
-        }
-        const isFirst = purchase.timestampFirstSuccessfulPay === undefined;
-        if (isFirst) {
-            throw Error("invalid payment state");
-        }
-        purchase.paymentSubmitPending = false;
-        purchase.lastPayError = undefined;
-        purchase.payRetryInfo = initRetryInfo(false);
-        purchase.lastSessionId = sessionId;
-        await tx.purchases.put(purchase);
-    });
-}
-/**
- * Handle a 409 Conflict response from the merchant.
- *
- * We do this by going through the coin history provided by the exchange and
- * (1) verifying the signatures from the exchange
- * (2) adjusting the remaining coin value and refreshing it
- * (3) re-do coin selection with the bad coin removed
- */
-async function handleInsufficientFunds(ws, proposalId, err) {
-    var _a;
-    logger$c.trace("handling insufficient funds, trying to re-select coins");
-    const proposal = await ws.db
-        .mktx((x) => ({ purchaes: x.purchases }))
-        .runReadOnly(async (tx) => {
-        return tx.purchaes.get(proposalId);
-    });
-    if (!proposal) {
-        return;
-    }
-    const brokenCoinPub = err.coin_pub;
-    const exchangeReply = err.exchange_reply;
-    if (exchangeReply.code !== 
TalerErrorCode.EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS) {
-        // FIXME: set as failed
-        throw Error("can't handle error code");
-    }
-    logger$c.trace(`got error details: ${j2s(err)}`);
-    const { contractData } = proposal.download;
-    const candidates = await getCandidatePayCoins(ws, {
-        allowedAuditors: contractData.allowedAuditors,
-        allowedExchanges: contractData.allowedExchanges,
-        amount: contractData.amount,
-        maxDepositFee: contractData.maxDepositFee,
-        maxWireFee: contractData.maxWireFee,
-        timestamp: contractData.timestamp,
-        wireFeeAmortization: contractData.wireFeeAmortization,
-        wireMethod: contractData.wireMethod,
-    });
-    const prevPayCoins = [];
-    await ws.db
-        .mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
-        .runReadOnly(async (tx) => {
-        for (let i = 0; i < proposal.payCoinSelection.coinPubs.length; i++) {
-            const coinPub = proposal.payCoinSelection.coinPubs[i];
-            if (coinPub === brokenCoinPub) {
-                continue;
-            }
-            const contrib = proposal.payCoinSelection.coinContributions[i];
-            const coin = await tx.coins.get(coinPub);
-            if (!coin) {
-                continue;
-            }
-            const denom = await tx.denominations.get([
-                coin.exchangeBaseUrl,
-                coin.denomPubHash,
-            ]);
-            if (!denom) {
-                continue;
-            }
-            prevPayCoins.push({
-                coinPub,
-                contribution: contrib,
-                exchangeBaseUrl: coin.exchangeBaseUrl,
-                feeDeposit: denom.feeDeposit,
-            });
-        }
-    });
-    const res = selectPayCoins({
-        candidates,
-        contractTermsAmount: contractData.amount,
-        depositFeeLimit: contractData.maxDepositFee,
-        wireFeeAmortization: (_a = contractData.wireFeeAmortization) !== null 
&& _a !== void 0 ? _a : 1,
-        wireFeeLimit: contractData.maxWireFee,
-        prevPayCoins,
-    });
-    if (!res) {
-        logger$c.trace("insufficient funds for coin re-selection");
-        return;
-    }
-    logger$c.trace("re-selected coins");
-    await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-        coins: x.coins,
-        denominations: x.denominations,
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const p = await tx.purchases.get(proposalId);
-        if (!p) {
-            return;
-        }
-        p.payCoinSelection = res;
-        p.coinDepositPermissions = undefined;
-        await tx.purchases.put(p);
-        await applyCoinSpend(ws, tx, res);
-    });
-}
-/**
- * Submit a payment to the merchant.
- *
- * If the wallet has previously paid, it just transmits the merchant's
- * own signature certifying that the wallet has previously paid.
- */
-async function submitPay(ws, proposalId) {
-    var _a, _b, _c, _d;
-    const purchase = await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
-        .runReadOnly(async (tx) => {
-        return tx.purchases.get(proposalId);
-    });
-    if (!purchase) {
-        throw Error("Purchase not found: " + proposalId);
-    }
-    if (purchase.abortStatus !== AbortStatus.None) {
-        throw Error("not submitting payment for aborted purchase");
-    }
-    const sessionId = purchase.lastSessionId;
-    logger$c.trace("paying with session ID", sessionId);
-    if (!purchase.merchantPaySig) {
-        const payUrl = new 
URL$1(`orders/${purchase.download.contractData.orderId}/pay`, 
purchase.download.contractData.merchantBaseUrl).href;
-        let depositPermissions;
-        if (purchase.coinDepositPermissions) {
-            depositPermissions = purchase.coinDepositPermissions;
-        }
-        else {
-            // FIXME: also cache!
-            depositPermissions = await generateDepositPermissions(ws, 
purchase.payCoinSelection, purchase.download.contractData);
-        }
-        const reqBody = {
-            coins: depositPermissions,
-            session_id: purchase.lastSessionId,
-        };
-        logger$c.trace("making pay request ... ", JSON.stringify(reqBody, 
undefined, 2));
-        const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], () => 
ws.http.postJson(payUrl, reqBody, {
-            timeout: getPayRequestTimeout(purchase),
-        }));
-        logger$c.trace(`got resp ${JSON.stringify(resp)}`);
-        // Hide transient errors.
-        if (((_b = (_a = purchase.payRetryInfo) === null || _a === void 0 ? 
void 0 : _a.retryCounter) !== null && _b !== void 0 ? _b : 0) <= 5 &&
-            resp.status >= 500 &&
-            resp.status <= 599) {
-            logger$c.trace("treating /pay error as transient");
-            const err = 
makeErrorDetails(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, "/pay failed", 
getHttpResponseErrorDetails(resp));
-            incrementPurchasePayRetry(ws, proposalId, undefined);
-            return {
-                type: ConfirmPayResultType.Pending,
-                lastError: err,
-            };
-        }
-        if (resp.status === HttpResponseStatus.Conflict) {
-            const err = await readTalerErrorResponse(resp);
-            if (err.code ===
-                TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS) 
{
-                // Do this in the background, as it might take some time
-                handleInsufficientFunds(ws, proposalId, err).catch(async (e) 
=> {
-                    await incrementProposalRetry(ws, proposalId, {
-                        code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
-                        message: "unexpected exception",
-                        hint: "unexpected exception",
-                        details: {
-                            exception: e.toString(),
-                        },
-                    });
-                });
-                return {
-                    type: ConfirmPayResultType.Pending,
-                    // FIXME: should we return something better here?
-                    lastError: err,
-                };
-            }
-        }
-        const merchantResp = await readSuccessResponseJsonOrThrow(resp, 
codecForMerchantPayResponse());
-        logger$c.trace("got success from pay URL", merchantResp);
-        const merchantPub = purchase.download.contractData.merchantPub;
-        const valid = await 
ws.cryptoApi.isValidPaymentSignature(merchantResp.sig, 
purchase.download.contractData.contractTermsHash, merchantPub);
-        if (!valid) {
-            logger$c.error("merchant payment signature invalid");
-            // FIXME: properly display error
-            throw Error("merchant payment signature invalid");
-        }
-        await storeFirstPaySuccess(ws, proposalId, sessionId, 
merchantResp.sig);
-    }
-    else {
-        const payAgainUrl = new 
URL$1(`orders/${purchase.download.contractData.orderId}/paid`, 
purchase.download.contractData.merchantBaseUrl).href;
-        const reqBody = {
-            sig: purchase.merchantPaySig,
-            h_contract: purchase.download.contractData.contractTermsHash,
-            session_id: sessionId !== null && sessionId !== void 0 ? sessionId 
: "",
-        };
-        const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], () => 
ws.http.postJson(payAgainUrl, reqBody));
-        // Hide transient errors.
-        if (((_d = (_c = purchase.payRetryInfo) === null || _c === void 0 ? 
void 0 : _c.retryCounter) !== null && _d !== void 0 ? _d : 0) <= 5 &&
-            resp.status >= 500 &&
-            resp.status <= 599) {
-            const err = 
makeErrorDetails(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, "/paid 
failed", getHttpResponseErrorDetails(resp));
-            incrementPurchasePayRetry(ws, proposalId, undefined);
-            return {
-                type: ConfirmPayResultType.Pending,
-                lastError: err,
-            };
-        }
-        if (resp.status !== 204) {
-            throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, 
"/paid failed", getHttpResponseErrorDetails(resp));
-        }
-        await storePayReplaySuccess(ws, proposalId, sessionId);
-    }
-    ws.notify({
-        type: NotificationType.PayOperationSuccess,
-        proposalId: purchase.proposalId,
-    });
-    return {
-        type: ConfirmPayResultType.Done,
-        contractTerms: purchase.download.contractTermsRaw,
-    };
-}
-async function checkPaymentByProposalId(ws, proposalId, sessionId) {
-    var _a;
-    let proposal = await ws.db
-        .mktx((x) => ({ proposals: x.proposals }))
-        .runReadOnly(async (tx) => {
-        return tx.proposals.get(proposalId);
-    });
-    if (!proposal) {
-        throw Error(`could not get proposal ${proposalId}`);
-    }
-    if (proposal.proposalStatus === ProposalStatus.REPURCHASE) {
-        const existingProposalId = proposal.repurchaseProposalId;
-        if (!existingProposalId) {
-            throw Error("invalid proposal state");
-        }
-        logger$c.trace("using existing purchase for same product");
-        proposal = await ws.db
-            .mktx((x) => ({ proposals: x.proposals }))
-            .runReadOnly(async (tx) => {
-            return tx.proposals.get(existingProposalId);
-        });
-        if (!proposal) {
-            throw Error("existing proposal is in wrong state");
-        }
-    }
-    const d = proposal.download;
-    if (!d) {
-        logger$c.error("bad proposal", proposal);
-        throw Error("proposal is in invalid state");
-    }
-    const contractData = d.contractData;
-    const merchantSig = d.contractData.merchantSig;
-    if (!merchantSig) {
-        throw Error("BUG: proposal is in invalid state");
-    }
-    proposalId = proposal.proposalId;
-    // First check if we already paid for it.
-    const purchase = await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
-        .runReadOnly(async (tx) => {
-        return tx.purchases.get(proposalId);
-    });
-    if (!purchase) {
-        // If not already paid, check if we could pay for it.
-        const candidates = await getCandidatePayCoins(ws, {
-            allowedAuditors: contractData.allowedAuditors,
-            allowedExchanges: contractData.allowedExchanges,
-            amount: contractData.amount,
-            maxDepositFee: contractData.maxDepositFee,
-            maxWireFee: contractData.maxWireFee,
-            timestamp: contractData.timestamp,
-            wireFeeAmortization: contractData.wireFeeAmortization,
-            wireMethod: contractData.wireMethod,
-        });
-        const res = selectPayCoins({
-            candidates,
-            contractTermsAmount: contractData.amount,
-            depositFeeLimit: contractData.maxDepositFee,
-            wireFeeAmortization: (_a = contractData.wireFeeAmortization) !== 
null && _a !== void 0 ? _a : 1,
-            wireFeeLimit: contractData.maxWireFee,
-            prevPayCoins: [],
-        });
-        if (!res) {
-            logger$c.info("not confirming payment, insufficient coins");
-            return {
-                status: PreparePayResultType.InsufficientBalance,
-                contractTerms: d.contractTermsRaw,
-                proposalId: proposal.proposalId,
-                amountRaw: Amounts.stringify(d.contractData.amount),
-            };
-        }
-        const totalCost = await getTotalPaymentCost(ws, res);
-        logger$c.trace("costInfo", totalCost);
-        logger$c.trace("coinsForPayment", res);
-        return {
-            status: PreparePayResultType.PaymentPossible,
-            contractTerms: d.contractTermsRaw,
-            proposalId: proposal.proposalId,
-            amountEffective: Amounts.stringify(totalCost),
-            amountRaw: Amounts.stringify(res.paymentAmount),
-        };
-    }
-    if (purchase.lastSessionId !== sessionId) {
-        logger$c.trace("automatically re-submitting payment with different 
session ID");
-        await ws.db
-            .mktx((x) => ({ purchases: x.purchases }))
-            .runReadWrite(async (tx) => {
-            const p = await tx.purchases.get(proposalId);
-            if (!p) {
-                return;
-            }
-            p.lastSessionId = sessionId;
-            await tx.purchases.put(p);
-        });
-        const r = await guardOperationException(() => submitPay(ws, 
proposalId), (e) => incrementPurchasePayRetry(ws, proposalId, e));
-        if (r.type !== ConfirmPayResultType.Done) {
-            throw Error("submitting pay failed");
-        }
-        return {
-            status: PreparePayResultType.AlreadyConfirmed,
-            contractTerms: purchase.download.contractTermsRaw,
-            contractTermsHash: 
purchase.download.contractData.contractTermsHash,
-            paid: true,
-            amountRaw: 
Amounts.stringify(purchase.download.contractData.amount),
-            amountEffective: Amounts.stringify(purchase.totalPayCost),
-            proposalId,
-        };
-    }
-    else if (!purchase.timestampFirstSuccessfulPay) {
-        return {
-            status: PreparePayResultType.AlreadyConfirmed,
-            contractTerms: purchase.download.contractTermsRaw,
-            contractTermsHash: 
purchase.download.contractData.contractTermsHash,
-            paid: false,
-            amountRaw: 
Amounts.stringify(purchase.download.contractData.amount),
-            amountEffective: Amounts.stringify(purchase.totalPayCost),
-            proposalId,
-        };
-    }
-    else {
-        const paid = !purchase.paymentSubmitPending;
-        return Object.assign(Object.assign({ status: 
PreparePayResultType.AlreadyConfirmed, contractTerms: 
purchase.download.contractTermsRaw, contractTermsHash: 
purchase.download.contractData.contractTermsHash, paid, amountRaw: 
Amounts.stringify(purchase.download.contractData.amount), amountEffective: 
Amounts.stringify(purchase.totalPayCost) }, (paid ? { nextUrl: 
purchase.download.contractData.orderId } : {})), { proposalId });
-    }
-}
-/**
- * Check if a payment for the given taler://pay/ URI is possible.
- *
- * If the payment is possible, the signature are already generated but not
- * yet send to the merchant.
- */
-async function preparePayForUri(ws, talerPayUri) {
-    const uriResult = parsePayUri(talerPayUri);
-    if (!uriResult) {
-        throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_INVALID_TALER_PAY_URI, 
`invalid taler://pay URI (${talerPayUri})`, {
-            talerPayUri,
-        });
-    }
-    let proposalId = await startDownloadProposal(ws, 
uriResult.merchantBaseUrl, uriResult.orderId, uriResult.sessionId, 
uriResult.claimToken);
-    return checkPaymentByProposalId(ws, proposalId, uriResult.sessionId);
-}
-/**
- * Generate deposit permissions for a purchase.
- *
- * Accesses the database and the crypto worker.
- */
-async function generateDepositPermissions(ws, payCoinSel, contractData) {
-    const depositPermissions = [];
-    const coinWithDenom = [];
-    await ws.db
-        .mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
-        .runReadOnly(async (tx) => {
-        for (let i = 0; i < payCoinSel.coinPubs.length; i++) {
-            const coin = await tx.coins.get(payCoinSel.coinPubs[i]);
-            if (!coin) {
-                throw Error("can't pay, allocated coin not found anymore");
-            }
-            const denom = await tx.denominations.get([
-                coin.exchangeBaseUrl,
-                coin.denomPubHash,
-            ]);
-            if (!denom) {
-                throw Error("can't pay, denomination of allocated coin not 
found anymore");
-            }
-            coinWithDenom.push({ coin, denom });
-        }
-    });
-    for (let i = 0; i < payCoinSel.coinPubs.length; i++) {
-        const { coin, denom } = coinWithDenom[i];
-        const dp = await ws.cryptoApi.signDepositPermission({
-            coinPriv: coin.coinPriv,
-            coinPub: coin.coinPub,
-            contractTermsHash: contractData.contractTermsHash,
-            denomPubHash: coin.denomPubHash,
-            denomSig: coin.denomSig,
-            exchangeBaseUrl: coin.exchangeBaseUrl,
-            feeDeposit: denom.feeDeposit,
-            merchantPub: contractData.merchantPub,
-            refundDeadline: contractData.refundDeadline,
-            spendAmount: payCoinSel.coinContributions[i],
-            timestamp: contractData.timestamp,
-            wireInfoHash: contractData.wireInfoHash,
-        });
-        depositPermissions.push(dp);
-    }
-    return depositPermissions;
-}
-/**
- * Add a contract to the wallet and sign coins, and send them.
- */
-async function confirmPay(ws, proposalId, sessionIdOverride) {
-    var _a;
-    logger$c.trace(`executing confirmPay with proposalId ${proposalId} and 
sessionIdOverride ${sessionIdOverride}`);
-    const proposal = await ws.db
-        .mktx((x) => ({ proposals: x.proposals }))
-        .runReadOnly(async (tx) => {
-        return tx.proposals.get(proposalId);
-    });
-    if (!proposal) {
-        throw Error(`proposal with id ${proposalId} not found`);
-    }
-    const d = proposal.download;
-    if (!d) {
-        throw Error("proposal is in invalid state");
-    }
-    const existingPurchase = await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
-        .runReadWrite(async (tx) => {
-        const purchase = await tx.purchases.get(proposalId);
-        if (purchase &&
-            sessionIdOverride !== undefined &&
-            sessionIdOverride != purchase.lastSessionId) {
-            logger$c.trace(`changing session ID to ${sessionIdOverride}`);
-            purchase.lastSessionId = sessionIdOverride;
-            purchase.paymentSubmitPending = true;
-            await tx.purchases.put(purchase);
-        }
-        return purchase;
-    });
-    if (existingPurchase) {
-        logger$c.trace("confirmPay: submitting payment for existing purchase");
-        return await guardOperationException(() => submitPay(ws, proposalId), 
(e) => incrementPurchasePayRetry(ws, proposalId, e));
-    }
-    logger$c.trace("confirmPay: purchase record does not exist yet");
-    const contractData = d.contractData;
-    const candidates = await getCandidatePayCoins(ws, {
-        allowedAuditors: contractData.allowedAuditors,
-        allowedExchanges: contractData.allowedExchanges,
-        amount: contractData.amount,
-        maxDepositFee: contractData.maxDepositFee,
-        maxWireFee: contractData.maxWireFee,
-        timestamp: contractData.timestamp,
-        wireFeeAmortization: contractData.wireFeeAmortization,
-        wireMethod: contractData.wireMethod,
-    });
-    const res = selectPayCoins({
-        candidates,
-        contractTermsAmount: contractData.amount,
-        depositFeeLimit: contractData.maxDepositFee,
-        wireFeeAmortization: (_a = contractData.wireFeeAmortization) !== null 
&& _a !== void 0 ? _a : 1,
-        wireFeeLimit: contractData.maxWireFee,
-        prevPayCoins: [],
-    });
-    logger$c.trace("coin selection result", res);
-    if (!res) {
-        // Should not happen, since checkPay should be called first
-        // FIXME: Actually, this should be handled gracefully,
-        // and the status should be stored in the DB.
-        logger$c.warn("not confirming payment, insufficient coins");
-        throw Error("insufficient balance");
-    }
-    const depositPermissions = await generateDepositPermissions(ws, res, 
d.contractData);
-    await recordConfirmPay(ws, proposal, res, depositPermissions, 
sessionIdOverride);
-    return await guardOperationException(() => submitPay(ws, proposalId), (e) 
=> incrementPurchasePayRetry(ws, proposalId, e));
-}
-async function processPurchasePay(ws, proposalId, forceNow = false) {
-    const onOpErr = (e) => incrementPurchasePayRetry(ws, proposalId, e);
-    await guardOperationException(() => processPurchasePayImpl(ws, proposalId, 
forceNow), onOpErr);
-}
-async function resetPurchasePayRetry(ws, proposalId) {
-    await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
-        .runReadWrite(async (tx) => {
-        const p = await tx.purchases.get(proposalId);
-        if (p) {
-            p.payRetryInfo = initRetryInfo();
-            await tx.purchases.put(p);
-        }
-    });
-}
-async function processPurchasePayImpl(ws, proposalId, forceNow) {
-    if (forceNow) {
-        await resetPurchasePayRetry(ws, proposalId);
-    }
-    const purchase = await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
-        .runReadOnly(async (tx) => {
-        return tx.purchases.get(proposalId);
-    });
-    if (!purchase) {
-        return;
-    }
-    if (!purchase.paymentSubmitPending) {
-        return;
-    }
-    logger$c.trace(`processing purchase pay ${proposalId}`);
-    await submitPay(ws, proposalId);
-}
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems SA
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-async function provideBackupState(ws) {
-    const bs = await ws.db
-        .mktx((x) => ({
-        config: x.config,
-    }))
-        .runReadOnly(async (tx) => {
-        return await tx.config.get(WALLET_BACKUP_STATE_KEY);
-    });
-    if (bs) {
-        checkDbInvariant(bs.key === WALLET_BACKUP_STATE_KEY);
-        return bs.value;
-    }
-    // We need to generate the key outside of the transaction
-    // due to how IndexedDB works.
-    const k = await ws.cryptoApi.createEddsaKeypair();
-    const d = getRandomBytes(5);
-    // FIXME: device ID should be configured when wallet is initialized
-    // and be based on hostname
-    const deviceId = `wallet-core-${encodeCrock(d)}`;
-    return await ws.db
-        .mktx((x) => ({
-        config: x.config,
-    }))
-        .runReadWrite(async (tx) => {
-        let backupStateEntry = await tx.config.get(WALLET_BACKUP_STATE_KEY);
-        if (!backupStateEntry) {
-            backupStateEntry = {
-                key: WALLET_BACKUP_STATE_KEY,
-                value: {
-                    deviceId,
-                    walletRootPub: k.pub,
-                    walletRootPriv: k.priv,
-                    lastBackupPlainHash: undefined,
-                },
-            };
-            await tx.config.put(backupStateEntry);
-        }
-        checkDbInvariant(backupStateEntry.key === WALLET_BACKUP_STATE_KEY);
-        return backupStateEntry.value;
-    });
-}
-async function getWalletBackupState(ws, tx) {
-    const bs = await tx.config.get(WALLET_BACKUP_STATE_KEY);
-    checkDbInvariant(!!bs, "wallet backup state should be in DB");
-    checkDbInvariant(bs.key === WALLET_BACKUP_STATE_KEY);
-    return bs.value;
-}
-async function setWalletDeviceId(ws, deviceId) {
-    await provideBackupState(ws);
-    await ws.db
-        .mktx((x) => ({
-        config: x.config,
-    }))
-        .runReadWrite(async (tx) => {
-        let backupStateEntry = await tx.config.get(WALLET_BACKUP_STATE_KEY);
-        if (!backupStateEntry ||
-            backupStateEntry.key !== WALLET_BACKUP_STATE_KEY) {
-            return;
-        }
-        backupStateEntry.value.deviceId = deviceId;
-        await tx.config.put(backupStateEntry);
-    });
-}
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems SA
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-async function exportBackup(ws) {
-    await provideBackupState(ws);
-    return ws.db
-        .mktx((x) => ({
-        config: x.config,
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-        coins: x.coins,
-        denominations: x.denominations,
-        purchases: x.purchases,
-        proposals: x.proposals,
-        refreshGroups: x.refreshGroups,
-        backupProviders: x.backupProviders,
-        tips: x.tips,
-        recoupGroups: x.recoupGroups,
-        reserves: x.reserves,
-        withdrawalGroups: x.withdrawalGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const bs = await getWalletBackupState(ws, tx);
-        const backupExchangeDetails = [];
-        const backupExchanges = [];
-        const backupCoinsByDenom = {};
-        const backupDenominationsByExchange = {};
-        const backupReservesByExchange = {};
-        const backupPurchases = [];
-        const backupProposals = [];
-        const backupRefreshGroups = [];
-        const backupBackupProviders = [];
-        const backupTips = [];
-        const backupRecoupGroups = [];
-        const withdrawalGroupsByReserve = {};
-        await tx.withdrawalGroups.iter().forEachAsync(async (wg) => {
-            var _a;
-            var _b;
-            const withdrawalGroups = ((_a = withdrawalGroupsByReserve[_b = 
wg.reservePub]) !== null && _a !== void 0 ? _a : (withdrawalGroupsByReserve[_b] 
= []));
-            withdrawalGroups.push({
-                raw_withdrawal_amount: 
Amounts.stringify(wg.rawWithdrawalAmount),
-                selected_denoms: wg.denomsSel.selectedDenoms.map((x) => ({
-                    count: x.count,
-                    denom_pub_hash: x.denomPubHash,
-                })),
-                timestamp_created: wg.timestampStart,
-                timestamp_finish: wg.timestampFinish,
-                withdrawal_group_id: wg.withdrawalGroupId,
-                secret_seed: wg.secretSeed,
-                selected_denoms_id: wg.denomSelUid,
-            });
-        });
-        await tx.reserves.iter().forEach((reserve) => {
-            var _a, _b;
-            var _c;
-            const backupReserve = {
-                initial_selected_denoms: 
reserve.initialDenomSel.selectedDenoms.map((x) => ({
-                    count: x.count,
-                    denom_pub_hash: x.denomPubHash,
-                })),
-                initial_withdrawal_group_id: reserve.initialWithdrawalGroupId,
-                instructed_amount: Amounts.stringify(reserve.instructedAmount),
-                reserve_priv: reserve.reservePriv,
-                timestamp_created: reserve.timestampCreated,
-                withdrawal_groups: (_a = 
withdrawalGroupsByReserve[reserve.reservePub]) !== null && _a !== void 0 ? _a : 
[],
-                // FIXME!
-                timestamp_last_activity: reserve.timestampCreated,
-            };
-            const backupReserves = ((_b = backupReservesByExchange[_c = 
reserve.exchangeBaseUrl]) !== null && _b !== void 0 ? _b : 
(backupReservesByExchange[_c] = []));
-            backupReserves.push(backupReserve);
-        });
-        await tx.tips.iter().forEach((tip) => {
-            backupTips.push({
-                exchange_base_url: tip.exchangeBaseUrl,
-                merchant_base_url: tip.merchantBaseUrl,
-                merchant_tip_id: tip.merchantTipId,
-                wallet_tip_id: tip.walletTipId,
-                secret_seed: tip.secretSeed,
-                selected_denoms: tip.denomsSel.selectedDenoms.map((x) => ({
-                    count: x.count,
-                    denom_pub_hash: x.denomPubHash,
-                })),
-                timestamp_finished: tip.pickedUpTimestamp,
-                timestamp_accepted: tip.acceptedTimestamp,
-                timestamp_created: tip.createdTimestamp,
-                timestamp_expiration: tip.tipExpiration,
-                tip_amount_raw: Amounts.stringify(tip.tipAmountRaw),
-                selected_denoms_uid: tip.denomSelUid,
-            });
-        });
-        await tx.recoupGroups.iter().forEach((recoupGroup) => {
-            backupRecoupGroups.push({
-                recoup_group_id: recoupGroup.recoupGroupId,
-                timestamp_created: recoupGroup.timestampStarted,
-                timestamp_finish: recoupGroup.timestampFinished,
-                coins: recoupGroup.coinPubs.map((x, i) => ({
-                    coin_pub: x,
-                    recoup_finished: recoupGroup.recoupFinishedPerCoin[i],
-                    old_amount: 
Amounts.stringify(recoupGroup.oldAmountPerCoin[i]),
-                })),
-            });
-        });
-        await tx.backupProviders.iter().forEach((bp) => {
-            let terms;
-            if (bp.terms) {
-                terms = {
-                    annual_fee: Amounts.stringify(bp.terms.annualFee),
-                    storage_limit_in_megabytes: 
bp.terms.storageLimitInMegabytes,
-                    supported_protocol_version: 
bp.terms.supportedProtocolVersion,
-                };
-            }
-            backupBackupProviders.push({
-                terms,
-                base_url: canonicalizeBaseUrl(bp.baseUrl),
-                pay_proposal_ids: bp.paymentProposalIds,
-                uids: bp.uids,
-            });
-        });
-        await tx.coins.iter().forEach((coin) => {
-            var _a;
-            var _b;
-            let bcs;
-            switch (coin.coinSource.type) {
-                case CoinSourceType.Refresh:
-                    bcs = {
-                        type: BackupCoinSourceType.Refresh,
-                        old_coin_pub: coin.coinSource.oldCoinPub,
-                    };
-                    break;
-                case CoinSourceType.Tip:
-                    bcs = {
-                        type: BackupCoinSourceType.Tip,
-                        coin_index: coin.coinSource.coinIndex,
-                        wallet_tip_id: coin.coinSource.walletTipId,
-                    };
-                    break;
-                case CoinSourceType.Withdraw:
-                    bcs = {
-                        type: BackupCoinSourceType.Withdraw,
-                        coin_index: coin.coinSource.coinIndex,
-                        reserve_pub: coin.coinSource.reservePub,
-                        withdrawal_group_id: coin.coinSource.withdrawalGroupId,
-                    };
-                    break;
-            }
-            const coins = ((_a = backupCoinsByDenom[_b = coin.denomPubHash]) 
!== null && _a !== void 0 ? _a : (backupCoinsByDenom[_b] = []));
-            coins.push({
-                blinding_key: coin.blindingKey,
-                coin_priv: coin.coinPriv,
-                coin_source: bcs,
-                current_amount: Amounts.stringify(coin.currentAmount),
-                fresh: coin.status === CoinStatus.Fresh,
-                denom_sig: coin.denomSig,
-            });
-        });
-        await tx.denominations.iter().forEach((denom) => {
-            var _a, _b;
-            var _c;
-            const backupDenoms = ((_a = backupDenominationsByExchange[_c = 
denom.exchangeBaseUrl]) !== null && _a !== void 0 ? _a : 
(backupDenominationsByExchange[_c] = []));
-            backupDenoms.push({
-                coins: (_b = backupCoinsByDenom[denom.denomPubHash]) !== null 
&& _b !== void 0 ? _b : [],
-                denom_pub: denom.denomPub,
-                fee_deposit: Amounts.stringify(denom.feeDeposit),
-                fee_refresh: Amounts.stringify(denom.feeRefresh),
-                fee_refund: Amounts.stringify(denom.feeRefund),
-                fee_withdraw: Amounts.stringify(denom.feeWithdraw),
-                is_offered: denom.isOffered,
-                is_revoked: denom.isRevoked,
-                master_sig: denom.masterSig,
-                stamp_expire_deposit: denom.stampExpireDeposit,
-                stamp_expire_legal: denom.stampExpireLegal,
-                stamp_expire_withdraw: denom.stampExpireWithdraw,
-                stamp_start: denom.stampStart,
-                value: Amounts.stringify(denom.value),
-            });
-        });
-        await tx.exchanges.iter().forEachAsync(async (ex) => {
-            const dp = ex.detailsPointer;
-            if (!dp) {
-                return;
-            }
-            backupExchanges.push({
-                base_url: ex.baseUrl,
-                currency: dp.currency,
-                master_public_key: dp.masterPublicKey,
-                update_clock: dp.updateClock,
-            });
-        });
-        await tx.exchangeDetails.iter().forEachAsync(async (ex) => {
-            // Only back up permanently added exchanges.
-            var _a, _b;
-            const wi = ex.wireInfo;
-            const wireFees = [];
-            Object.keys(wi.feesForType).forEach((x) => {
-                for (const f of wi.feesForType[x]) {
-                    wireFees.push({
-                        wire_type: x,
-                        closing_fee: Amounts.stringify(f.closingFee),
-                        end_stamp: f.endStamp,
-                        sig: f.sig,
-                        start_stamp: f.startStamp,
-                        wire_fee: Amounts.stringify(f.wireFee),
-                    });
-                }
-            });
-            backupExchangeDetails.push({
-                base_url: ex.exchangeBaseUrl,
-                reserve_closing_delay: ex.reserveClosingDelay,
-                accounts: ex.wireInfo.accounts.map((x) => ({
-                    payto_uri: x.payto_uri,
-                    master_sig: x.master_sig,
-                })),
-                auditors: ex.auditors.map((x) => ({
-                    auditor_pub: x.auditor_pub,
-                    auditor_url: x.auditor_url,
-                    denomination_keys: x.denomination_keys,
-                })),
-                master_public_key: ex.masterPublicKey,
-                currency: ex.currency,
-                protocol_version: ex.protocolVersion,
-                wire_fees: wireFees,
-                signing_keys: ex.signingKeys.map((x) => ({
-                    key: x.key,
-                    master_sig: x.master_sig,
-                    stamp_end: x.stamp_end,
-                    stamp_expire: x.stamp_expire,
-                    stamp_start: x.stamp_start,
-                })),
-                tos_accepted_etag: ex.termsOfServiceAcceptedEtag,
-                tos_accepted_timestamp: ex.termsOfServiceAcceptedTimestamp,
-                denominations: (_a = 
backupDenominationsByExchange[ex.exchangeBaseUrl]) !== null && _a !== void 0 ? 
_a : [],
-                reserves: (_b = backupReservesByExchange[ex.exchangeBaseUrl]) 
!== null && _b !== void 0 ? _b : [],
-            });
-        });
-        const purchaseProposalIdSet = new Set();
-        await tx.purchases.iter().forEach((purch) => {
-            const refunds = [];
-            purchaseProposalIdSet.add(purch.proposalId);
-            for (const refundKey of Object.keys(purch.refunds)) {
-                const ri = purch.refunds[refundKey];
-                const common = {
-                    coin_pub: ri.coinPub,
-                    execution_time: ri.executionTime,
-                    obtained_time: ri.obtainedTime,
-                    refund_amount: Amounts.stringify(ri.refundAmount),
-                    rtransaction_id: ri.rtransactionId,
-                    total_refresh_cost_bound: 
Amounts.stringify(ri.totalRefreshCostBound),
-                };
-                switch (ri.type) {
-                    case RefundState.Applied:
-                        refunds.push(Object.assign({ type: 
BackupRefundState.Applied }, common));
-                        break;
-                    case RefundState.Failed:
-                        refunds.push(Object.assign({ type: 
BackupRefundState.Failed }, common));
-                        break;
-                    case RefundState.Pending:
-                        refunds.push(Object.assign({ type: 
BackupRefundState.Pending }, common));
-                        break;
-                }
-            }
-            backupPurchases.push({
-                contract_terms_raw: purch.download.contractTermsRaw,
-                auto_refund_deadline: purch.autoRefundDeadline,
-                merchant_pay_sig: purch.merchantPaySig,
-                pay_coins: purch.payCoinSelection.coinPubs.map((x, i) => ({
-                    coin_pub: x,
-                    contribution: 
Amounts.stringify(purch.payCoinSelection.coinContributions[i]),
-                })),
-                proposal_id: purch.proposalId,
-                refunds,
-                timestamp_accept: purch.timestampAccept,
-                timestamp_first_successful_pay: 
purch.timestampFirstSuccessfulPay,
-                abort_status: purch.abortStatus === AbortStatus.None
-                    ? undefined
-                    : purch.abortStatus,
-                nonce_priv: purch.noncePriv,
-                merchant_sig: purch.download.contractData.merchantSig,
-                total_pay_cost: Amounts.stringify(purch.totalPayCost),
-                pay_coins_uid: purch.payCoinSelectionUid,
-            });
-        });
-        await tx.proposals.iter().forEach((prop) => {
-            var _a, _b;
-            if (purchaseProposalIdSet.has(prop.proposalId)) {
-                return;
-            }
-            let propStatus;
-            switch (prop.proposalStatus) {
-                case ProposalStatus.ACCEPTED:
-                    return;
-                case ProposalStatus.DOWNLOADING:
-                case ProposalStatus.PROPOSED:
-                    propStatus = BackupProposalStatus.Proposed;
-                    break;
-                case ProposalStatus.PERMANENTLY_FAILED:
-                    propStatus = BackupProposalStatus.PermanentlyFailed;
-                    break;
-                case ProposalStatus.REFUSED:
-                    propStatus = BackupProposalStatus.Refused;
-                    break;
-                case ProposalStatus.REPURCHASE:
-                    propStatus = BackupProposalStatus.Repurchase;
-                    break;
-            }
-            backupProposals.push({
-                claim_token: prop.claimToken,
-                nonce_priv: prop.noncePriv,
-                proposal_id: prop.noncePriv,
-                proposal_status: propStatus,
-                repurchase_proposal_id: prop.repurchaseProposalId,
-                timestamp: prop.timestamp,
-                contract_terms_raw: (_a = prop.download) === null || _a === 
void 0 ? void 0 : _a.contractTermsRaw,
-                download_session_id: prop.downloadSessionId,
-                merchant_base_url: prop.merchantBaseUrl,
-                order_id: prop.orderId,
-                merchant_sig: (_b = prop.download) === null || _b === void 0 ? 
void 0 : _b.contractData.merchantSig,
-            });
-        });
-        await tx.refreshGroups.iter().forEach((rg) => {
-            const oldCoins = [];
-            for (let i = 0; i < rg.oldCoinPubs.length; i++) {
-                let refreshSession;
-                const s = rg.refreshSessionPerCoin[i];
-                if (s) {
-                    refreshSession = {
-                        new_denoms: s.newDenoms.map((x) => ({
-                            count: x.count,
-                            denom_pub_hash: x.denomPubHash,
-                        })),
-                        session_secret_seed: s.sessionSecretSeed,
-                        noreveal_index: s.norevealIndex,
-                    };
-                }
-                oldCoins.push({
-                    coin_pub: rg.oldCoinPubs[i],
-                    estimated_output_amount: 
Amounts.stringify(rg.estimatedOutputPerCoin[i]),
-                    finished: rg.finishedPerCoin[i],
-                    input_amount: Amounts.stringify(rg.inputPerCoin[i]),
-                    refresh_session: refreshSession,
-                });
-            }
-            backupRefreshGroups.push({
-                reason: rg.reason,
-                refresh_group_id: rg.refreshGroupId,
-                timestamp_created: rg.timestampCreated,
-                timestamp_finish: rg.timestampFinished,
-                old_coins: oldCoins,
-            });
-        });
-        if (!bs.lastBackupTimestamp) {
-            bs.lastBackupTimestamp = getTimestampNow();
-        }
-        const backupBlob = {
-            schema_id: "gnu-taler-wallet-backup-content",
-            schema_version: 1,
-            exchanges: backupExchanges,
-            exchange_details: backupExchangeDetails,
-            wallet_root_pub: bs.walletRootPub,
-            backup_providers: backupBackupProviders,
-            current_device_id: bs.deviceId,
-            proposals: backupProposals,
-            purchases: backupPurchases,
-            recoup_groups: backupRecoupGroups,
-            refresh_groups: backupRefreshGroups,
-            tips: backupTips,
-            timestamp: bs.lastBackupTimestamp,
-            trusted_auditors: {},
-            trusted_exchanges: {},
-            intern_table: {},
-            error_reports: [],
-            tombstones: [],
-        };
-        // If the backup changed, we increment our clock.
-        let h = encodeCrock(hash$1(stringToBytes(canonicalJson(backupBlob))));
-        if (h != bs.lastBackupPlainHash) {
-            bs.lastBackupPlainHash = 
encodeCrock(hash$1(stringToBytes(canonicalJson(backupBlob))));
-            bs.lastBackupNonce = encodeCrock(getRandomBytes(32));
-            await tx.config.put({
-                key: WALLET_BACKUP_STATE_KEY,
-                value: bs,
-            });
-        }
-        return backupBlob;
-    });
-}
-
-/*
- This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Logger.
- */
-const logger$b = new Logger("deposits.ts");
-const codecForDepositSuccess = () => buildCodecForObject()
-    .property("exchange_pub", codecForString())
-    .property("exchange_sig", codecForString())
-    .property("exchange_timestamp", codecForTimestamp)
-    .property("transaction_base_url", codecOptional(codecForString()))
-    .build("DepositSuccess");
-function hashWire(paytoUri, salt) {
-    const r = kdf(64, stringToBytes(paytoUri + "\0"), stringToBytes(salt + 
"\0"), stringToBytes("merchant-wire-signature"));
-    return encodeCrock(r);
-}
-async function resetDepositGroupRetry(ws, depositGroupId) {
-    await ws.db
-        .mktx((x) => ({
-        depositGroups: x.depositGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const x = await tx.depositGroups.get(depositGroupId);
-        if (x) {
-            x.retryInfo = initRetryInfo();
-            await tx.depositGroups.put(x);
-        }
-    });
-}
-async function incrementDepositRetry(ws, depositGroupId, err) {
-    await ws.db
-        .mktx((x) => ({ depositGroups: x.depositGroups }))
-        .runReadWrite(async (tx) => {
-        const r = await tx.depositGroups.get(depositGroupId);
-        if (!r) {
-            return;
-        }
-        if (!r.retryInfo) {
-            return;
-        }
-        r.retryInfo.retryCounter++;
-        updateRetryInfoTimeout(r.retryInfo);
-        r.lastError = err;
-        await tx.depositGroups.put(r);
-    });
-    if (err) {
-        ws.notify({ type: NotificationType.DepositOperationError, error: err 
});
-    }
-}
-async function processDepositGroup(ws, depositGroupId, forceNow = false) {
-    await ws.memoProcessDeposit.memo(depositGroupId, async () => {
-        const onOpErr = (e) => incrementDepositRetry(ws, depositGroupId, e);
-        return await guardOperationException(async () => await 
processDepositGroupImpl(ws, depositGroupId, forceNow), onOpErr);
-    });
-}
-async function processDepositGroupImpl(ws, depositGroupId, forceNow = false) {
-    if (forceNow) {
-        await resetDepositGroupRetry(ws, depositGroupId);
-    }
-    const depositGroup = await ws.db
-        .mktx((x) => ({
-        depositGroups: x.depositGroups,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.depositGroups.get(depositGroupId);
-    });
-    if (!depositGroup) {
-        logger$b.warn(`deposit group ${depositGroupId} not found`);
-        return;
-    }
-    if (depositGroup.timestampFinished) {
-        logger$b.trace(`deposit group ${depositGroupId} already finished`);
-        return;
-    }
-    const contractData = extractContractData(depositGroup.contractTermsRaw, 
depositGroup.contractTermsHash, "");
-    const depositPermissions = await generateDepositPermissions(ws, 
depositGroup.payCoinSelection, contractData);
-    for (let i = 0; i < depositPermissions.length; i++) {
-        if (depositGroup.depositedPerCoin[i]) {
-            continue;
-        }
-        const perm = depositPermissions[i];
-        const url = new URL$1(`/coins/${perm.coin_pub}/deposit`, 
perm.exchange_url);
-        const httpResp = await ws.http.postJson(url.href, {
-            contribution: Amounts.stringify(perm.contribution),
-            wire: depositGroup.wire,
-            h_wire: depositGroup.contractTermsRaw.h_wire,
-            h_contract_terms: depositGroup.contractTermsHash,
-            ub_sig: perm.ub_sig,
-            timestamp: depositGroup.contractTermsRaw.timestamp,
-            wire_transfer_deadline: 
depositGroup.contractTermsRaw.wire_transfer_deadline,
-            refund_deadline: depositGroup.contractTermsRaw.refund_deadline,
-            coin_sig: perm.coin_sig,
-            denom_pub_hash: perm.h_denom,
-            merchant_pub: depositGroup.merchantPub,
-        });
-        await readSuccessResponseJsonOrThrow(httpResp, 
codecForDepositSuccess());
-        await ws.db
-            .mktx((x) => ({ depositGroups: x.depositGroups }))
-            .runReadWrite(async (tx) => {
-            const dg = await tx.depositGroups.get(depositGroupId);
-            if (!dg) {
-                return;
-            }
-            dg.depositedPerCoin[i] = true;
-            await tx.depositGroups.put(dg);
-        });
-    }
-    await ws.db
-        .mktx((x) => ({
-        depositGroups: x.depositGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const dg = await tx.depositGroups.get(depositGroupId);
-        if (!dg) {
-            return;
-        }
-        let allDeposited = true;
-        for (const d of depositGroup.depositedPerCoin) {
-            if (!d) {
-                allDeposited = false;
-            }
-        }
-        if (allDeposited) {
-            dg.timestampFinished = getTimestampNow();
-            await tx.depositGroups.put(dg);
-        }
-    });
-}
-async function trackDepositGroup(ws, req) {
-    const responses = [];
-    const depositGroup = await ws.db
-        .mktx((x) => ({
-        depositGroups: x.depositGroups,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.depositGroups.get(req.depositGroupId);
-    });
-    if (!depositGroup) {
-        throw Error("deposit group not found");
-    }
-    const contractData = extractContractData(depositGroup.contractTermsRaw, 
depositGroup.contractTermsHash, "");
-    const depositPermissions = await generateDepositPermissions(ws, 
depositGroup.payCoinSelection, contractData);
-    const wireHash = depositGroup.contractTermsRaw.h_wire;
-    for (const dp of depositPermissions) {
-        const url = new 
URL$1(`/deposits/${wireHash}/${depositGroup.merchantPub}/${depositGroup.contractTermsHash}/${dp.coin_pub}`,
 dp.exchange_url);
-        const sig = await ws.cryptoApi.signTrackTransaction({
-            coinPub: dp.coin_pub,
-            contractTermsHash: depositGroup.contractTermsHash,
-            merchantPriv: depositGroup.merchantPriv,
-            merchantPub: depositGroup.merchantPub,
-            wireHash,
-        });
-        url.searchParams.set("merchant_sig", sig);
-        const httpResp = await ws.http.get(url.href);
-        const body = await httpResp.json();
-        responses.push({
-            body,
-            status: httpResp.status,
-        });
-    }
-    return {
-        responses,
-    };
-}
-async function createDepositGroup(ws, req) {
-    var _a;
-    const p = parsePaytoUri(req.depositPaytoUri);
-    if (!p) {
-        throw Error("invalid payto URI");
-    }
-    const amount = Amounts.parseOrThrow(req.amount);
-    const exchangeInfos = [];
-    await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-    }))
-        .runReadOnly(async (tx) => {
-        const allExchanges = await tx.exchanges.iter().toArray();
-        for (const e of allExchanges) {
-            const details = await getExchangeDetails(tx, e.baseUrl);
-            if (!details) {
-                continue;
-            }
-            exchangeInfos.push({
-                master_pub: details.masterPublicKey,
-                url: e.baseUrl,
-            });
-        }
-    });
-    const timestamp = getTimestampNow();
-    const timestampRound = timestampTruncateToSecond(timestamp);
-    const noncePair = await ws.cryptoApi.createEddsaKeypair();
-    const merchantPair = await ws.cryptoApi.createEddsaKeypair();
-    const wireSalt = encodeCrock(getRandomBytes(64));
-    const wireHash = hashWire(req.depositPaytoUri, wireSalt);
-    const contractTerms = {
-        auditors: [],
-        exchanges: exchangeInfos,
-        amount: req.amount,
-        max_fee: Amounts.stringify(amount),
-        max_wire_fee: Amounts.stringify(amount),
-        wire_method: p.targetType,
-        timestamp: timestampRound,
-        merchant_base_url: "",
-        summary: "",
-        nonce: noncePair.pub,
-        wire_transfer_deadline: timestampRound,
-        order_id: "",
-        h_wire: wireHash,
-        pay_deadline: timestampAddDuration(timestampRound, durationFromSpec({ 
hours: 1 })),
-        merchant: {
-            name: "",
-        },
-        merchant_pub: merchantPair.pub,
-        refund_deadline: { t_ms: 0 },
-    };
-    const contractTermsHash = await 
ws.cryptoApi.hashString(canonicalJson(contractTerms));
-    const contractData = extractContractData(contractTerms, contractTermsHash, 
"");
-    const candidates = await getCandidatePayCoins(ws, {
-        allowedAuditors: contractData.allowedAuditors,
-        allowedExchanges: contractData.allowedExchanges,
-        amount: contractData.amount,
-        maxDepositFee: contractData.maxDepositFee,
-        maxWireFee: contractData.maxWireFee,
-        timestamp: contractData.timestamp,
-        wireFeeAmortization: contractData.wireFeeAmortization,
-        wireMethod: contractData.wireMethod,
-    });
-    const payCoinSel = selectPayCoins({
-        candidates,
-        contractTermsAmount: contractData.amount,
-        depositFeeLimit: contractData.maxDepositFee,
-        wireFeeAmortization: (_a = contractData.wireFeeAmortization) !== null 
&& _a !== void 0 ? _a : 1,
-        wireFeeLimit: contractData.maxWireFee,
-        prevPayCoins: [],
-    });
-    if (!payCoinSel) {
-        throw Error("insufficient funds");
-    }
-    const totalDepositCost = await getTotalPaymentCost(ws, payCoinSel);
-    const depositGroupId = encodeCrock(getRandomBytes(32));
-    const effectiveDepositAmount = await getEffectiveDepositAmount(ws, 
p.targetType, payCoinSel);
-    const depositGroup = {
-        contractTermsHash,
-        contractTermsRaw: contractTerms,
-        depositGroupId,
-        noncePriv: noncePair.priv,
-        noncePub: noncePair.pub,
-        timestampCreated: timestamp,
-        timestampFinished: undefined,
-        payCoinSelection: payCoinSel,
-        depositedPerCoin: payCoinSel.coinPubs.map((x) => false),
-        merchantPriv: merchantPair.priv,
-        merchantPub: merchantPair.pub,
-        totalPayCost: totalDepositCost,
-        effectiveDepositAmount,
-        wire: {
-            payto_uri: req.depositPaytoUri,
-            salt: wireSalt,
-        },
-        retryInfo: initRetryInfo(true),
-        lastError: undefined,
-    };
-    await ws.db
-        .mktx((x) => ({
-        depositGroups: x.depositGroups,
-        coins: x.coins,
-        refreshGroups: x.refreshGroups,
-        denominations: x.denominations,
-    }))
-        .runReadWrite(async (tx) => {
-        await applyCoinSpend(ws, tx, payCoinSel);
-        await tx.depositGroups.put(depositGroup);
-    });
-    return { depositGroupId };
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$a = new Logger("operations/tip.ts");
-async function prepareTip(ws, talerTipUri) {
-    const res = parseTipUri(talerTipUri);
-    if (!res) {
-        throw Error("invalid taler://tip URI");
-    }
-    let tipRecord = await ws.db
-        .mktx((x) => ({
-        tips: x.tips,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.tips.indexes.byMerchantTipIdAndBaseUrl.get([
-            res.merchantTipId,
-            res.merchantBaseUrl,
-        ]);
-    });
-    if (!tipRecord) {
-        const tipStatusUrl = new URL$1(`tips/${res.merchantTipId}`, 
res.merchantBaseUrl);
-        logger$a.trace("checking tip status from", tipStatusUrl.href);
-        const merchantResp = await ws.http.get(tipStatusUrl.href);
-        const tipPickupStatus = await 
readSuccessResponseJsonOrThrow(merchantResp, codecForTipPickupGetResponse());
-        logger$a.trace(`status ${j2s(tipPickupStatus)}`);
-        const amount = Amounts.parseOrThrow(tipPickupStatus.tip_amount);
-        logger$a.trace("new tip, creating tip record");
-        await updateExchangeFromUrl(ws, tipPickupStatus.exchange_url);
-        const withdrawDetails = await getExchangeWithdrawalInfo(ws, 
tipPickupStatus.exchange_url, amount);
-        const walletTipId = encodeCrock(getRandomBytes(32));
-        await updateWithdrawalDenoms(ws, tipPickupStatus.exchange_url);
-        const denoms = await getCandidateWithdrawalDenoms(ws, 
tipPickupStatus.exchange_url);
-        const selectedDenoms = selectWithdrawalDenominations(amount, denoms);
-        const secretSeed = encodeCrock(getRandomBytes(64));
-        const denomSelUid = encodeCrock(getRandomBytes(32));
-        const newTipRecord = {
-            walletTipId: walletTipId,
-            acceptedTimestamp: undefined,
-            tipAmountRaw: amount,
-            tipExpiration: tipPickupStatus.expiration,
-            exchangeBaseUrl: tipPickupStatus.exchange_url,
-            merchantBaseUrl: res.merchantBaseUrl,
-            createdTimestamp: getTimestampNow(),
-            merchantTipId: res.merchantTipId,
-            tipAmountEffective: Amounts.sub(amount, 
Amounts.add(withdrawDetails.overhead, withdrawDetails.withdrawFee)
-                .amount).amount,
-            retryInfo: initRetryInfo(),
-            lastError: undefined,
-            denomsSel: denomSelectionInfoToState(selectedDenoms),
-            pickedUpTimestamp: undefined,
-            secretSeed,
-            denomSelUid,
-        };
-        await ws.db
-            .mktx((x) => ({
-            tips: x.tips,
-        }))
-            .runReadWrite(async (tx) => {
-            await tx.tips.put(newTipRecord);
-        });
-        tipRecord = newTipRecord;
-    }
-    const tipStatus = {
-        accepted: !!tipRecord && !!tipRecord.acceptedTimestamp,
-        tipAmountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
-        exchangeBaseUrl: tipRecord.exchangeBaseUrl,
-        merchantBaseUrl: tipRecord.merchantBaseUrl,
-        expirationTimestamp: tipRecord.tipExpiration,
-        tipAmountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
-        walletTipId: tipRecord.walletTipId,
-    };
-    return tipStatus;
-}
-async function incrementTipRetry(ws, walletTipId, err) {
-    await ws.db
-        .mktx((x) => ({
-        tips: x.tips,
-    }))
-        .runReadWrite(async (tx) => {
-        const t = await tx.tips.get(walletTipId);
-        if (!t) {
-            return;
-        }
-        if (!t.retryInfo) {
-            return;
-        }
-        t.retryInfo.retryCounter++;
-        updateRetryInfoTimeout(t.retryInfo);
-        t.lastError = err;
-        await tx.tips.put(t);
-    });
-    if (err) {
-        ws.notify({ type: NotificationType.TipOperationError, error: err });
-    }
-}
-async function processTip(ws, tipId, forceNow = false) {
-    const onOpErr = (e) => incrementTipRetry(ws, tipId, e);
-    await guardOperationException(() => processTipImpl(ws, tipId, forceNow), 
onOpErr);
-}
-async function resetTipRetry(ws, tipId) {
-    await ws.db
-        .mktx((x) => ({
-        tips: x.tips,
-    }))
-        .runReadWrite(async (tx) => {
-        const x = await tx.tips.get(tipId);
-        if (x) {
-            x.retryInfo = initRetryInfo();
-            await tx.tips.put(x);
-        }
-    });
-}
-async function processTipImpl(ws, walletTipId, forceNow) {
-    if (forceNow) {
-        await resetTipRetry(ws, walletTipId);
-    }
-    const tipRecord = await ws.db
-        .mktx((x) => ({
-        tips: x.tips,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.tips.get(walletTipId);
-    });
-    if (!tipRecord) {
-        return;
-    }
-    if (tipRecord.pickedUpTimestamp) {
-        logger$a.warn("tip already picked up");
-        return;
-    }
-    const denomsForWithdraw = tipRecord.denomsSel;
-    const planchets = [];
-    // Planchets in the form that the merchant expects
-    const planchetsDetail = [];
-    const denomForPlanchet = [];
-    for (const dh of denomsForWithdraw.selectedDenoms) {
-        const denom = await ws.db
-            .mktx((x) => ({
-            denominations: x.denominations,
-        }))
-            .runReadOnly(async (tx) => {
-            return tx.denominations.get([
-                tipRecord.exchangeBaseUrl,
-                dh.denomPubHash,
-            ]);
-        });
-        checkDbInvariant(!!denom, "denomination should be in database");
-        for (let i = 0; i < dh.count; i++) {
-            const deriveReq = {
-                denomPub: denom.denomPub,
-                planchetIndex: planchets.length,
-                secretSeed: tipRecord.secretSeed,
-            };
-            logger$a.trace(`deriving tip planchet: ${j2s(deriveReq)}`);
-            const p = await ws.cryptoApi.createTipPlanchet(deriveReq);
-            logger$a.trace(`derive result: ${j2s(p)}`);
-            denomForPlanchet[planchets.length] = denom;
-            planchets.push(p);
-            planchetsDetail.push({
-                coin_ev: p.coinEv,
-                denom_pub_hash: denom.denomPubHash,
-            });
-        }
-    }
-    const tipStatusUrl = new URL$1(`tips/${tipRecord.merchantTipId}/pickup`, 
tipRecord.merchantBaseUrl);
-    const req = { planchets: planchetsDetail };
-    logger$a.trace(`sending tip request: ${j2s(req)}`);
-    const merchantResp = await ws.http.postJson(tipStatusUrl.href, req);
-    logger$a.trace(`got tip response, status ${merchantResp.status}`);
-    // Hide transient errors.
-    if (tipRecord.retryInfo.retryCounter < 5 &&
-        ((merchantResp.status >= 500 && merchantResp.status <= 599) ||
-            merchantResp.status === 424)) {
-        logger$a.trace(`got transient tip error`);
-        const err = 
makeErrorDetails(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR, "tip pickup 
failed (transient)", getHttpResponseErrorDetails(merchantResp));
-        await incrementTipRetry(ws, tipRecord.walletTipId, err);
-        // FIXME: Maybe we want to signal to the caller that the transient 
error happened?
-        return;
-    }
-    const response = await readSuccessResponseJsonOrThrow(merchantResp, 
codecForTipResponse());
-    if (response.blind_sigs.length !== planchets.length) {
-        throw Error("number of tip responses does not match requested 
planchets");
-    }
-    const newCoinRecords = [];
-    for (let i = 0; i < response.blind_sigs.length; i++) {
-        const blindedSig = response.blind_sigs[i].blind_sig;
-        const denom = denomForPlanchet[i];
-        checkLogicInvariant(!!denom);
-        const planchet = planchets[i];
-        checkLogicInvariant(!!planchet);
-        const denomSig = await ws.cryptoApi.rsaUnblind(blindedSig, 
planchet.blindingKey, denom.denomPub);
-        const isValid = await ws.cryptoApi.rsaVerify(planchet.coinPub, 
denomSig, denom.denomPub);
-        if (!isValid) {
-            await ws.db
-                .mktx((x) => ({ tips: x.tips }))
-                .runReadWrite(async (tx) => {
-                const tipRecord = await tx.tips.get(walletTipId);
-                if (!tipRecord) {
-                    return;
-                }
-                tipRecord.lastError = 
makeErrorDetails(TalerErrorCode.WALLET_TIPPING_COIN_SIGNATURE_INVALID, "invalid 
signature from the exchange (via merchant tip) after unblinding", {});
-                await tx.tips.put(tipRecord);
-            });
-            return;
-        }
-        newCoinRecords.push({
-            blindingKey: planchet.blindingKey,
-            coinPriv: planchet.coinPriv,
-            coinPub: planchet.coinPub,
-            coinSource: {
-                type: CoinSourceType.Tip,
-                coinIndex: i,
-                walletTipId: walletTipId,
-            },
-            currentAmount: denom.value,
-            denomPub: denom.denomPub,
-            denomPubHash: denom.denomPubHash,
-            denomSig: denomSig,
-            exchangeBaseUrl: tipRecord.exchangeBaseUrl,
-            status: CoinStatus.Fresh,
-            suspended: false,
-            coinEvHash: planchet.coinEvHash,
-        });
-    }
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        tips: x.tips,
-        withdrawalGroups: x.withdrawalGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const tr = await tx.tips.get(walletTipId);
-        if (!tr) {
-            return;
-        }
-        if (tr.pickedUpTimestamp) {
-            return;
-        }
-        tr.pickedUpTimestamp = getTimestampNow();
-        tr.lastError = undefined;
-        tr.retryInfo = initRetryInfo(false);
-        await tx.tips.put(tr);
-        for (const cr of newCoinRecords) {
-            await tx.coins.put(cr);
-        }
-    });
-}
-async function acceptTip(ws, tipId) {
-    const found = await ws.db
-        .mktx((x) => ({
-        tips: x.tips,
-    }))
-        .runReadWrite(async (tx) => {
-        const tipRecord = await tx.tips.get(tipId);
-        if (!tipRecord) {
-            logger$a.error("tip not found");
-            return false;
-        }
-        tipRecord.acceptedTimestamp = getTimestampNow();
-        await tx.tips.put(tipRecord);
-        return true;
-    });
-    if (found) {
-        await processTip(ws, tipId);
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-/**
- * Create an event ID from the type and the primary key for the event.
- */
-function makeEventId(type, ...args) {
-    return type + ":" + args.map((x) => encodeURIComponent(x)).join(":");
-}
-function shouldSkipCurrency(transactionsRequest, currency) {
-    if (!(transactionsRequest === null || transactionsRequest === void 0 ? 
void 0 : transactionsRequest.currency)) {
-        return false;
-    }
-    return transactionsRequest.currency.toLowerCase() !== 
currency.toLowerCase();
-}
-function shouldSkipSearch(transactionsRequest, fields) {
-    if (!(transactionsRequest === null || transactionsRequest === void 0 ? 
void 0 : transactionsRequest.search)) {
-        return false;
-    }
-    const needle = transactionsRequest.search.trim();
-    for (const f of fields) {
-        if (f.indexOf(needle) >= 0) {
-            return false;
-        }
-    }
-    return true;
-}
-/**
- * Retrieve the full event history for this wallet.
- */
-async function getTransactions(ws, transactionsRequest) {
-    const transactions = [];
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        denominations: x.denominations,
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-        proposals: x.proposals,
-        purchases: x.purchases,
-        refreshGroups: x.refreshGroups,
-        reserves: x.reserves,
-        tips: x.tips,
-        withdrawalGroups: x.withdrawalGroups,
-        planchets: x.planchets,
-        recoupGroups: x.recoupGroups,
-        depositGroups: x.depositGroups,
-        tombstones: x.tombstones,
-    }))
-        .runReadOnly(
-    // Report withdrawals that are currently in progress.
-    async (tx) => {
-        tx.withdrawalGroups.iter().forEachAsync(async (wsr) => {
-            var _a, _b;
-            if (shouldSkipCurrency(transactionsRequest, 
wsr.rawWithdrawalAmount.currency)) {
-                return;
-            }
-            if (shouldSkipSearch(transactionsRequest, [])) {
-                return;
-            }
-            const r = await tx.reserves.get(wsr.reservePub);
-            if (!r) {
-                return;
-            }
-            let amountRaw = undefined;
-            if (wsr.withdrawalGroupId === r.initialWithdrawalGroupId) {
-                amountRaw = r.instructedAmount;
-            }
-            else {
-                amountRaw = wsr.denomsSel.totalWithdrawCost;
-            }
-            let withdrawalDetails;
-            if (r.bankInfo) {
-                withdrawalDetails = {
-                    type: WithdrawalType.TalerBankIntegrationApi,
-                    confirmed: true,
-                    bankConfirmationUrl: r.bankInfo.confirmUrl,
-                };
-            }
-            else {
-                const exchangeDetails = await getExchangeDetails(tx, 
wsr.exchangeBaseUrl);
-                if (!exchangeDetails) {
-                    // FIXME: report somehow
-                    return;
-                }
-                withdrawalDetails = {
-                    type: WithdrawalType.ManualTransfer,
-                    exchangePaytoUris: (_b = (_a = exchangeDetails.wireInfo) 
=== null || _a === void 0 ? void 0 : _a.accounts.map((x) => x.payto_uri)) !== 
null && _b !== void 0 ? _b : [],
-                };
-            }
-            transactions.push(Object.assign({ type: 
TransactionType.Withdrawal, amountEffective: 
Amounts.stringify(wsr.denomsSel.totalCoinValue), amountRaw: 
Amounts.stringify(amountRaw), withdrawalDetails, exchangeBaseUrl: 
wsr.exchangeBaseUrl, pending: !wsr.timestampFinish, timestamp: 
wsr.timestampStart, transactionId: makeEventId(TransactionType.Withdrawal, 
wsr.withdrawalGroupId) }, (wsr.lastError ? { error: wsr.lastError } : {})));
-        });
-        // Report pending withdrawals based on reserves that
-        // were created, but where the actual withdrawal group has
-        // not started yet.
-        tx.reserves.iter().forEachAsync(async (r) => {
-            if (shouldSkipCurrency(transactionsRequest, r.currency)) {
-                return;
-            }
-            if (shouldSkipSearch(transactionsRequest, [])) {
-                return;
-            }
-            if (r.initialWithdrawalStarted) {
-                return;
-            }
-            if (r.reserveStatus === ReserveRecordStatus.BANK_ABORTED) {
-                return;
-            }
-            let withdrawalDetails;
-            if (r.bankInfo) {
-                withdrawalDetails = {
-                    type: WithdrawalType.TalerBankIntegrationApi,
-                    confirmed: false,
-                    bankConfirmationUrl: r.bankInfo.confirmUrl,
-                };
-            }
-            else {
-                withdrawalDetails = {
-                    type: WithdrawalType.ManualTransfer,
-                    exchangePaytoUris: await getFundingPaytoUris(tx, 
r.reservePub),
-                };
-            }
-            transactions.push(Object.assign({ type: 
TransactionType.Withdrawal, amountRaw: Amounts.stringify(r.instructedAmount), 
amountEffective: Amounts.stringify(r.initialDenomSel.totalCoinValue), 
exchangeBaseUrl: r.exchangeBaseUrl, pending: true, timestamp: 
r.timestampCreated, withdrawalDetails: withdrawalDetails, transactionId: 
makeEventId(TransactionType.Withdrawal, r.initialWithdrawalGroupId) }, 
(r.lastError ? { error: r.lastError } : {})));
-        });
-        tx.depositGroups.iter().forEachAsync(async (dg) => {
-            const amount = Amounts.parseOrThrow(dg.contractTermsRaw.amount);
-            if (shouldSkipCurrency(transactionsRequest, amount.currency)) {
-                return;
-            }
-            transactions.push(Object.assign({ type: TransactionType.Deposit, 
amountRaw: Amounts.stringify(dg.effectiveDepositAmount), amountEffective: 
Amounts.stringify(dg.totalPayCost), pending: !dg.timestampFinished, timestamp: 
dg.timestampCreated, targetPaytoUri: dg.wire.payto_uri, transactionId: 
makeEventId(TransactionType.Deposit, dg.depositGroupId), depositGroupId: 
dg.depositGroupId }, (dg.lastError ? { error: dg.lastError } : {})));
-        });
-        tx.purchases.iter().forEachAsync(async (pr) => {
-            var _a;
-            if (shouldSkipCurrency(transactionsRequest, 
pr.download.contractData.amount.currency)) {
-                return;
-            }
-            const contractData = pr.download.contractData;
-            if (shouldSkipSearch(transactionsRequest, [contractData.summary])) 
{
-                return;
-            }
-            const proposal = await tx.proposals.get(pr.proposalId);
-            if (!proposal) {
-                return;
-            }
-            const info = {
-                merchant: contractData.merchant,
-                orderId: contractData.orderId,
-                products: contractData.products,
-                summary: contractData.summary,
-                summary_i18n: contractData.summaryI18n,
-                contractTermsHash: contractData.contractTermsHash,
-            };
-            if (contractData.fulfillmentUrl !== "") {
-                info.fulfillmentUrl = contractData.fulfillmentUrl;
-            }
-            const paymentTransactionId = makeEventId(TransactionType.Payment, 
pr.proposalId);
-            const err = (_a = pr.lastPayError) !== null && _a !== void 0 ? _a 
: pr.lastRefundStatusError;
-            transactions.push(Object.assign({ type: TransactionType.Payment, 
amountRaw: Amounts.stringify(contractData.amount), amountEffective: 
Amounts.stringify(pr.totalPayCost), status: pr.timestampFirstSuccessfulPay
-                    ? PaymentStatus.Paid
-                    : PaymentStatus.Accepted, pending: 
!pr.timestampFirstSuccessfulPay &&
-                    pr.abortStatus === AbortStatus.None, timestamp: 
pr.timestampAccept, transactionId: paymentTransactionId, proposalId: 
pr.proposalId, info: info }, (err ? { error: err } : {})));
-            const refundGroupKeys = new Set();
-            for (const rk of Object.keys(pr.refunds)) {
-                const refund = pr.refunds[rk];
-                const groupKey = `${refund.executionTime.t_ms}`;
-                refundGroupKeys.add(groupKey);
-            }
-            for (const groupKey of refundGroupKeys.values()) {
-                const refundTombstoneId = 
makeEventId(TombstoneTag.DeleteRefund, pr.proposalId, groupKey);
-                const tombstone = await tx.tombstones.get(refundTombstoneId);
-                if (tombstone) {
-                    continue;
-                }
-                const refundTransactionId = 
makeEventId(TransactionType.Refund, pr.proposalId, groupKey);
-                let r0;
-                let amountRaw = Amounts.getZero(contractData.amount.currency);
-                let amountEffective = 
Amounts.getZero(contractData.amount.currency);
-                for (const rk of Object.keys(pr.refunds)) {
-                    const refund = pr.refunds[rk];
-                    const myGroupKey = `${refund.executionTime.t_ms}`;
-                    if (myGroupKey !== groupKey) {
-                        continue;
-                    }
-                    if (!r0) {
-                        r0 = refund;
-                    }
-                    if (refund.type === RefundState.Applied) {
-                        amountRaw = Amounts.add(amountRaw, 
refund.refundAmount).amount;
-                        amountEffective = Amounts.add(amountEffective, 
Amounts.sub(refund.refundAmount, refund.refundFee, 
refund.totalRefreshCostBound).amount).amount;
-                    }
-                }
-                if (!r0) {
-                    throw Error("invariant violated");
-                }
-                transactions.push({
-                    type: TransactionType.Refund,
-                    info,
-                    refundedTransactionId: paymentTransactionId,
-                    transactionId: refundTransactionId,
-                    timestamp: r0.obtainedTime,
-                    amountEffective: Amounts.stringify(amountEffective),
-                    amountRaw: Amounts.stringify(amountRaw),
-                    pending: false,
-                });
-            }
-        });
-        tx.tips.iter().forEachAsync(async (tipRecord) => {
-            if (shouldSkipCurrency(transactionsRequest, 
tipRecord.tipAmountRaw.currency)) {
-                return;
-            }
-            if (!tipRecord.acceptedTimestamp) {
-                return;
-            }
-            transactions.push({
-                type: TransactionType.Tip,
-                amountEffective: 
Amounts.stringify(tipRecord.tipAmountEffective),
-                amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
-                pending: !tipRecord.pickedUpTimestamp,
-                timestamp: tipRecord.acceptedTimestamp,
-                transactionId: makeEventId(TransactionType.Tip, 
tipRecord.walletTipId),
-                merchantBaseUrl: tipRecord.merchantBaseUrl,
-                error: tipRecord.lastError,
-            });
-        });
-    });
-    const txPending = transactions.filter((x) => x.pending);
-    const txNotPending = transactions.filter((x) => !x.pending);
-    txPending.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp));
-    txNotPending.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp));
-    return { transactions: [...txNotPending, ...txPending] };
-}
-var TombstoneTag;
-(function (TombstoneTag) {
-    TombstoneTag["DeleteWithdrawalGroup"] = "delete-withdrawal-group";
-    TombstoneTag["DeleteReserve"] = "delete-reserve";
-    TombstoneTag["DeletePayment"] = "delete-payment";
-    TombstoneTag["DeleteTip"] = "delete-tip";
-    TombstoneTag["DeleteRefreshGroup"] = "delete-refresh-group";
-    TombstoneTag["DeleteDepositGroup"] = "delete-deposit-group";
-    TombstoneTag["DeleteRefund"] = "delete-refund";
-})(TombstoneTag || (TombstoneTag = {}));
-/**
- * Immediately retry the underlying operation
- * of a transaction.
- */
-async function retryTransaction(ws, transactionId) {
-    const [type, ...rest] = transactionId.split(":");
-    switch (type) {
-        case TransactionType.Deposit:
-            const depositGroupId = rest[0];
-            processDepositGroup(ws, depositGroupId, true);
-            break;
-        case TransactionType.Withdrawal:
-            const withdrawalGroupId = rest[0];
-            await processWithdrawGroup(ws, withdrawalGroupId, true);
-            break;
-        case TransactionType.Payment:
-            const proposalId = rest[0];
-            await processPurchasePay(ws, proposalId, true);
-            break;
-        case TransactionType.Tip:
-            const walletTipId = rest[0];
-            await processTip(ws, walletTipId, true);
-            break;
-        case TransactionType.Refresh:
-            const refreshGroupId = rest[0];
-            await processRefreshGroup(ws, refreshGroupId, true);
-            break;
-    }
-}
-/**
- * Permanently delete a transaction based on the transaction ID.
- */
-async function deleteTransaction(ws, transactionId) {
-    const [type, ...rest] = transactionId.split(":");
-    if (type === TransactionType.Withdrawal) {
-        const withdrawalGroupId = rest[0];
-        await ws.db
-            .mktx((x) => ({
-            withdrawalGroups: x.withdrawalGroups,
-            reserves: x.reserves,
-            tombstones: x.tombstones,
-        }))
-            .runReadWrite(async (tx) => {
-            const withdrawalGroupRecord = await 
tx.withdrawalGroups.get(withdrawalGroupId);
-            if (withdrawalGroupRecord) {
-                await tx.withdrawalGroups.delete(withdrawalGroupId);
-                await tx.tombstones.put({
-                    id: TombstoneTag.DeleteWithdrawalGroup + ":" + 
withdrawalGroupId,
-                });
-                return;
-            }
-            const reserveRecord = await 
tx.reserves.indexes.byInitialWithdrawalGroupId.get(withdrawalGroupId);
-            if (reserveRecord && !reserveRecord.initialWithdrawalStarted) {
-                const reservePub = reserveRecord.reservePub;
-                await tx.reserves.delete(reservePub);
-                await tx.tombstones.put({
-                    id: TombstoneTag.DeleteReserve + ":" + reservePub,
-                });
-            }
-        });
-    }
-    else if (type === TransactionType.Payment) {
-        const proposalId = rest[0];
-        await ws.db
-            .mktx((x) => ({
-            proposals: x.proposals,
-            purchases: x.purchases,
-            tombstones: x.tombstones,
-        }))
-            .runReadWrite(async (tx) => {
-            let found = false;
-            const proposal = await tx.proposals.get(proposalId);
-            if (proposal) {
-                found = true;
-                await tx.proposals.delete(proposalId);
-            }
-            const purchase = await tx.purchases.get(proposalId);
-            if (purchase) {
-                found = true;
-                await tx.proposals.delete(proposalId);
-            }
-            if (found) {
-                await tx.tombstones.put({
-                    id: TombstoneTag.DeletePayment + ":" + proposalId,
-                });
-            }
-        });
-    }
-    else if (type === TransactionType.Refresh) {
-        const refreshGroupId = rest[0];
-        await ws.db
-            .mktx((x) => ({
-            refreshGroups: x.refreshGroups,
-            tombstones: x.tombstones,
-        }))
-            .runReadWrite(async (tx) => {
-            const rg = await tx.refreshGroups.get(refreshGroupId);
-            if (rg) {
-                await tx.refreshGroups.delete(refreshGroupId);
-                await tx.tombstones.put({
-                    id: TombstoneTag.DeleteRefreshGroup + ":" + refreshGroupId,
-                });
-            }
-        });
-    }
-    else if (type === TransactionType.Tip) {
-        const tipId = rest[0];
-        await ws.db
-            .mktx((x) => ({
-            tips: x.tips,
-            tombstones: x.tombstones,
-        }))
-            .runReadWrite(async (tx) => {
-            const tipRecord = await tx.tips.get(tipId);
-            if (tipRecord) {
-                await tx.tips.delete(tipId);
-                await tx.tombstones.put({
-                    id: TombstoneTag.DeleteTip + ":" + tipId,
-                });
-            }
-        });
-    }
-    else if (type === TransactionType.Deposit) {
-        const depositGroupId = rest[0];
-        await ws.db
-            .mktx((x) => ({
-            depositGroups: x.depositGroups,
-            tombstones: x.tombstones,
-        }))
-            .runReadWrite(async (tx) => {
-            const tipRecord = await tx.depositGroups.get(depositGroupId);
-            if (tipRecord) {
-                await tx.depositGroups.delete(depositGroupId);
-                await tx.tombstones.put({
-                    id: TombstoneTag.DeleteDepositGroup + ":" + depositGroupId,
-                });
-            }
-        });
-    }
-    else if (type === TransactionType.Refund) {
-        const proposalId = rest[0];
-        const executionTimeStr = rest[1];
-        await ws.db
-            .mktx((x) => ({
-            proposals: x.proposals,
-            purchases: x.purchases,
-            tombstones: x.tombstones,
-        }))
-            .runReadWrite(async (tx) => {
-            const purchase = await tx.purchases.get(proposalId);
-            if (purchase) {
-                // This should just influence the history view,
-                // but won't delete any actual refund information.
-                await tx.tombstones.put({
-                    id: makeEventId(TombstoneTag.DeleteRefund, proposalId, 
executionTimeStr),
-                });
-            }
-        });
-    }
-    else {
-        throw Error(`can't delete a '${type}' transaction`);
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems SA
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$9 = new Logger("operations/backup/import.ts");
-function checkBackupInvariant(b, m) {
-    if (!b) {
-        if (m) {
-            throw Error(`BUG: backup invariant failed (${m})`);
-        }
-        else {
-            throw Error("BUG: backup invariant failed");
-        }
-    }
-}
-/**
- * Re-compute information about the coin selection for a payment.
- */
-async function recoverPayCoinSelection(tx, contractData, backupPurchase) {
-    const coinPubs = backupPurchase.pay_coins.map((x) => x.coin_pub);
-    const coinContributions = backupPurchase.pay_coins.map((x) => 
Amounts.parseOrThrow(x.contribution));
-    const coveredExchanges = new Set();
-    let totalWireFee = Amounts.getZero(contractData.amount.currency);
-    let totalDepositFees = Amounts.getZero(contractData.amount.currency);
-    for (const coinPub of coinPubs) {
-        const coinRecord = await tx.coins.get(coinPub);
-        checkBackupInvariant(!!coinRecord);
-        const denom = await tx.denominations.get([
-            coinRecord.exchangeBaseUrl,
-            coinRecord.denomPubHash,
-        ]);
-        checkBackupInvariant(!!denom);
-        totalDepositFees = Amounts.add(totalDepositFees, 
denom.feeDeposit).amount;
-        if (!coveredExchanges.has(coinRecord.exchangeBaseUrl)) {
-            const exchangeDetails = await getExchangeDetails(tx, 
coinRecord.exchangeBaseUrl);
-            checkBackupInvariant(!!exchangeDetails);
-            let wireFee;
-            const feesForType = exchangeDetails.wireInfo.feesForType;
-            checkBackupInvariant(!!feesForType);
-            for (const fee of feesForType[contractData.wireMethod] || []) {
-                if (fee.startStamp <= contractData.timestamp &&
-                    fee.endStamp >= contractData.timestamp) {
-                    wireFee = fee.wireFee;
-                    break;
-                }
-            }
-            if (wireFee) {
-                totalWireFee = Amounts.add(totalWireFee, wireFee).amount;
-            }
-            coveredExchanges.add(coinRecord.exchangeBaseUrl);
-        }
-    }
-    let customerWireFee;
-    const amortizedWireFee = Amounts.divide(totalWireFee, 
contractData.wireFeeAmortization);
-    if (Amounts.cmp(contractData.maxWireFee, amortizedWireFee) < 0) {
-        customerWireFee = amortizedWireFee;
-    }
-    else {
-        customerWireFee = Amounts.getZero(contractData.amount.currency);
-    }
-    const customerDepositFees = Amounts.sub(totalDepositFees, 
contractData.maxDepositFee).amount;
-    return {
-        coinPubs,
-        coinContributions,
-        paymentAmount: contractData.amount,
-        customerWireFees: customerWireFee,
-        customerDepositFees,
-    };
-}
-async function getDenomSelStateFromBackup(tx, exchangeBaseUrl, sel) {
-    const d0 = await tx.denominations.get([
-        exchangeBaseUrl,
-        sel[0].denom_pub_hash,
-    ]);
-    checkBackupInvariant(!!d0);
-    const selectedDenoms = [];
-    let totalCoinValue = Amounts.getZero(d0.value.currency);
-    let totalWithdrawCost = Amounts.getZero(d0.value.currency);
-    for (const s of sel) {
-        const d = await tx.denominations.get([exchangeBaseUrl, 
s.denom_pub_hash]);
-        checkBackupInvariant(!!d);
-        totalCoinValue = Amounts.add(totalCoinValue, d.value).amount;
-        totalWithdrawCost = Amounts.add(totalWithdrawCost, d.value, 
d.feeWithdraw)
-            .amount;
-    }
-    return {
-        selectedDenoms,
-        totalCoinValue,
-        totalWithdrawCost,
-    };
-}
-async function importBackup(ws, backupBlobArg, cryptoComp) {
-    await provideBackupState(ws);
-    logger$9.info(`importing backup ${j2s(backupBlobArg)}`);
-    return ws.db
-        .mktx((x) => ({
-        config: x.config,
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-        coins: x.coins,
-        denominations: x.denominations,
-        purchases: x.purchases,
-        proposals: x.proposals,
-        refreshGroups: x.refreshGroups,
-        backupProviders: x.backupProviders,
-        tips: x.tips,
-        recoupGroups: x.recoupGroups,
-        reserves: x.reserves,
-        withdrawalGroups: x.withdrawalGroups,
-        tombstones: x.tombstones,
-        depositGroups: x.depositGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        var _a, _b, _c, _d, _e;
-        var _f, _g;
-        // FIXME: validate schema!
-        const backupBlob = backupBlobArg;
-        // FIXME: validate version
-        for (const tombstone of backupBlob.tombstones) {
-            await tx.tombstones.put({
-                id: tombstone,
-            });
-        }
-        const tombstoneSet = new Set(backupBlob.tombstones);
-        // FIXME:  Validate that the "details pointer" is correct
-        for (const backupExchange of backupBlob.exchanges) {
-            const existingExchange = await 
tx.exchanges.get(backupExchange.base_url);
-            if (existingExchange) {
-                continue;
-            }
-            await tx.exchanges.put({
-                baseUrl: backupExchange.base_url,
-                detailsPointer: {
-                    currency: backupExchange.currency,
-                    masterPublicKey: backupExchange.master_public_key,
-                    updateClock: backupExchange.update_clock,
-                },
-                permanent: true,
-                retryInfo: initRetryInfo(false),
-                lastUpdate: undefined,
-                nextUpdate: getTimestampNow(),
-                nextRefreshCheck: getTimestampNow(),
-            });
-        }
-        for (const backupExchangeDetails of backupBlob.exchange_details) {
-            const existingExchangeDetails = await tx.exchangeDetails.get([
-                backupExchangeDetails.base_url,
-                backupExchangeDetails.currency,
-                backupExchangeDetails.master_public_key,
-            ]);
-            if (!existingExchangeDetails) {
-                const wireInfo = {
-                    accounts: backupExchangeDetails.accounts.map((x) => ({
-                        master_sig: x.master_sig,
-                        payto_uri: x.payto_uri,
-                    })),
-                    feesForType: {},
-                };
-                for (const fee of backupExchangeDetails.wire_fees) {
-                    const w = ((_a = (_f = wireInfo.feesForType)[_g = 
fee.wire_type]) !== null && _a !== void 0 ? _a : (_f[_g] = []));
-                    w.push({
-                        closingFee: Amounts.parseOrThrow(fee.closing_fee),
-                        endStamp: fee.end_stamp,
-                        sig: fee.sig,
-                        startStamp: fee.start_stamp,
-                        wireFee: Amounts.parseOrThrow(fee.wire_fee),
-                    });
-                }
-                await tx.exchangeDetails.put({
-                    exchangeBaseUrl: backupExchangeDetails.base_url,
-                    termsOfServiceAcceptedEtag: 
backupExchangeDetails.tos_accepted_etag,
-                    termsOfServiceText: undefined,
-                    termsOfServiceLastEtag: undefined,
-                    termsOfServiceAcceptedTimestamp: 
backupExchangeDetails.tos_accepted_timestamp,
-                    wireInfo,
-                    currency: backupExchangeDetails.currency,
-                    auditors: backupExchangeDetails.auditors.map((x) => ({
-                        auditor_pub: x.auditor_pub,
-                        auditor_url: x.auditor_url,
-                        denomination_keys: x.denomination_keys,
-                    })),
-                    masterPublicKey: backupExchangeDetails.master_public_key,
-                    protocolVersion: backupExchangeDetails.protocol_version,
-                    reserveClosingDelay: 
backupExchangeDetails.reserve_closing_delay,
-                    signingKeys: backupExchangeDetails.signing_keys.map((x) => 
({
-                        key: x.key,
-                        master_sig: x.master_sig,
-                        stamp_end: x.stamp_end,
-                        stamp_expire: x.stamp_expire,
-                        stamp_start: x.stamp_start,
-                    })),
-                });
-            }
-            for (const backupDenomination of 
backupExchangeDetails.denominations) {
-                const denomPubHash = 
cryptoComp.denomPubToHash[backupDenomination.denom_pub];
-                checkLogicInvariant(!!denomPubHash);
-                const existingDenom = await tx.denominations.get([
-                    backupExchangeDetails.base_url,
-                    denomPubHash,
-                ]);
-                if (!existingDenom) {
-                    logger$9.info(`importing backup denomination: 
${j2s(backupDenomination)}`);
-                    await tx.denominations.put({
-                        denomPub: backupDenomination.denom_pub,
-                        denomPubHash: denomPubHash,
-                        exchangeBaseUrl: backupExchangeDetails.base_url,
-                        exchangeMasterPub: 
backupExchangeDetails.master_public_key,
-                        feeDeposit: 
Amounts.parseOrThrow(backupDenomination.fee_deposit),
-                        feeRefresh: 
Amounts.parseOrThrow(backupDenomination.fee_refresh),
-                        feeRefund: 
Amounts.parseOrThrow(backupDenomination.fee_refund),
-                        feeWithdraw: 
Amounts.parseOrThrow(backupDenomination.fee_withdraw),
-                        isOffered: backupDenomination.is_offered,
-                        isRevoked: backupDenomination.is_revoked,
-                        masterSig: backupDenomination.master_sig,
-                        stampExpireDeposit: 
backupDenomination.stamp_expire_deposit,
-                        stampExpireLegal: 
backupDenomination.stamp_expire_legal,
-                        stampExpireWithdraw: 
backupDenomination.stamp_expire_withdraw,
-                        stampStart: backupDenomination.stamp_start,
-                        status: DenominationStatus.VerifiedGood,
-                        value: Amounts.parseOrThrow(backupDenomination.value),
-                    });
-                }
-                for (const backupCoin of backupDenomination.coins) {
-                    const compCoin = 
cryptoComp.coinPrivToCompletedCoin[backupCoin.coin_priv];
-                    checkLogicInvariant(!!compCoin);
-                    const existingCoin = await tx.coins.get(compCoin.coinPub);
-                    if (!existingCoin) {
-                        let coinSource;
-                        switch (backupCoin.coin_source.type) {
-                            case BackupCoinSourceType.Refresh:
-                                coinSource = {
-                                    type: CoinSourceType.Refresh,
-                                    oldCoinPub: 
backupCoin.coin_source.old_coin_pub,
-                                };
-                                break;
-                            case BackupCoinSourceType.Tip:
-                                coinSource = {
-                                    type: CoinSourceType.Tip,
-                                    coinIndex: 
backupCoin.coin_source.coin_index,
-                                    walletTipId: 
backupCoin.coin_source.wallet_tip_id,
-                                };
-                                break;
-                            case BackupCoinSourceType.Withdraw:
-                                coinSource = {
-                                    type: CoinSourceType.Withdraw,
-                                    coinIndex: 
backupCoin.coin_source.coin_index,
-                                    reservePub: 
backupCoin.coin_source.reserve_pub,
-                                    withdrawalGroupId: 
backupCoin.coin_source.withdrawal_group_id,
-                                };
-                                break;
-                        }
-                        await tx.coins.put({
-                            blindingKey: backupCoin.blinding_key,
-                            coinEvHash: compCoin.coinEvHash,
-                            coinPriv: backupCoin.coin_priv,
-                            currentAmount: 
Amounts.parseOrThrow(backupCoin.current_amount),
-                            denomSig: backupCoin.denom_sig,
-                            coinPub: compCoin.coinPub,
-                            suspended: false,
-                            exchangeBaseUrl: backupExchangeDetails.base_url,
-                            denomPub: backupDenomination.denom_pub,
-                            denomPubHash,
-                            status: backupCoin.fresh
-                                ? CoinStatus.Fresh
-                                : CoinStatus.Dormant,
-                            coinSource,
-                        });
-                    }
-                }
-            }
-            for (const backupReserve of backupExchangeDetails.reserves) {
-                const reservePub = 
cryptoComp.reservePrivToPub[backupReserve.reserve_priv];
-                const ts = makeEventId(TombstoneTag.DeleteReserve, reservePub);
-                if (tombstoneSet.has(ts)) {
-                    continue;
-                }
-                checkLogicInvariant(!!reservePub);
-                const existingReserve = await tx.reserves.get(reservePub);
-                const instructedAmount = 
Amounts.parseOrThrow(backupReserve.instructed_amount);
-                if (!existingReserve) {
-                    let bankInfo;
-                    if (backupReserve.bank_info) {
-                        bankInfo = {
-                            exchangePaytoUri: 
backupReserve.bank_info.exchange_payto_uri,
-                            statusUrl: backupReserve.bank_info.status_url,
-                            confirmUrl: backupReserve.bank_info.confirm_url,
-                        };
-                    }
-                    await tx.reserves.put({
-                        currency: instructedAmount.currency,
-                        instructedAmount,
-                        exchangeBaseUrl: backupExchangeDetails.base_url,
-                        reservePub,
-                        reservePriv: backupReserve.reserve_priv,
-                        requestedQuery: false,
-                        bankInfo,
-                        timestampCreated: backupReserve.timestamp_created,
-                        timestampBankConfirmed: (_b = backupReserve.bank_info) 
=== null || _b === void 0 ? void 0 : _b.timestamp_bank_confirmed,
-                        timestampReserveInfoPosted: (_c = 
backupReserve.bank_info) === null || _c === void 0 ? void 0 : 
_c.timestamp_reserve_info_posted,
-                        senderWire: backupReserve.sender_wire,
-                        retryInfo: initRetryInfo(false),
-                        lastError: undefined,
-                        lastSuccessfulStatusQuery: { t_ms: "never" },
-                        initialWithdrawalGroupId: 
backupReserve.initial_withdrawal_group_id,
-                        initialWithdrawalStarted: 
backupReserve.withdrawal_groups.length > 0,
-                        // FIXME!
-                        reserveStatus: ReserveRecordStatus.QUERYING_STATUS,
-                        initialDenomSel: await getDenomSelStateFromBackup(tx, 
backupExchangeDetails.base_url, backupReserve.initial_selected_denoms),
-                    });
-                }
-                for (const backupWg of backupReserve.withdrawal_groups) {
-                    const ts = makeEventId(TombstoneTag.DeleteWithdrawalGroup, 
backupWg.withdrawal_group_id);
-                    if (tombstoneSet.has(ts)) {
-                        continue;
-                    }
-                    const existingWg = await 
tx.withdrawalGroups.get(backupWg.withdrawal_group_id);
-                    if (!existingWg) {
-                        await tx.withdrawalGroups.put({
-                            denomsSel: await getDenomSelStateFromBackup(tx, 
backupExchangeDetails.base_url, backupWg.selected_denoms),
-                            exchangeBaseUrl: backupExchangeDetails.base_url,
-                            lastError: undefined,
-                            rawWithdrawalAmount: 
Amounts.parseOrThrow(backupWg.raw_withdrawal_amount),
-                            reservePub,
-                            retryInfo: initRetryInfo(false),
-                            secretSeed: backupWg.secret_seed,
-                            timestampStart: backupWg.timestamp_created,
-                            timestampFinish: backupWg.timestamp_finish,
-                            withdrawalGroupId: backupWg.withdrawal_group_id,
-                            denomSelUid: backupWg.selected_denoms_id,
-                        });
-                    }
-                }
-            }
-        }
-        for (const backupProposal of backupBlob.proposals) {
-            const ts = makeEventId(TombstoneTag.DeletePayment, 
backupProposal.proposal_id);
-            if (tombstoneSet.has(ts)) {
-                continue;
-            }
-            const existingProposal = await 
tx.proposals.get(backupProposal.proposal_id);
-            if (!existingProposal) {
-                let download;
-                let proposalStatus;
-                switch (backupProposal.proposal_status) {
-                    case BackupProposalStatus.Proposed:
-                        if (backupProposal.contract_terms_raw) {
-                            proposalStatus = ProposalStatus.PROPOSED;
-                        }
-                        else {
-                            proposalStatus = ProposalStatus.DOWNLOADING;
-                        }
-                        break;
-                    case BackupProposalStatus.Refused:
-                        proposalStatus = ProposalStatus.REFUSED;
-                        break;
-                    case BackupProposalStatus.Repurchase:
-                        proposalStatus = ProposalStatus.REPURCHASE;
-                        break;
-                    case BackupProposalStatus.PermanentlyFailed:
-                        proposalStatus = ProposalStatus.PERMANENTLY_FAILED;
-                        break;
-                }
-                if (backupProposal.contract_terms_raw) {
-                    checkDbInvariant(!!backupProposal.merchant_sig);
-                    const parsedContractTerms = 
codecForContractTerms().decode(backupProposal.contract_terms_raw);
-                    const amount = 
Amounts.parseOrThrow(parsedContractTerms.amount);
-                    const contractTermsHash = 
cryptoComp.proposalIdToContractTermsHash[backupProposal.proposal_id];
-                    let maxWireFee;
-                    if (parsedContractTerms.max_wire_fee) {
-                        maxWireFee = 
Amounts.parseOrThrow(parsedContractTerms.max_wire_fee);
-                    }
-                    else {
-                        maxWireFee = Amounts.getZero(amount.currency);
-                    }
-                    download = {
-                        contractData: {
-                            amount,
-                            contractTermsHash: contractTermsHash,
-                            fulfillmentUrl: (_d = 
parsedContractTerms.fulfillment_url) !== null && _d !== void 0 ? _d : "",
-                            merchantBaseUrl: 
parsedContractTerms.merchant_base_url,
-                            merchantPub: parsedContractTerms.merchant_pub,
-                            merchantSig: backupProposal.merchant_sig,
-                            orderId: parsedContractTerms.order_id,
-                            summary: parsedContractTerms.summary,
-                            autoRefund: parsedContractTerms.auto_refund,
-                            maxWireFee,
-                            payDeadline: parsedContractTerms.pay_deadline,
-                            refundDeadline: 
parsedContractTerms.refund_deadline,
-                            wireFeeAmortization: 
parsedContractTerms.wire_fee_amortization || 1,
-                            allowedAuditors: 
parsedContractTerms.auditors.map((x) => ({
-                                auditorBaseUrl: x.url,
-                                auditorPub: x.auditor_pub,
-                            })),
-                            allowedExchanges: 
parsedContractTerms.exchanges.map((x) => ({
-                                exchangeBaseUrl: x.url,
-                                exchangePub: x.master_pub,
-                            })),
-                            timestamp: parsedContractTerms.timestamp,
-                            wireMethod: parsedContractTerms.wire_method,
-                            wireInfoHash: parsedContractTerms.h_wire,
-                            maxDepositFee: 
Amounts.parseOrThrow(parsedContractTerms.max_fee),
-                            merchant: parsedContractTerms.merchant,
-                            products: parsedContractTerms.products,
-                            summaryI18n: parsedContractTerms.summary_i18n,
-                        },
-                        contractTermsRaw: backupProposal.contract_terms_raw,
-                    };
-                }
-                await tx.proposals.put({
-                    claimToken: backupProposal.claim_token,
-                    lastError: undefined,
-                    merchantBaseUrl: backupProposal.merchant_base_url,
-                    timestamp: backupProposal.timestamp,
-                    orderId: backupProposal.order_id,
-                    noncePriv: backupProposal.nonce_priv,
-                    noncePub: 
cryptoComp.proposalNoncePrivToPub[backupProposal.nonce_priv],
-                    proposalId: backupProposal.proposal_id,
-                    repurchaseProposalId: 
backupProposal.repurchase_proposal_id,
-                    retryInfo: initRetryInfo(false),
-                    download,
-                    proposalStatus,
-                });
-            }
-        }
-        for (const backupPurchase of backupBlob.purchases) {
-            const ts = makeEventId(TombstoneTag.DeletePayment, 
backupPurchase.proposal_id);
-            if (tombstoneSet.has(ts)) {
-                continue;
-            }
-            const existingPurchase = await 
tx.purchases.get(backupPurchase.proposal_id);
-            if (!existingPurchase) {
-                const refunds = {};
-                for (const backupRefund of backupPurchase.refunds) {
-                    const key = 
`${backupRefund.coin_pub}-${backupRefund.rtransaction_id}`;
-                    const coin = await tx.coins.get(backupRefund.coin_pub);
-                    checkBackupInvariant(!!coin);
-                    const denom = await tx.denominations.get([
-                        coin.exchangeBaseUrl,
-                        coin.denomPubHash,
-                    ]);
-                    checkBackupInvariant(!!denom);
-                    const common = {
-                        coinPub: backupRefund.coin_pub,
-                        executionTime: backupRefund.execution_time,
-                        obtainedTime: backupRefund.obtained_time,
-                        refundAmount: 
Amounts.parseOrThrow(backupRefund.refund_amount),
-                        refundFee: denom.feeRefund,
-                        rtransactionId: backupRefund.rtransaction_id,
-                        totalRefreshCostBound: 
Amounts.parseOrThrow(backupRefund.total_refresh_cost_bound),
-                    };
-                    switch (backupRefund.type) {
-                        case BackupRefundState.Applied:
-                            refunds[key] = Object.assign({ type: 
RefundState.Applied }, common);
-                            break;
-                        case BackupRefundState.Failed:
-                            refunds[key] = Object.assign({ type: 
RefundState.Failed }, common);
-                            break;
-                        case BackupRefundState.Pending:
-                            refunds[key] = Object.assign({ type: 
RefundState.Pending }, common);
-                            break;
-                    }
-                }
-                let abortStatus;
-                switch (backupPurchase.abort_status) {
-                    case "abort-finished":
-                        abortStatus = AbortStatus.AbortFinished;
-                        break;
-                    case "abort-refund":
-                        abortStatus = AbortStatus.AbortRefund;
-                        break;
-                    case undefined:
-                        abortStatus = AbortStatus.None;
-                        break;
-                    default:
-                        logger$9.warn(`got backup purchase abort_status 
${j2s(backupPurchase.abort_status)}`);
-                        throw Error("not reachable");
-                }
-                const parsedContractTerms = 
codecForContractTerms().decode(backupPurchase.contract_terms_raw);
-                const amount = 
Amounts.parseOrThrow(parsedContractTerms.amount);
-                const contractTermsHash = 
cryptoComp.proposalIdToContractTermsHash[backupPurchase.proposal_id];
-                let maxWireFee;
-                if (parsedContractTerms.max_wire_fee) {
-                    maxWireFee = 
Amounts.parseOrThrow(parsedContractTerms.max_wire_fee);
-                }
-                else {
-                    maxWireFee = Amounts.getZero(amount.currency);
-                }
-                const download = {
-                    contractData: {
-                        amount,
-                        contractTermsHash: contractTermsHash,
-                        fulfillmentUrl: (_e = 
parsedContractTerms.fulfillment_url) !== null && _e !== void 0 ? _e : "",
-                        merchantBaseUrl: parsedContractTerms.merchant_base_url,
-                        merchantPub: parsedContractTerms.merchant_pub,
-                        merchantSig: backupPurchase.merchant_sig,
-                        orderId: parsedContractTerms.order_id,
-                        summary: parsedContractTerms.summary,
-                        autoRefund: parsedContractTerms.auto_refund,
-                        maxWireFee,
-                        payDeadline: parsedContractTerms.pay_deadline,
-                        refundDeadline: parsedContractTerms.refund_deadline,
-                        wireFeeAmortization: 
parsedContractTerms.wire_fee_amortization || 1,
-                        allowedAuditors: parsedContractTerms.auditors.map((x) 
=> ({
-                            auditorBaseUrl: x.url,
-                            auditorPub: x.auditor_pub,
-                        })),
-                        allowedExchanges: 
parsedContractTerms.exchanges.map((x) => ({
-                            exchangeBaseUrl: x.url,
-                            exchangePub: x.master_pub,
-                        })),
-                        timestamp: parsedContractTerms.timestamp,
-                        wireMethod: parsedContractTerms.wire_method,
-                        wireInfoHash: parsedContractTerms.h_wire,
-                        maxDepositFee: 
Amounts.parseOrThrow(parsedContractTerms.max_fee),
-                        merchant: parsedContractTerms.merchant,
-                        products: parsedContractTerms.products,
-                        summaryI18n: parsedContractTerms.summary_i18n,
-                    },
-                    contractTermsRaw: backupPurchase.contract_terms_raw,
-                };
-                await tx.purchases.put({
-                    proposalId: backupPurchase.proposal_id,
-                    noncePriv: backupPurchase.nonce_priv,
-                    noncePub: 
cryptoComp.proposalNoncePrivToPub[backupPurchase.nonce_priv],
-                    lastPayError: undefined,
-                    autoRefundDeadline: { t_ms: "never" },
-                    refundStatusRetryInfo: initRetryInfo(false),
-                    lastRefundStatusError: undefined,
-                    timestampAccept: backupPurchase.timestamp_accept,
-                    timestampFirstSuccessfulPay: 
backupPurchase.timestamp_first_successful_pay,
-                    timestampLastRefundStatus: undefined,
-                    merchantPaySig: backupPurchase.merchant_pay_sig,
-                    lastSessionId: undefined,
-                    abortStatus,
-                    // FIXME!
-                    payRetryInfo: initRetryInfo(false),
-                    download,
-                    paymentSubmitPending: 
!backupPurchase.timestamp_first_successful_pay,
-                    refundQueryRequested: false,
-                    payCoinSelection: await recoverPayCoinSelection(tx, 
download.contractData, backupPurchase),
-                    coinDepositPermissions: undefined,
-                    totalPayCost: 
Amounts.parseOrThrow(backupPurchase.total_pay_cost),
-                    refunds,
-                    payCoinSelectionUid: backupPurchase.pay_coins_uid,
-                });
-            }
-        }
-        for (const backupRefreshGroup of backupBlob.refresh_groups) {
-            const ts = makeEventId(TombstoneTag.DeleteRefreshGroup, 
backupRefreshGroup.refresh_group_id);
-            if (tombstoneSet.has(ts)) {
-                continue;
-            }
-            const existingRg = await 
tx.refreshGroups.get(backupRefreshGroup.refresh_group_id);
-            if (!existingRg) {
-                let reason;
-                switch (backupRefreshGroup.reason) {
-                    case BackupRefreshReason.AbortPay:
-                        reason = RefreshReason.AbortPay;
-                        break;
-                    case BackupRefreshReason.BackupRestored:
-                        reason = RefreshReason.BackupRestored;
-                        break;
-                    case BackupRefreshReason.Manual:
-                        reason = RefreshReason.Manual;
-                        break;
-                    case BackupRefreshReason.Pay:
-                        reason = RefreshReason.Pay;
-                        break;
-                    case BackupRefreshReason.Recoup:
-                        reason = RefreshReason.Recoup;
-                        break;
-                    case BackupRefreshReason.Refund:
-                        reason = RefreshReason.Refund;
-                        break;
-                    case BackupRefreshReason.Scheduled:
-                        reason = RefreshReason.Scheduled;
-                        break;
-                }
-                const refreshSessionPerCoin = [];
-                for (const oldCoin of backupRefreshGroup.old_coins) {
-                    const c = await tx.coins.get(oldCoin.coin_pub);
-                    checkBackupInvariant(!!c);
-                    if (oldCoin.refresh_session) {
-                        const denomSel = await getDenomSelStateFromBackup(tx, 
c.exchangeBaseUrl, oldCoin.refresh_session.new_denoms);
-                        refreshSessionPerCoin.push({
-                            sessionSecretSeed: 
oldCoin.refresh_session.session_secret_seed,
-                            norevealIndex: 
oldCoin.refresh_session.noreveal_index,
-                            newDenoms: 
oldCoin.refresh_session.new_denoms.map((x) => ({
-                                count: x.count,
-                                denomPubHash: x.denom_pub_hash,
-                            })),
-                            amountRefreshOutput: denomSel.totalCoinValue,
-                        });
-                    }
-                    else {
-                        refreshSessionPerCoin.push(undefined);
-                    }
-                }
-                await tx.refreshGroups.put({
-                    timestampFinished: backupRefreshGroup.timestamp_finish,
-                    timestampCreated: backupRefreshGroup.timestamp_created,
-                    refreshGroupId: backupRefreshGroup.refresh_group_id,
-                    reason,
-                    lastError: undefined,
-                    lastErrorPerCoin: {},
-                    oldCoinPubs: backupRefreshGroup.old_coins.map((x) => 
x.coin_pub),
-                    finishedPerCoin: backupRefreshGroup.old_coins.map((x) => 
x.finished),
-                    inputPerCoin: backupRefreshGroup.old_coins.map((x) => 
Amounts.parseOrThrow(x.input_amount)),
-                    estimatedOutputPerCoin: 
backupRefreshGroup.old_coins.map((x) => 
Amounts.parseOrThrow(x.estimated_output_amount)),
-                    refreshSessionPerCoin,
-                    retryInfo: initRetryInfo(false),
-                });
-            }
-        }
-        for (const backupTip of backupBlob.tips) {
-            const ts = makeEventId(TombstoneTag.DeleteTip, 
backupTip.wallet_tip_id);
-            if (tombstoneSet.has(ts)) {
-                continue;
-            }
-            const existingTip = await tx.tips.get(backupTip.wallet_tip_id);
-            if (!existingTip) {
-                const denomsSel = await getDenomSelStateFromBackup(tx, 
backupTip.exchange_base_url, backupTip.selected_denoms);
-                await tx.tips.put({
-                    acceptedTimestamp: backupTip.timestamp_accepted,
-                    createdTimestamp: backupTip.timestamp_created,
-                    denomsSel,
-                    exchangeBaseUrl: backupTip.exchange_base_url,
-                    lastError: undefined,
-                    merchantBaseUrl: backupTip.exchange_base_url,
-                    merchantTipId: backupTip.merchant_tip_id,
-                    pickedUpTimestamp: backupTip.timestamp_finished,
-                    retryInfo: initRetryInfo(false),
-                    secretSeed: backupTip.secret_seed,
-                    tipAmountEffective: denomsSel.totalCoinValue,
-                    tipAmountRaw: 
Amounts.parseOrThrow(backupTip.tip_amount_raw),
-                    tipExpiration: backupTip.timestamp_expiration,
-                    walletTipId: backupTip.wallet_tip_id,
-                    denomSelUid: backupTip.selected_denoms_uid,
-                });
-            }
-        }
-        // We now process tombstones.
-        // The import code above should already prevent
-        // importing things that are tombstoned,
-        // but we do tombstone processing last just to be sure.
-        for (const tombstone of backupBlob.tombstones) {
-            const [type, ...rest] = tombstone.split(":");
-            if (type === TombstoneTag.DeleteDepositGroup) {
-                await tx.depositGroups.delete(rest[0]);
-            }
-            else if (type === TombstoneTag.DeletePayment) {
-                await tx.purchases.delete(rest[0]);
-                await tx.proposals.delete(rest[0]);
-            }
-            else if (type === TombstoneTag.DeleteRefreshGroup) {
-                await tx.refreshGroups.delete(rest[0]);
-            }
-            else if (type === TombstoneTag.DeleteRefund) ;
-            else if (type === TombstoneTag.DeleteReserve) {
-                // FIXME:  Once we also have account (=kyc) reserves,
-                // we need to check if the reserve is an account before 
deleting here
-                await tx.reserves.delete(rest[0]);
-            }
-            else if (type === TombstoneTag.DeleteTip) {
-                await tx.tips.delete(rest[0]);
-            }
-            else if (type === TombstoneTag.DeleteWithdrawalGroup) {
-                await tx.withdrawalGroups.delete(rest[0]);
-            }
-            else {
-                logger$9.warn(`unable to process tombstone of type '${type}'`);
-            }
-        }
-    });
-}
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems SA
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$8 = new Logger("operations/backup.ts");
-function concatArrays(xs) {
-    let len = 0;
-    for (const x of xs) {
-        len += x.byteLength;
-    }
-    const out = new Uint8Array(len);
-    let offset = 0;
-    for (const x of xs) {
-        out.set(x, offset);
-        offset += x.length;
-    }
-    return out;
-}
-const magic = "TLRWBK01";
-/**
- * Encrypt the backup.
- *
- * Blob format:
- * Magic "TLRWBK01" (8 bytes)
- * Nonce (24 bytes)
- * Compressed JSON blob (rest)
- */
-async function encryptBackup(config, blob) {
-    const chunks = [];
-    chunks.push(stringToBytes(magic));
-    const nonceStr = config.lastBackupNonce;
-    checkLogicInvariant(!!nonceStr);
-    const nonce = decodeCrock(nonceStr).slice(0, 24);
-    chunks.push(nonce);
-    const backupJsonContent = canonicalJson(blob);
-    logger$8.trace("backup JSON size", backupJsonContent.length);
-    const compressedContent = gzipSync(stringToBytes(backupJsonContent));
-    const secret = deriveBlobSecret(config);
-    const encrypted = secretbox(compressedContent, nonce.slice(0, 24), secret);
-    chunks.push(encrypted);
-    return concatArrays(chunks);
-}
-/**
- * Compute cryptographic values for a backup blob.
- *
- * FIXME: Take data that we already know from the DB.
- * FIXME: Move computations into crypto worker.
- */
-async function computeBackupCryptoData(cryptoApi, backupContent) {
-    const cryptoData = {
-        coinPrivToCompletedCoin: {},
-        denomPubToHash: {},
-        proposalIdToContractTermsHash: {},
-        proposalNoncePrivToPub: {},
-        reservePrivToPub: {},
-    };
-    for (const backupExchangeDetails of backupContent.exchange_details) {
-        for (const backupDenom of backupExchangeDetails.denominations) {
-            for (const backupCoin of backupDenom.coins) {
-                const coinPub = 
encodeCrock(eddsaGetPublic(decodeCrock(backupCoin.coin_priv)));
-                const blindedCoin = 
rsaBlind(hash(decodeCrock(backupCoin.coin_priv)), 
decodeCrock(backupCoin.blinding_key), decodeCrock(backupDenom.denom_pub));
-                cryptoData.coinPrivToCompletedCoin[backupCoin.coin_priv] = {
-                    coinEvHash: encodeCrock(hash(blindedCoin)),
-                    coinPub,
-                };
-            }
-            cryptoData.denomPubToHash[backupDenom.denom_pub] = 
encodeCrock(hash(decodeCrock(backupDenom.denom_pub)));
-        }
-        for (const backupReserve of backupExchangeDetails.reserves) {
-            cryptoData.reservePrivToPub[backupReserve.reserve_priv] = 
encodeCrock(eddsaGetPublic(decodeCrock(backupReserve.reserve_priv)));
-        }
-    }
-    for (const prop of backupContent.proposals) {
-        const contractTermsHash = await 
cryptoApi.hashString(canonicalJson(prop.contract_terms_raw));
-        const noncePub = 
encodeCrock(eddsaGetPublic(decodeCrock(prop.nonce_priv)));
-        cryptoData.proposalNoncePrivToPub[prop.nonce_priv] = noncePub;
-        cryptoData.proposalIdToContractTermsHash[prop.proposal_id] = 
contractTermsHash;
-    }
-    for (const purch of backupContent.purchases) {
-        const contractTermsHash = await 
cryptoApi.hashString(canonicalJson(purch.contract_terms_raw));
-        const noncePub = 
encodeCrock(eddsaGetPublic(decodeCrock(purch.nonce_priv)));
-        cryptoData.proposalNoncePrivToPub[purch.nonce_priv] = noncePub;
-        cryptoData.proposalIdToContractTermsHash[purch.proposal_id] = 
contractTermsHash;
-    }
-    return cryptoData;
-}
-function deriveAccountKeyPair(bc, providerUrl) {
-    const privateKey = kdf(32, decodeCrock(bc.walletRootPriv), 
stringToBytes("taler-sync-account-key-salt"), stringToBytes(providerUrl));
-    return {
-        eddsaPriv: privateKey,
-        eddsaPub: eddsaGetPublic(privateKey),
-    };
-}
-function deriveBlobSecret(bc) {
-    return kdf(32, decodeCrock(bc.walletRootPriv), 
stringToBytes("taler-sync-blob-secret-salt"), 
stringToBytes("taler-sync-blob-secret-info"));
-}
-async function runBackupCycleForProvider(ws, args) {
-    const { backupConfig, provider, currentBackupHash, encBackup, backupJson, 
} = args;
-    const accountKeyPair = deriveAccountKeyPair(backupConfig, 
provider.baseUrl);
-    logger$8.trace(`trying to upload backup to ${provider.baseUrl}`);
-    const syncSig = await ws.cryptoApi.makeSyncSignature({
-        newHash: encodeCrock(currentBackupHash),
-        oldHash: provider.lastBackupHash,
-        accountPriv: encodeCrock(accountKeyPair.eddsaPriv),
-    });
-    logger$8.trace(`sync signature is ${syncSig}`);
-    const accountBackupUrl = new 
URL$1(`/backups/${encodeCrock(accountKeyPair.eddsaPub)}`, provider.baseUrl);
-    const resp = await ws.http.fetch(accountBackupUrl.href, {
-        method: "POST",
-        body: encBackup,
-        headers: Object.assign({ "content-type": "application/octet-stream", 
"sync-signature": syncSig, "if-none-match": encodeCrock(currentBackupHash) }, 
(provider.lastBackupHash
-            ? {
-                "if-match": provider.lastBackupHash,
-            }
-            : {})),
-    });
-    logger$8.trace(`sync response status: ${resp.status}`);
-    if (resp.status === HttpResponseStatus.PaymentRequired) {
-        logger$8.trace("payment required for backup");
-        logger$8.trace(`headers: ${j2s(resp.headers)}`);
-        const talerUri = resp.headers.get("taler");
-        if (!talerUri) {
-            throw Error("no taler URI available to pay provider");
-        }
-        const res = await preparePayForUri(ws, talerUri);
-        let proposalId = res.proposalId;
-        let doPay = false;
-        switch (res.status) {
-            case PreparePayResultType.InsufficientBalance:
-                // FIXME: record in provider state!
-                logger$8.warn("insufficient balance to pay for backup 
provider");
-                proposalId = res.proposalId;
-                break;
-            case PreparePayResultType.PaymentPossible:
-                doPay = true;
-                break;
-            case PreparePayResultType.AlreadyConfirmed:
-                break;
-        }
-        // FIXME: check if the provider is overcharging us!
-        await ws.db
-            .mktx((x) => ({ backupProviders: x.backupProviders }))
-            .runReadWrite(async (tx) => {
-            const provRec = await tx.backupProviders.get(provider.baseUrl);
-            checkDbInvariant(!!provRec);
-            const ids = new Set(provRec.paymentProposalIds);
-            ids.add(proposalId);
-            provRec.paymentProposalIds = Array.from(ids).sort();
-            provRec.currentPaymentProposalId = proposalId;
-            await tx.backupProviders.put(provRec);
-        });
-        if (doPay) {
-            const confirmRes = await confirmPay(ws, proposalId);
-            switch (confirmRes.type) {
-                case ConfirmPayResultType.Pending:
-                    logger$8.warn("payment not yet finished yet");
-                    break;
-            }
-        }
-        if (args.retryAfterPayment) {
-            await runBackupCycleForProvider(ws, 
Object.assign(Object.assign({}, args), { retryAfterPayment: false }));
-        }
-        return;
-    }
-    if (resp.status === HttpResponseStatus.NoContent) {
-        await ws.db
-            .mktx((x) => ({ backupProviders: x.backupProviders }))
-            .runReadWrite(async (tx) => {
-            const prov = await tx.backupProviders.get(provider.baseUrl);
-            if (!prov) {
-                return;
-            }
-            prov.lastBackupHash = encodeCrock(currentBackupHash);
-            prov.lastBackupTimestamp = getTimestampNow();
-            prov.lastError = undefined;
-            await tx.backupProviders.put(prov);
-        });
-        return;
-    }
-    if (resp.status === HttpResponseStatus.Conflict) {
-        logger$8.info("conflicting backup found");
-        const backupEnc = new Uint8Array(await resp.bytes());
-        const backupConfig = await provideBackupState(ws);
-        const blob = await decryptBackup(backupConfig, backupEnc);
-        const cryptoData = await computeBackupCryptoData(ws.cryptoApi, blob);
-        await importBackup(ws, blob, cryptoData);
-        await ws.db
-            .mktx((x) => ({ backupProvider: x.backupProviders }))
-            .runReadWrite(async (tx) => {
-            const prov = await tx.backupProvider.get(provider.baseUrl);
-            if (!prov) {
-                return;
-            }
-            prov.lastBackupHash = encodeCrock(hash(backupEnc));
-            prov.lastBackupTimestamp = getTimestampNow();
-            prov.lastError = undefined;
-            await tx.backupProvider.put(prov);
-        });
-        logger$8.info("processed existing backup");
-        return;
-    }
-    // Some other response that we did not expect!
-    logger$8.error("parsing error response");
-    const err = await readTalerErrorResponse(resp);
-    logger$8.error(`got error response from backup provider: ${j2s(err)}`);
-    await ws.db
-        .mktx((x) => ({ backupProvider: x.backupProviders }))
-        .runReadWrite(async (tx) => {
-        const prov = await tx.backupProvider.get(provider.baseUrl);
-        if (!prov) {
-            return;
-        }
-        prov.lastError = err;
-        await tx.backupProvider.put(prov);
-    });
-}
-/**
- * Do one backup cycle that consists of:
- * 1. Exporting a backup and try to upload it.
- *    Stop if this step succeeds.
- * 2. Download, verify and import backups from connected sync accounts.
- * 3. Upload the updated backup blob.
- */
-async function runBackupCycle(ws) {
-    const providers = await ws.db
-        .mktx((x) => ({ backupProviders: x.backupProviders }))
-        .runReadOnly(async (tx) => {
-        return await tx.backupProviders.iter().toArray();
-    });
-    logger$8.trace("got backup providers", providers);
-    const backupJson = await exportBackup(ws);
-    logger$8.trace(`running backup cycle with backup JSON: 
${j2s(backupJson)}`);
-    const backupConfig = await provideBackupState(ws);
-    const encBackup = await encryptBackup(backupConfig, backupJson);
-    const currentBackupHash = hash(encBackup);
-    for (const provider of providers) {
-        await runBackupCycleForProvider(ws, {
-            provider,
-            backupJson,
-            backupConfig,
-            encBackup,
-            currentBackupHash,
-            retryAfterPayment: true,
-        });
-    }
-}
-const codecForSyncTermsOfServiceResponse = () => buildCodecForObject()
-    .property("storage_limit_in_megabytes", codecForNumber())
-    .property("annual_fee", codecForAmountString())
-    .property("version", codecForString())
-    .build("SyncTermsOfServiceResponse");
-const codecForAddBackupProviderRequest = () => buildCodecForObject()
-    .property("backupProviderBaseUrl", codecForString())
-    .property("activate", codecOptional(codecForBoolean()))
-    .build("AddBackupProviderRequest");
-async function addBackupProvider(ws, req) {
-    logger$8.info(`adding backup provider ${j2s(req)}`);
-    await provideBackupState(ws);
-    const canonUrl = canonicalizeBaseUrl(req.backupProviderBaseUrl);
-    await ws.db
-        .mktx((x) => ({ backupProviders: x.backupProviders }))
-        .runReadWrite(async (tx) => {
-        const oldProv = await tx.backupProviders.get(canonUrl);
-        if (oldProv) {
-            logger$8.info("old backup provider found");
-            if (req.activate) {
-                oldProv.active = true;
-                logger$8.info("setting existing backup provider to active");
-                await tx.backupProviders.put(oldProv);
-            }
-            return;
-        }
-    });
-    const termsUrl = new URL$1("terms", canonUrl);
-    const resp = await ws.http.get(termsUrl.href);
-    const terms = await readSuccessResponseJsonOrThrow(resp, 
codecForSyncTermsOfServiceResponse());
-    await ws.db
-        .mktx((x) => ({ backupProviders: x.backupProviders }))
-        .runReadWrite(async (tx) => {
-        await tx.backupProviders.put({
-            active: !!req.activate,
-            terms: {
-                annualFee: terms.annual_fee,
-                storageLimitInMegabytes: terms.storage_limit_in_megabytes,
-                supportedProtocolVersion: terms.version,
-            },
-            paymentProposalIds: [],
-            baseUrl: canonUrl,
-            lastError: undefined,
-            retryInfo: initRetryInfo(false),
-            uids: [encodeCrock(getRandomBytes(32))],
-        });
-    });
-}
-var ProviderPaymentType;
-(function (ProviderPaymentType) {
-    ProviderPaymentType["Unpaid"] = "unpaid";
-    ProviderPaymentType["Pending"] = "pending";
-    ProviderPaymentType["InsufficientBalance"] = "insufficient-balance";
-    ProviderPaymentType["Paid"] = "paid";
-    ProviderPaymentType["TermsChanged"] = "terms-changed";
-})(ProviderPaymentType || (ProviderPaymentType = {}));
-async function getProviderPaymentInfo(ws, provider) {
-    if (!provider.currentPaymentProposalId) {
-        return {
-            type: ProviderPaymentType.Unpaid,
-        };
-    }
-    const status = await checkPaymentByProposalId(ws, 
provider.currentPaymentProposalId);
-    if (status.status === PreparePayResultType.InsufficientBalance) {
-        return {
-            type: ProviderPaymentType.InsufficientBalance,
-        };
-    }
-    if (status.status === PreparePayResultType.PaymentPossible) {
-        return {
-            type: ProviderPaymentType.Pending,
-        };
-    }
-    if (status.status === PreparePayResultType.AlreadyConfirmed) {
-        if (status.paid) {
-            return {
-                type: ProviderPaymentType.Paid,
-                paidUntil: 
timestampAddDuration(status.contractTerms.timestamp, durationFromSpec({ years: 
1 })),
-            };
-        }
-        else {
-            return {
-                type: ProviderPaymentType.Pending,
-            };
-        }
-    }
-    throw Error("not reached");
-}
-/**
- * Get information about the current state of wallet backups.
- */
-async function getBackupInfo(ws) {
-    const backupConfig = await provideBackupState(ws);
-    const providerRecords = await ws.db
-        .mktx((x) => ({ backupProviders: x.backupProviders }))
-        .runReadOnly(async (tx) => {
-        return await tx.backupProviders.iter().toArray();
-    });
-    const providers = [];
-    for (const x of providerRecords) {
-        providers.push({
-            active: x.active,
-            syncProviderBaseUrl: x.baseUrl,
-            lastSuccessfulBackupTimestamp: x.lastBackupTimestamp,
-            paymentProposalIds: x.paymentProposalIds,
-            lastError: x.lastError,
-            paymentStatus: await getProviderPaymentInfo(ws, x),
-            terms: x.terms,
-        });
-    }
-    return {
-        deviceId: backupConfig.deviceId,
-        walletRootPub: backupConfig.walletRootPub,
-        providers,
-    };
-}
-/**
- * Get backup recovery information, including the wallet's
- * private key.
- */
-async function getBackupRecovery(ws) {
-    const bs = await provideBackupState(ws);
-    const providers = await ws.db
-        .mktx((x) => ({ backupProviders: x.backupProviders }))
-        .runReadOnly(async (tx) => {
-        return await tx.backupProviders.iter().toArray();
-    });
-    return {
-        providers: providers
-            .filter((x) => x.active)
-            .map((x) => {
-            return {
-                url: x.baseUrl,
-            };
-        }),
-        walletRootPriv: bs.walletRootPriv,
-    };
-}
-async function backupRecoveryTheirs(ws, br) {
-    await ws.db
-        .mktx((x) => ({ config: x.config, backupProviders: x.backupProviders 
}))
-        .runReadWrite(async (tx) => {
-        let backupStateEntry = await tx.config.get(WALLET_BACKUP_STATE_KEY);
-        checkDbInvariant(!!backupStateEntry);
-        checkDbInvariant(backupStateEntry.key === WALLET_BACKUP_STATE_KEY);
-        backupStateEntry.value.lastBackupNonce = undefined;
-        backupStateEntry.value.lastBackupTimestamp = undefined;
-        backupStateEntry.value.lastBackupCheckTimestamp = undefined;
-        backupStateEntry.value.lastBackupPlainHash = undefined;
-        backupStateEntry.value.walletRootPriv = br.walletRootPriv;
-        backupStateEntry.value.walletRootPub = 
encodeCrock(eddsaGetPublic(decodeCrock(br.walletRootPriv)));
-        await tx.config.put(backupStateEntry);
-        for (const prov of br.providers) {
-            const existingProv = await tx.backupProviders.get(prov.url);
-            if (!existingProv) {
-                await tx.backupProviders.put({
-                    active: true,
-                    baseUrl: prov.url,
-                    paymentProposalIds: [],
-                    retryInfo: initRetryInfo(false),
-                    lastError: undefined,
-                    uids: [encodeCrock(getRandomBytes(32))],
-                });
-            }
-        }
-        const providers = await tx.backupProviders.iter().toArray();
-        for (const prov of providers) {
-            prov.lastBackupTimestamp = undefined;
-            prov.lastBackupHash = undefined;
-            await tx.backupProviders.put(prov);
-        }
-    });
-}
-async function backupRecoveryOurs(ws, br) {
-    throw Error("not implemented");
-}
-async function loadBackupRecovery(ws, br) {
-    const bs = await provideBackupState(ws);
-    const providers = await ws.db
-        .mktx((x) => ({ backupProviders: x.backupProviders }))
-        .runReadOnly(async (tx) => {
-        return await tx.backupProviders.iter().toArray();
-    });
-    let strategy = br.strategy;
-    if (br.recovery.walletRootPriv != bs.walletRootPriv &&
-        providers.length > 0 &&
-        !strategy) {
-        throw Error("recovery load strategy must be specified for wallet with 
existing providers");
-    }
-    else if (!strategy) {
-        // Default to using the new key if we don't have providers yet.
-        strategy = RecoveryMergeStrategy.Theirs;
-    }
-    if (strategy === RecoveryMergeStrategy.Theirs) {
-        return backupRecoveryTheirs(ws, br.recovery);
-    }
-    else {
-        return backupRecoveryOurs(ws, br.recovery);
-    }
-}
-async function decryptBackup(backupConfig, data) {
-    const rMagic = bytesToString(data.slice(0, 8));
-    if (rMagic != magic) {
-        throw Error("invalid backup file (magic tag mismatch)");
-    }
-    const nonce = data.slice(8, 8 + 24);
-    const box = data.slice(8 + 24);
-    const secret = deriveBlobSecret(backupConfig);
-    const dataCompressed = secretbox_open(box, nonce, secret);
-    if (!dataCompressed) {
-        throw Error("decryption failed");
-    }
-    return JSON.parse(bytesToString(gunzipSync(dataCompressed)));
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$7 = new Logger("operations/balance.ts");
-/**
- * Get balance information.
- */
-async function getBalancesInsideTransaction(ws, tx) {
-    const balanceStore = {};
-    /**
-     * Add amount to a balance field, both for
-     * the slicing by exchange and currency.
-     */
-    const initBalance = (currency) => {
-        const b = balanceStore[currency];
-        if (!b) {
-            balanceStore[currency] = {
-                available: Amounts.getZero(currency),
-                pendingIncoming: Amounts.getZero(currency),
-                pendingOutgoing: Amounts.getZero(currency),
-            };
-        }
-        return balanceStore[currency];
-    };
-    // Initialize balance to zero, even if we didn't start withdrawing yet.
-    await tx.reserves.iter().forEach((r) => {
-        const b = initBalance(r.currency);
-        if (!r.initialWithdrawalStarted) {
-            b.pendingIncoming = Amounts.add(b.pendingIncoming, 
r.initialDenomSel.totalCoinValue).amount;
-        }
-    });
-    await tx.coins.iter().forEach((c) => {
-        // Only count fresh coins, as dormant coins will
-        // already be in a refresh session.
-        if (c.status === CoinStatus.Fresh) {
-            const b = initBalance(c.currentAmount.currency);
-            b.available = Amounts.add(b.available, c.currentAmount).amount;
-        }
-    });
-    await tx.refreshGroups.iter().forEach((r) => {
-        // Don't count finished refreshes, since the refresh already resulted
-        // in coins being added to the wallet.
-        if (r.timestampFinished) {
-            return;
-        }
-        for (let i = 0; i < r.oldCoinPubs.length; i++) {
-            const session = r.refreshSessionPerCoin[i];
-            if (session) {
-                const b = initBalance(session.amountRefreshOutput.currency);
-                // We are always assuming the refresh will succeed, thus we
-                // report the output as available balance.
-                b.available = Amounts.add(b.available, 
session.amountRefreshOutput).amount;
-            }
-            else {
-                const b = initBalance(r.inputPerCoin[i].currency);
-                b.available = Amounts.add(b.available, 
r.estimatedOutputPerCoin[i]).amount;
-            }
-        }
-    });
-    await tx.withdrawalGroups.iter().forEach((wds) => {
-        if (wds.timestampFinish) {
-            return;
-        }
-        const b = initBalance(wds.denomsSel.totalWithdrawCost.currency);
-        b.pendingIncoming = Amounts.add(b.pendingIncoming, 
wds.denomsSel.totalCoinValue).amount;
-    });
-    const balancesResponse = {
-        balances: [],
-    };
-    Object.keys(balanceStore)
-        .sort()
-        .forEach((c) => {
-        const v = balanceStore[c];
-        balancesResponse.balances.push({
-            available: Amounts.stringify(v.available),
-            pendingIncoming: Amounts.stringify(v.pendingIncoming),
-            pendingOutgoing: Amounts.stringify(v.pendingOutgoing),
-            hasPendingTransactions: false,
-            requiresUserInput: false,
-        });
-    });
-    return balancesResponse;
-}
-/**
- * Get detailed balance information, sliced by exchange and by currency.
- */
-async function getBalances(ws) {
-    logger$7.trace("starting to compute balance");
-    const wbal = await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        refreshGroups: x.refreshGroups,
-        reserves: x.reserves,
-        purchases: x.purchases,
-        withdrawalGroups: x.withdrawalGroups,
-    }))
-        .runReadOnly(async (tx) => {
-        return getBalancesInsideTransaction(ws, tx);
-    });
-    logger$7.trace("finished computing wallet balance");
-    return wbal;
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-async function gatherExchangePending(tx, now, resp) {
-    await tx.exchanges.iter().forEachAsync(async (e) => {
-        resp.pendingOperations.push({
-            type: PendingOperationType.ExchangeUpdate,
-            givesLifeness: false,
-            timestampDue: e.nextUpdate,
-            exchangeBaseUrl: e.baseUrl,
-            lastError: e.lastError,
-        });
-        resp.pendingOperations.push({
-            type: PendingOperationType.ExchangeCheckRefresh,
-            timestampDue: e.nextRefreshCheck,
-            givesLifeness: false,
-            exchangeBaseUrl: e.baseUrl,
-        });
-    });
-}
-async function gatherReservePending(tx, now, resp) {
-    await tx.reserves.iter().forEach((reserve) => {
-        const reserveType = reserve.bankInfo
-            ? ReserveType.TalerBankWithdraw
-            : ReserveType.Manual;
-        switch (reserve.reserveStatus) {
-            case ReserveRecordStatus.DORMANT:
-                // nothing to report as pending
-                break;
-            case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-            case ReserveRecordStatus.QUERYING_STATUS:
-            case ReserveRecordStatus.REGISTERING_BANK:
-                resp.pendingOperations.push({
-                    type: PendingOperationType.Reserve,
-                    givesLifeness: true,
-                    timestampDue: reserve.retryInfo.nextRetry,
-                    stage: reserve.reserveStatus,
-                    timestampCreated: reserve.timestampCreated,
-                    reserveType,
-                    reservePub: reserve.reservePub,
-                    retryInfo: reserve.retryInfo,
-                });
-                break;
-        }
-    });
-}
-async function gatherRefreshPending(tx, now, resp) {
-    await tx.refreshGroups.iter().forEach((r) => {
-        if (r.timestampFinished) {
-            return;
-        }
-        resp.pendingOperations.push({
-            type: PendingOperationType.Refresh,
-            givesLifeness: true,
-            timestampDue: r.retryInfo.nextRetry,
-            refreshGroupId: r.refreshGroupId,
-            finishedPerCoin: r.finishedPerCoin,
-            retryInfo: r.retryInfo,
-        });
-    });
-}
-async function gatherWithdrawalPending(tx, now, resp) {
-    await tx.withdrawalGroups.iter().forEachAsync(async (wsr) => {
-        if (wsr.timestampFinish) {
-            return;
-        }
-        await tx.planchets.indexes.byGroup
-            .iter(wsr.withdrawalGroupId)
-            .forEach((x) => {
-            if (x.withdrawalDone) ;
-        });
-        resp.pendingOperations.push({
-            type: PendingOperationType.Withdraw,
-            givesLifeness: true,
-            timestampDue: wsr.retryInfo.nextRetry,
-            withdrawalGroupId: wsr.withdrawalGroupId,
-            lastError: wsr.lastError,
-            retryInfo: wsr.retryInfo,
-        });
-    });
-}
-async function gatherProposalPending(tx, now, resp) {
-    await tx.proposals.iter().forEach((proposal) => {
-        var _a, _b;
-        if (proposal.proposalStatus == ProposalStatus.PROPOSED) ;
-        else if (proposal.proposalStatus == ProposalStatus.DOWNLOADING) {
-            const timestampDue = (_b = (_a = proposal.retryInfo) === null || 
_a === void 0 ? void 0 : _a.nextRetry) !== null && _b !== void 0 ? _b : 
getTimestampNow();
-            resp.pendingOperations.push({
-                type: PendingOperationType.ProposalDownload,
-                givesLifeness: true,
-                timestampDue,
-                merchantBaseUrl: proposal.merchantBaseUrl,
-                orderId: proposal.orderId,
-                proposalId: proposal.proposalId,
-                proposalTimestamp: proposal.timestamp,
-                lastError: proposal.lastError,
-                retryInfo: proposal.retryInfo,
-            });
-        }
-    });
-}
-async function gatherTipPending(tx, now, resp) {
-    await tx.tips.iter().forEach((tip) => {
-        if (tip.pickedUpTimestamp) {
-            return;
-        }
-        if (tip.acceptedTimestamp) {
-            resp.pendingOperations.push({
-                type: PendingOperationType.TipPickup,
-                givesLifeness: true,
-                timestampDue: tip.retryInfo.nextRetry,
-                merchantBaseUrl: tip.merchantBaseUrl,
-                tipId: tip.walletTipId,
-                merchantTipId: tip.merchantTipId,
-            });
-        }
-    });
-}
-async function gatherPurchasePending(tx, now, resp) {
-    await tx.purchases.iter().forEach((pr) => {
-        var _a, _b;
-        if (pr.paymentSubmitPending && pr.abortStatus === AbortStatus.None) {
-            const timestampDue = (_b = (_a = pr.payRetryInfo) === null || _a 
=== void 0 ? void 0 : _a.nextRetry) !== null && _b !== void 0 ? _b : 
getTimestampNow();
-            resp.pendingOperations.push({
-                type: PendingOperationType.Pay,
-                givesLifeness: true,
-                timestampDue,
-                isReplay: false,
-                proposalId: pr.proposalId,
-                retryInfo: pr.payRetryInfo,
-                lastError: pr.lastPayError,
-            });
-        }
-        if (pr.refundQueryRequested) {
-            resp.pendingOperations.push({
-                type: PendingOperationType.RefundQuery,
-                givesLifeness: true,
-                timestampDue: pr.refundStatusRetryInfo.nextRetry,
-                proposalId: pr.proposalId,
-                retryInfo: pr.refundStatusRetryInfo,
-                lastError: pr.lastRefundStatusError,
-            });
-        }
-    });
-}
-async function gatherRecoupPending(tx, now, resp) {
-    await tx.recoupGroups.iter().forEach((rg) => {
-        if (rg.timestampFinished) {
-            return;
-        }
-        resp.pendingOperations.push({
-            type: PendingOperationType.Recoup,
-            givesLifeness: true,
-            timestampDue: rg.retryInfo.nextRetry,
-            recoupGroupId: rg.recoupGroupId,
-            retryInfo: rg.retryInfo,
-            lastError: rg.lastError,
-        });
-    });
-}
-async function gatherDepositPending(tx, now, resp) {
-    await tx.depositGroups.iter().forEach((dg) => {
-        if (dg.timestampFinished) {
-            return;
-        }
-        resp.pendingOperations.push({
-            type: PendingOperationType.Deposit,
-            givesLifeness: true,
-            timestampDue: dg.retryInfo.nextRetry,
-            depositGroupId: dg.depositGroupId,
-            retryInfo: dg.retryInfo,
-            lastError: dg.lastError,
-        });
-    });
-}
-async function getPendingOperations(ws) {
-    const now = getTimestampNow();
-    return await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-        reserves: x.reserves,
-        refreshGroups: x.refreshGroups,
-        coins: x.coins,
-        withdrawalGroups: x.withdrawalGroups,
-        proposals: x.proposals,
-        tips: x.tips,
-        purchases: x.purchases,
-        planchets: x.planchets,
-        depositGroups: x.depositGroups,
-        recoupGroups: x.recoupGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const walletBalance = await getBalancesInsideTransaction(ws, tx);
-        const resp = {
-            walletBalance,
-            pendingOperations: [],
-        };
-        await gatherExchangePending(tx, now, resp);
-        await gatherReservePending(tx, now, resp);
-        await gatherRefreshPending(tx, now, resp);
-        await gatherWithdrawalPending(tx, now, resp);
-        await gatherProposalPending(tx, now, resp);
-        await gatherTipPending(tx, now, resp);
-        await gatherPurchasePending(tx, now, resp);
-        await gatherRecoupPending(tx, now, resp);
-        await gatherDepositPending(tx, now, resp);
-        return resp;
-    });
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019-2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$6 = new Logger("refund.ts");
-/**
- * Retry querying and applying refunds for an order later.
- */
-async function incrementPurchaseQueryRefundRetry(ws, proposalId, err) {
-    await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-    }))
-        .runReadWrite(async (tx) => {
-        const pr = await tx.purchases.get(proposalId);
-        if (!pr) {
-            return;
-        }
-        if (!pr.refundStatusRetryInfo) {
-            return;
-        }
-        pr.refundStatusRetryInfo.retryCounter++;
-        updateRetryInfoTimeout(pr.refundStatusRetryInfo);
-        pr.lastRefundStatusError = err;
-        await tx.purchases.put(pr);
-    });
-    if (err) {
-        ws.notify({
-            type: NotificationType.RefundStatusOperationError,
-            error: err,
-        });
-    }
-}
-function getRefundKey(d) {
-    return `${d.coin_pub}-${d.rtransaction_id}`;
-}
-async function applySuccessfulRefund(tx, p, refreshCoinsMap, r) {
-    // FIXME: check signature before storing it as valid!
-    const refundKey = getRefundKey(r);
-    const coin = await tx.coins.get(r.coin_pub);
-    if (!coin) {
-        logger$6.warn("coin not found, can't apply refund");
-        return;
-    }
-    const denom = await tx.denominations.get([
-        coin.exchangeBaseUrl,
-        coin.denomPubHash,
-    ]);
-    if (!denom) {
-        throw Error("inconsistent database");
-    }
-    refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub };
-    const refundAmount = Amounts.parseOrThrow(r.refund_amount);
-    const refundFee = denom.feeRefund;
-    coin.status = CoinStatus.Dormant;
-    coin.currentAmount = Amounts.add(coin.currentAmount, refundAmount).amount;
-    coin.currentAmount = Amounts.sub(coin.currentAmount, refundFee).amount;
-    logger$6.trace(`coin amount after is 
${Amounts.stringify(coin.currentAmount)}`);
-    await tx.coins.put(coin);
-    const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
-        .iter(coin.exchangeBaseUrl)
-        .toArray();
-    const amountLeft = Amounts.sub(Amounts.add(coin.currentAmount, 
Amounts.parseOrThrow(r.refund_amount))
-        .amount, denom.feeRefund).amount;
-    const totalRefreshCostBound = getTotalRefreshCost(allDenoms, denom, 
amountLeft);
-    p.refunds[refundKey] = {
-        type: RefundState.Applied,
-        obtainedTime: getTimestampNow(),
-        executionTime: r.execution_time,
-        refundAmount: Amounts.parseOrThrow(r.refund_amount),
-        refundFee: denom.feeRefund,
-        totalRefreshCostBound,
-        coinPub: r.coin_pub,
-        rtransactionId: r.rtransaction_id,
-    };
-}
-async function storePendingRefund(tx, p, r) {
-    const refundKey = getRefundKey(r);
-    const coin = await tx.coins.get(r.coin_pub);
-    if (!coin) {
-        logger$6.warn("coin not found, can't apply refund");
-        return;
-    }
-    const denom = await tx.denominations.get([
-        coin.exchangeBaseUrl,
-        coin.denomPubHash,
-    ]);
-    if (!denom) {
-        throw Error("inconsistent database");
-    }
-    const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
-        .iter(coin.exchangeBaseUrl)
-        .toArray();
-    const amountLeft = Amounts.sub(Amounts.add(coin.currentAmount, 
Amounts.parseOrThrow(r.refund_amount))
-        .amount, denom.feeRefund).amount;
-    const totalRefreshCostBound = getTotalRefreshCost(allDenoms, denom, 
amountLeft);
-    p.refunds[refundKey] = {
-        type: RefundState.Pending,
-        obtainedTime: getTimestampNow(),
-        executionTime: r.execution_time,
-        refundAmount: Amounts.parseOrThrow(r.refund_amount),
-        refundFee: denom.feeRefund,
-        totalRefreshCostBound,
-        coinPub: r.coin_pub,
-        rtransactionId: r.rtransaction_id,
-    };
-}
-async function storeFailedRefund(tx, p, refreshCoinsMap, r) {
-    const refundKey = getRefundKey(r);
-    const coin = await tx.coins.get(r.coin_pub);
-    if (!coin) {
-        logger$6.warn("coin not found, can't apply refund");
-        return;
-    }
-    const denom = await tx.denominations.get([
-        coin.exchangeBaseUrl,
-        coin.denomPubHash,
-    ]);
-    if (!denom) {
-        throw Error("inconsistent database");
-    }
-    const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
-        .iter(coin.exchangeBaseUrl)
-        .toArray();
-    const amountLeft = Amounts.sub(Amounts.add(coin.currentAmount, 
Amounts.parseOrThrow(r.refund_amount))
-        .amount, denom.feeRefund).amount;
-    const totalRefreshCostBound = getTotalRefreshCost(allDenoms, denom, 
amountLeft);
-    p.refunds[refundKey] = {
-        type: RefundState.Failed,
-        obtainedTime: getTimestampNow(),
-        executionTime: r.execution_time,
-        refundAmount: Amounts.parseOrThrow(r.refund_amount),
-        refundFee: denom.feeRefund,
-        totalRefreshCostBound,
-        coinPub: r.coin_pub,
-        rtransactionId: r.rtransaction_id,
-    };
-    if (p.abortStatus === AbortStatus.AbortRefund) {
-        // Refund failed because the merchant didn't even try to deposit
-        // the coin yet, so we try to refresh.
-        if (r.exchange_code === 
TalerErrorCode.EXCHANGE_REFUND_DEPOSIT_NOT_FOUND) {
-            const coin = await tx.coins.get(r.coin_pub);
-            if (!coin) {
-                logger$6.warn("coin not found, can't apply refund");
-                return;
-            }
-            const denom = await tx.denominations.get([
-                coin.exchangeBaseUrl,
-                coin.denomPubHash,
-            ]);
-            if (!denom) {
-                logger$6.warn("denomination for coin missing");
-                return;
-            }
-            let contrib;
-            for (let i = 0; i < p.payCoinSelection.coinPubs.length; i++) {
-                if (p.payCoinSelection.coinPubs[i] === r.coin_pub) {
-                    contrib = p.payCoinSelection.coinContributions[i];
-                }
-            }
-            if (contrib) {
-                coin.currentAmount = Amounts.add(coin.currentAmount, 
contrib).amount;
-                coin.currentAmount = Amounts.sub(coin.currentAmount, 
denom.feeRefund).amount;
-            }
-            refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub };
-            await tx.coins.put(coin);
-        }
-    }
-}
-async function acceptRefunds(ws, proposalId, refunds, reason) {
-    logger$6.trace("handling refunds", refunds);
-    const now = getTimestampNow();
-    await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-        coins: x.coins,
-        denominations: x.denominations,
-        refreshGroups: x.refreshGroups,
-    }))
-        .runReadWrite(async (tx) => {
-        const p = await tx.purchases.get(proposalId);
-        if (!p) {
-            logger$6.error("purchase not found, not adding refunds");
-            return;
-        }
-        const refreshCoinsMap = {};
-        for (const refundStatus of refunds) {
-            const refundKey = getRefundKey(refundStatus);
-            const existingRefundInfo = p.refunds[refundKey];
-            const isPermanentFailure = refundStatus.type === "failure" &&
-                refundStatus.exchange_status >= 400 &&
-                refundStatus.exchange_status < 500;
-            // Already failed.
-            if ((existingRefundInfo === null || existingRefundInfo === void 0 
? void 0 : existingRefundInfo.type) === RefundState.Failed) {
-                continue;
-            }
-            // Already applied.
-            if ((existingRefundInfo === null || existingRefundInfo === void 0 
? void 0 : existingRefundInfo.type) === RefundState.Applied) {
-                continue;
-            }
-            // Still pending.
-            if (refundStatus.type === "failure" &&
-                !isPermanentFailure &&
-                (existingRefundInfo === null || existingRefundInfo === void 0 
? void 0 : existingRefundInfo.type) === RefundState.Pending) {
-                continue;
-            }
-            // Invariant: (!existingRefundInfo) || (existingRefundInfo === 
Pending)
-            if (refundStatus.type === "success") {
-                await applySuccessfulRefund(tx, p, refreshCoinsMap, 
refundStatus);
-            }
-            else if (isPermanentFailure) {
-                await storeFailedRefund(tx, p, refreshCoinsMap, refundStatus);
-            }
-            else {
-                await storePendingRefund(tx, p, refundStatus);
-            }
-        }
-        const refreshCoinsPubs = Object.values(refreshCoinsMap);
-        if (refreshCoinsPubs.length > 0) {
-            await createRefreshGroup(ws, tx, refreshCoinsPubs, 
RefreshReason.Refund);
-        }
-        // Are we done with querying yet, or do we need to do another round
-        // after a retry delay?
-        let queryDone = true;
-        if (p.timestampFirstSuccessfulPay &&
-            p.autoRefundDeadline &&
-            p.autoRefundDeadline.t_ms > now.t_ms) {
-            queryDone = false;
-        }
-        let numPendingRefunds = 0;
-        for (const ri of Object.values(p.refunds)) {
-            switch (ri.type) {
-                case RefundState.Pending:
-                    numPendingRefunds++;
-                    break;
-            }
-        }
-        if (numPendingRefunds > 0) {
-            queryDone = false;
-        }
-        if (queryDone) {
-            p.timestampLastRefundStatus = now;
-            p.lastRefundStatusError = undefined;
-            p.refundStatusRetryInfo = initRetryInfo(false);
-            p.refundQueryRequested = false;
-            if (p.abortStatus === AbortStatus.AbortRefund) {
-                p.abortStatus = AbortStatus.AbortFinished;
-            }
-            logger$6.trace("refund query done");
-        }
-        else {
-            // No error, but we need to try again!
-            p.timestampLastRefundStatus = now;
-            p.refundStatusRetryInfo.retryCounter++;
-            updateRetryInfoTimeout(p.refundStatusRetryInfo);
-            p.lastRefundStatusError = undefined;
-            logger$6.trace("refund query not done");
-        }
-        await tx.purchases.put(p);
-    });
-    ws.notify({
-        type: NotificationType.RefundQueried,
-    });
-}
-/**
- * Accept a refund, return the contract hash for the contract
- * that was involved in the refund.
- */
-async function applyRefund(ws, talerRefundUri) {
-    const parseResult = parseRefundUri(talerRefundUri);
-    logger$6.trace("applying refund", parseResult);
-    if (!parseResult) {
-        throw Error("invalid refund URI");
-    }
-    let purchase = await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.purchases.indexes.byMerchantUrlAndOrderId.get([
-            parseResult.merchantBaseUrl,
-            parseResult.orderId,
-        ]);
-    });
-    if (!purchase) {
-        throw Error(`no purchase for the taler://refund/ URI 
(${talerRefundUri}) was found`);
-    }
-    const proposalId = purchase.proposalId;
-    logger$6.info("processing purchase for refund");
-    const success = await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-    }))
-        .runReadWrite(async (tx) => {
-        const p = await tx.purchases.get(proposalId);
-        if (!p) {
-            logger$6.error("no purchase found for refund URL");
-            return false;
-        }
-        p.refundQueryRequested = true;
-        p.lastRefundStatusError = undefined;
-        p.refundStatusRetryInfo = initRetryInfo();
-        await tx.purchases.put(p);
-        return true;
-    });
-    if (success) {
-        ws.notify({
-            type: NotificationType.RefundStarted,
-        });
-        await processPurchaseQueryRefund(ws, proposalId);
-    }
-    purchase = await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.purchases.get(proposalId);
-    });
-    if (!purchase) {
-        throw Error("purchase no longer exists");
-    }
-    const p = purchase;
-    let amountRefundGranted = 
Amounts.getZero(purchase.download.contractData.amount.currency);
-    let amountRefundGone = 
Amounts.getZero(purchase.download.contractData.amount.currency);
-    let pendingAtExchange = false;
-    Object.keys(purchase.refunds).forEach((rk) => {
-        const refund = p.refunds[rk];
-        if (refund.type === RefundState.Pending) {
-            pendingAtExchange = true;
-        }
-        if (refund.type === RefundState.Applied ||
-            refund.type === RefundState.Pending) {
-            amountRefundGranted = Amounts.add(amountRefundGranted, 
Amounts.sub(refund.refundAmount, refund.refundFee, 
refund.totalRefreshCostBound).amount).amount;
-        }
-        else {
-            amountRefundGone = Amounts.add(amountRefundGone, 
refund.refundAmount)
-                .amount;
-        }
-    });
-    return {
-        contractTermsHash: purchase.download.contractData.contractTermsHash,
-        proposalId: purchase.proposalId,
-        amountEffectivePaid: Amounts.stringify(purchase.totalPayCost),
-        amountRefundGone: Amounts.stringify(amountRefundGone),
-        amountRefundGranted: Amounts.stringify(amountRefundGranted),
-        pendingAtExchange,
-        info: {
-            contractTermsHash: 
purchase.download.contractData.contractTermsHash,
-            merchant: purchase.download.contractData.merchant,
-            orderId: purchase.download.contractData.orderId,
-            products: purchase.download.contractData.products,
-            summary: purchase.download.contractData.summary,
-            fulfillmentMessage: 
purchase.download.contractData.fulfillmentMessage,
-            summary_i18n: purchase.download.contractData.summaryI18n,
-            fulfillmentMessage_i18n: 
purchase.download.contractData.fulfillmentMessageI18n,
-        },
-    };
-}
-async function processPurchaseQueryRefund(ws, proposalId, forceNow = false) {
-    const onOpErr = (e) => incrementPurchaseQueryRefundRetry(ws, proposalId, 
e);
-    await guardOperationException(() => processPurchaseQueryRefundImpl(ws, 
proposalId, forceNow), onOpErr);
-}
-async function resetPurchaseQueryRefundRetry(ws, proposalId) {
-    await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-    }))
-        .runReadWrite(async (tx) => {
-        const x = await tx.purchases.get(proposalId);
-        if (x) {
-            x.refundStatusRetryInfo = initRetryInfo();
-            await tx.purchases.put(x);
-        }
-    });
-}
-async function processPurchaseQueryRefundImpl(ws, proposalId, forceNow) {
-    if (forceNow) {
-        await resetPurchaseQueryRefundRetry(ws, proposalId);
-    }
-    const purchase = await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-    }))
-        .runReadOnly(async (tx) => {
-        return tx.purchases.get(proposalId);
-    });
-    if (!purchase) {
-        return;
-    }
-    if (!purchase.refundQueryRequested) {
-        return;
-    }
-    if (purchase.timestampFirstSuccessfulPay) {
-        const requestUrl = new 
URL$1(`orders/${purchase.download.contractData.orderId}/refund`, 
purchase.download.contractData.merchantBaseUrl);
-        logger$6.trace(`making refund request to ${requestUrl.href}`);
-        const request = await ws.http.postJson(requestUrl.href, {
-            h_contract: purchase.download.contractData.contractTermsHash,
-        });
-        logger$6.trace("got json", JSON.stringify(await request.json(), 
undefined, 2));
-        const refundResponse = await readSuccessResponseJsonOrThrow(request, 
codecForMerchantOrderRefundPickupResponse());
-        await acceptRefunds(ws, proposalId, refundResponse.refunds, 
RefundReason.NormalRefund);
-    }
-    else if (purchase.abortStatus === AbortStatus.AbortRefund) {
-        const requestUrl = new 
URL$1(`orders/${purchase.download.contractData.orderId}/abort`, 
purchase.download.contractData.merchantBaseUrl);
-        const abortingCoins = [];
-        await ws.db
-            .mktx((x) => ({
-            coins: x.coins,
-        }))
-            .runReadOnly(async (tx) => {
-            for (let i = 0; i < purchase.payCoinSelection.coinPubs.length; 
i++) {
-                const coinPub = purchase.payCoinSelection.coinPubs[i];
-                const coin = await tx.coins.get(coinPub);
-                checkDbInvariant(!!coin, "expected coin to be present");
-                abortingCoins.push({
-                    coin_pub: coinPub,
-                    contribution: 
Amounts.stringify(purchase.payCoinSelection.coinContributions[i]),
-                    exchange_url: coin.exchangeBaseUrl,
-                });
-            }
-        });
-        const abortReq = {
-            h_contract: purchase.download.contractData.contractTermsHash,
-            coins: abortingCoins,
-        };
-        logger$6.trace(`making order abort request to ${requestUrl.href}`);
-        const request = await ws.http.postJson(requestUrl.href, abortReq);
-        const abortResp = await readSuccessResponseJsonOrThrow(request, 
codecForAbortResponse());
-        const refunds = [];
-        if (abortResp.refunds.length != abortingCoins.length) {
-            // FIXME: define error code!
-            throw Error("invalid order abort response");
-        }
-        for (let i = 0; i < abortResp.refunds.length; i++) {
-            const r = abortResp.refunds[i];
-            refunds.push(Object.assign(Object.assign({}, r), { coin_pub: 
purchase.payCoinSelection.coinPubs[i], refund_amount: 
Amounts.stringify(purchase.payCoinSelection.coinContributions[i]), 
rtransaction_id: 0, execution_time: 
timestampAddDuration(purchase.download.contractData.timestamp, {
-                    d_ms: 1000,
-                }) }));
-        }
-        await acceptRefunds(ws, proposalId, refunds, RefundReason.AbortRefund);
-    }
-}
-async function abortFailedPayWithRefund(ws, proposalId) {
-    await ws.db
-        .mktx((x) => ({
-        purchases: x.purchases,
-    }))
-        .runReadWrite(async (tx) => {
-        const purchase = await tx.purchases.get(proposalId);
-        if (!purchase) {
-            throw Error("purchase not found");
-        }
-        if (purchase.timestampFirstSuccessfulPay) {
-            // No point in aborting it.  We don't even report an error.
-            logger$6.warn(`tried to abort successful payment`);
-            return;
-        }
-        if (purchase.abortStatus !== AbortStatus.None) {
-            return;
-        }
-        purchase.refundQueryRequested = true;
-        purchase.paymentSubmitPending = false;
-        purchase.abortStatus = AbortStatus.AbortRefund;
-        purchase.lastPayError = undefined;
-        purchase.payRetryInfo = initRetryInfo(false);
-        await tx.purchases.put(purchase);
-    });
-    processPurchaseQueryRefund(ws, proposalId, true).catch((e) => {
-        logger$6.trace(`error during refund processing after abort pay: ${e}`);
-    });
-}
-
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$5 = new Logger("operations/testing.ts");
-/**
- * Generate a random alphanumeric ID.  Does *not* use cryptographically
- * secure randomness.
- */
-function makeId$1(length) {
-    let result = "";
-    const characters = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-    for (let i = 0; i < length; i++) {
-        result += characters.charAt(Math.floor(Math.random() * 
characters.length));
-    }
-    return result;
-}
-/**
- * Helper function to generate the "Authorization" HTTP header.
- */
-function makeAuth(username, password) {
-    const auth = `${username}:${password}`;
-    const authEncoded = Buffer.from(auth).toString("base64");
-    return `Basic ${authEncoded}`;
-}
-async function withdrawTestBalance(ws, amount = "TESTKUDOS:10", bankBaseUrl = 
"https://bank.test.taler.net/";, exchangeBaseUrl = 
"https://exchange.test.taler.net/";) {
-    const bankUser = await registerRandomBankUser(ws.http, bankBaseUrl);
-    logger$5.trace(`Registered bank user ${JSON.stringify(bankUser)}`);
-    const wresp = await createBankWithdrawalUri(ws.http, bankBaseUrl, 
bankUser, amount);
-    await createTalerWithdrawReserve(ws, wresp.taler_withdraw_uri, 
exchangeBaseUrl);
-    await confirmBankWithdrawalUri(ws.http, bankBaseUrl, bankUser, 
wresp.withdrawal_id);
-}
-function getMerchantAuthHeader(m) {
-    if (m.authToken) {
-        return {
-            Authorization: `Bearer ${m.authToken}`,
-        };
-    }
-    return {};
-}
-async function createBankWithdrawalUri(http, bankBaseUrl, bankUser, amount) {
-    const reqUrl = new URL$1(`accounts/${bankUser.username}/withdrawals`, 
bankBaseUrl).href;
-    const resp = await http.postJson(reqUrl, {
-        amount,
-    }, {
-        headers: {
-            Authorization: makeAuth(bankUser.username, bankUser.password),
-        },
-    });
-    const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny());
-    return respJson;
-}
-async function confirmBankWithdrawalUri(http, bankBaseUrl, bankUser, 
withdrawalId) {
-    const reqUrl = new 
URL$1(`accounts/${bankUser.username}/withdrawals/${withdrawalId}/confirm`, 
bankBaseUrl).href;
-    const resp = await http.postJson(reqUrl, {}, {
-        headers: {
-            Authorization: makeAuth(bankUser.username, bankUser.password),
-        },
-    });
-    await readSuccessResponseJsonOrThrow(resp, codecForAny());
-    return;
-}
-async function registerRandomBankUser(http, bankBaseUrl) {
-    const reqUrl = new URL$1("testing/register", bankBaseUrl).href;
-    const randId = makeId$1(8);
-    const bankUser = {
-        username: `testuser-${randId}`,
-        password: `testpw-${randId}`,
-    };
-    const resp = await http.postJson(reqUrl, bankUser);
-    await checkSuccessResponseOrThrow(resp);
-    return bankUser;
-}
-async function refund(http, merchantBackend, orderId, reason, refundAmount) {
-    const reqUrl = new URL$1(`private/orders/${orderId}/refund`, 
merchantBackend.baseUrl);
-    const refundReq = {
-        order_id: orderId,
-        reason,
-        refund: refundAmount,
-    };
-    const resp = await http.postJson(reqUrl.href, refundReq, {
-        headers: getMerchantAuthHeader(merchantBackend),
-    });
-    const r = await readSuccessResponseJsonOrThrow(resp, codecForAny());
-    const refundUri = r.taler_refund_uri;
-    if (!refundUri) {
-        throw Error("no refund URI in response");
-    }
-    return refundUri;
-}
-async function createOrder(http, merchantBackend, amount, summary, 
fulfillmentUrl) {
-    const t = Math.floor(new Date().getTime() / 1000) + 15 * 60;
-    const reqUrl = new URL$1("private/orders", merchantBackend.baseUrl).href;
-    const orderReq = {
-        order: {
-            amount,
-            summary,
-            fulfillment_url: fulfillmentUrl,
-            refund_deadline: { t_ms: t * 1000 },
-            wire_transfer_deadline: { t_ms: t * 1000 },
-        },
-    };
-    const resp = await http.postJson(reqUrl, orderReq, {
-        headers: getMerchantAuthHeader(merchantBackend),
-    });
-    const r = await readSuccessResponseJsonOrThrow(resp, codecForAny());
-    const orderId = r.order_id;
-    if (!orderId) {
-        throw Error("no order id in response");
-    }
-    return { orderId };
-}
-async function checkPayment(http, merchantBackend, orderId) {
-    const reqUrl = new URL$1(`/private/orders/${orderId}`, 
merchantBackend.baseUrl);
-    reqUrl.searchParams.set("order_id", orderId);
-    const resp = await http.get(reqUrl.href, {
-        headers: getMerchantAuthHeader(merchantBackend),
-    });
-    return readSuccessResponseJsonOrThrow(resp, 
codecForCheckPaymentResponse());
-}
-async function makePayment(ws, merchant, amount, summary) {
-    const orderResp = await createOrder(ws.http, merchant, amount, summary, 
"taler://fulfillment-success/thx");
-    logger$5.trace("created order with orderId", orderResp.orderId);
-    let paymentStatus = await checkPayment(ws.http, merchant, 
orderResp.orderId);
-    logger$5.trace("payment status", paymentStatus);
-    const talerPayUri = paymentStatus.taler_pay_uri;
-    if (!talerPayUri) {
-        throw Error("no taler://pay/ URI in payment response");
-    }
-    const preparePayResult = await preparePayForUri(ws, talerPayUri);
-    logger$5.trace("prepare pay result", preparePayResult);
-    if (preparePayResult.status != "payment-possible") {
-        throw Error("payment not possible");
-    }
-    const confirmPayResult = await confirmPay(ws, preparePayResult.proposalId, 
undefined);
-    logger$5.trace("confirmPayResult", confirmPayResult);
-    paymentStatus = await checkPayment(ws.http, merchant, orderResp.orderId);
-    logger$5.trace("payment status after wallet payment:", paymentStatus);
-    if (paymentStatus.order_status !== "paid") {
-        throw Error("payment did not succeed");
-    }
-    return {
-        orderId: orderResp.orderId,
-    };
-}
-async function runIntegrationTest(ws, args) {
-    logger$5.info("running test with arguments", args);
-    const parsedSpendAmount = Amounts.parseOrThrow(args.amountToSpend);
-    const currency = parsedSpendAmount.currency;
-    logger$5.info("withdrawing test balance");
-    await withdrawTestBalance(ws, args.amountToWithdraw, args.bankBaseUrl, 
args.exchangeBaseUrl);
-    await runUntilDone(ws);
-    logger$5.info("done withdrawing test balance");
-    const balance = await getBalances(ws);
-    logger$5.trace(JSON.stringify(balance, null, 2));
-    const myMerchant = {
-        baseUrl: args.merchantBaseUrl,
-        authToken: args.merchantAuthToken,
-    };
-    await makePayment(ws, myMerchant, args.amountToSpend, "hello world");
-    // Wait until the refresh is done
-    await runUntilDone(ws);
-    logger$5.trace("withdrawing test balance for refund");
-    const withdrawAmountTwo = Amounts.parseOrThrow(`${currency}:18`);
-    const spendAmountTwo = Amounts.parseOrThrow(`${currency}:7`);
-    const refundAmount = Amounts.parseOrThrow(`${currency}:6`);
-    const spendAmountThree = Amounts.parseOrThrow(`${currency}:3`);
-    await withdrawTestBalance(ws, Amounts.stringify(withdrawAmountTwo), 
args.bankBaseUrl, args.exchangeBaseUrl);
-    // Wait until the withdraw is done
-    await runUntilDone(ws);
-    const { orderId: refundOrderId } = await makePayment(ws, myMerchant, 
Amounts.stringify(spendAmountTwo), "order that will be refunded");
-    const refundUri = await refund(ws.http, myMerchant, refundOrderId, "test 
refund", Amounts.stringify(refundAmount));
-    logger$5.trace("refund URI", refundUri);
-    await applyRefund(ws, refundUri);
-    logger$5.trace("integration test: applied refund");
-    // Wait until the refund is done
-    await runUntilDone(ws);
-    logger$5.trace("integration test: making payment after refund");
-    await makePayment(ws, myMerchant, Amounts.stringify(spendAmountThree), 
"payment after refund");
-    logger$5.trace("integration test: make payment done");
-    await runUntilDone(ws);
-    logger$5.trace("integration test: all done!");
-}
-async function testPay(ws, args) {
-    logger$5.trace("creating order");
-    const merchant = {
-        authToken: args.merchantAuthToken,
-        baseUrl: args.merchantBaseUrl,
-    };
-    const orderResp = await createOrder(ws.http, merchant, args.amount, 
args.summary, "taler://fulfillment-success/thank+you");
-    logger$5.trace("created new order with order ID", orderResp.orderId);
-    const checkPayResp = await checkPayment(ws.http, merchant, 
orderResp.orderId);
-    const talerPayUri = checkPayResp.taler_pay_uri;
-    if (!talerPayUri) {
-        console.error("fatal: no taler pay URI received from backend");
-        process.exit(1);
-        return;
-    }
-    logger$5.trace("taler pay URI:", talerPayUri);
-    const result = await preparePayForUri(ws, talerPayUri);
-    if (result.status !== PreparePayResultType.PaymentPossible) {
-        throw Error(`unexpected prepare pay status: ${result.status}`);
-    }
-    await confirmPay(ws, result.proposalId, undefined);
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-class AsyncOpMemoMap {
-    constructor() {
-        this.n = 0;
-        this.memoMap = {};
-    }
-    cleanUp(key, n) {
-        const r = this.memoMap[key];
-        if (r && r.n === n) {
-            delete this.memoMap[key];
-        }
-    }
-    memo(key, pg) {
-        const res = this.memoMap[key];
-        if (res) {
-            return res.p;
-        }
-        const n = this.n++;
-        // Wrap the operation in case it immediately throws
-        const p = Promise.resolve().then(() => pg());
-        this.memoMap[key] = {
-            p,
-            n,
-            t: new Date().getTime(),
-        };
-        return p.finally(() => {
-            this.cleanUp(key, n);
-        });
-    }
-    clear() {
-        this.memoMap = {};
-    }
-}
-class AsyncOpMemoSingle {
-    constructor() {
-        this.n = 0;
-    }
-    cleanUp(n) {
-        if (this.memoEntry && this.memoEntry.n === n) {
-            this.memoEntry = undefined;
-        }
-    }
-    memo(pg) {
-        const res = this.memoEntry;
-        if (res) {
-            return res.p;
-        }
-        const n = this.n++;
-        // Wrap the operation in case it immediately throws
-        const p = Promise.resolve().then(() => pg());
-        p.finally(() => {
-            this.cleanUp(n);
-        });
-        this.memoEntry = {
-            p,
-            n,
-            t: new Date().getTime(),
-        };
-        return p;
-    }
-    clear() {
-        this.memoEntry = undefined;
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2015-2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$4 = new Logger("wallet.ts");
-async function getWithdrawalDetailsForAmount(ws, exchangeBaseUrl, amount) {
-    const wi = await getExchangeWithdrawalInfo(ws, exchangeBaseUrl, amount);
-    const paytoUris = wi.exchangeDetails.wireInfo.accounts.map((x) => 
x.payto_uri);
-    if (!paytoUris) {
-        throw Error("exchange is in invalid state");
-    }
-    return {
-        amountRaw: Amounts.stringify(amount),
-        amountEffective: Amounts.stringify(wi.selectedDenoms.totalCoinValue),
-        paytoUris,
-        tosAccepted: wi.termsOfServiceAccepted,
-    };
-}
-/**
- * Execute one operation based on the pending operation info record.
- */
-async function processOnePendingOperation(ws, pending, forceNow = false) {
-    logger$4.trace(`running pending ${JSON.stringify(pending, undefined, 2)}`);
-    switch (pending.type) {
-        case PendingOperationType.ExchangeUpdate:
-            await updateExchangeFromUrl(ws, pending.exchangeBaseUrl, forceNow);
-            break;
-        case PendingOperationType.Refresh:
-            await processRefreshGroup(ws, pending.refreshGroupId, forceNow);
-            break;
-        case PendingOperationType.Reserve:
-            await processReserve(ws, pending.reservePub, forceNow);
-            break;
-        case PendingOperationType.Withdraw:
-            await processWithdrawGroup(ws, pending.withdrawalGroupId, 
forceNow);
-            break;
-        case PendingOperationType.ProposalDownload:
-            await processDownloadProposal(ws, pending.proposalId, forceNow);
-            break;
-        case PendingOperationType.TipPickup:
-            await processTip(ws, pending.tipId, forceNow);
-            break;
-        case PendingOperationType.Pay:
-            await processPurchasePay(ws, pending.proposalId, forceNow);
-            break;
-        case PendingOperationType.RefundQuery:
-            await processPurchaseQueryRefund(ws, pending.proposalId, forceNow);
-            break;
-        case PendingOperationType.Recoup:
-            await processRecoupGroup(ws, pending.recoupGroupId, forceNow);
-            break;
-        case PendingOperationType.ExchangeCheckRefresh:
-            await autoRefresh(ws, pending.exchangeBaseUrl);
-            break;
-        case PendingOperationType.Deposit:
-            await processDepositGroup(ws, pending.depositGroupId);
-            break;
-        default:
-            assertUnreachable();
-    }
-}
-/**
- * Process pending operations.
- */
-async function runPending(ws, forceNow = false) {
-    const pendingOpsResponse = await getPendingOperations(ws);
-    for (const p of pendingOpsResponse.pendingOperations) {
-        if (!forceNow && !isTimestampExpired(p.timestampDue)) {
-            continue;
-        }
-        try {
-            await processOnePendingOperation(ws, p, forceNow);
-        }
-        catch (e) {
-            if (e instanceof OperationFailedAndReportedError) {
-                console.error("Operation failed:", 
JSON.stringify(e.operationError, undefined, 2));
-            }
-            else {
-                console.error(e);
-            }
-        }
-    }
-}
-/**
- * Run the wallet until there are no more pending operations that give
- * liveness left.  The wallet will be in a stopped state when this function
- * returns without resolving to an exception.
- */
-async function runUntilDone(ws, req = {}) {
-    let done = false;
-    const p = new Promise((resolve, reject) => {
-        // Monitor for conditions that means we're done or we
-        // should quit with an error (due to exceeded retries).
-        ws.addNotificationListener((n) => {
-            if (done) {
-                return;
-            }
-            if (n.type === NotificationType.WaitingForRetry &&
-                n.numGivingLiveness == 0) {
-                done = true;
-                logger$4.trace("no liveness-giving operations left");
-                resolve();
-            }
-            const maxRetries = req.maxRetries;
-            if (!maxRetries) {
-                return;
-            }
-            getPendingOperations(ws)
-                .then((pending) => {
-                for (const p of pending.pendingOperations) {
-                    if (p.retryInfo && p.retryInfo.retryCounter > maxRetries) {
-                        console.warn(`stopping, as ${maxRetries} retries are 
exceeded in an operation of type ${p.type}`);
-                        ws.stop();
-                        done = true;
-                        resolve();
-                    }
-                }
-            })
-                .catch((e) => {
-                logger$4.error(e);
-                reject(e);
-            });
-        });
-        // Run this asynchronously
-        runRetryLoop(ws).catch((e) => {
-            logger$4.error("exception in wallet retry loop");
-            reject(e);
-        });
-    });
-    await p;
-}
-/**
- * Process pending operations and wait for scheduled operations in
- * a loop until the wallet is stopped explicitly.
- */
-async function runRetryLoop(ws) {
-    // Make sure we only run one main loop at a time.
-    return ws.memoRunRetryLoop.memo(async () => {
-        try {
-            await runRetryLoopImpl(ws);
-        }
-        catch (e) {
-            console.error("error during retry loop execution", e);
-            throw e;
-        }
-    });
-}
-async function runRetryLoopImpl(ws) {
-    for (let iteration = 0; !ws.stopped; iteration++) {
-        const pending = await getPendingOperations(ws);
-        logger$4.trace(`pending operations: ${j2s(pending)}`);
-        let numGivingLiveness = 0;
-        let numDue = 0;
-        let minDue = { t_ms: "never" };
-        for (const p of pending.pendingOperations) {
-            minDue = timestampMin(minDue, p.timestampDue);
-            if (isTimestampExpired(p.timestampDue)) {
-                numDue++;
-            }
-            if (p.givesLifeness) {
-                numGivingLiveness++;
-            }
-        }
-        // Make sure that we run tasks that don't give lifeness at least
-        // one time.
-        if (iteration !== 0 && numDue === 0) {
-            // We've executed pending, due operations at least one.
-            // Now we don't have any more operations available,
-            // and need to wait.
-            // Wait for at most 5 seconds to the next check.
-            const dt = durationMin(durationFromSpec({
-                seconds: 5,
-            }), getDurationRemaining(minDue));
-            logger$4.trace(`waiting for at most ${dt.d_ms} ms`);
-            const timeout = ws.timerGroup.resolveAfter(dt);
-            ws.notify({
-                type: NotificationType.WaitingForRetry,
-                numGivingLiveness,
-                numPending: pending.pendingOperations.length,
-            });
-            // Wait until either the timeout, or we are notified (via the 
latch)
-            // that more work might be available.
-            await Promise.race([timeout, ws.latch.wait()]);
-        }
-        else {
-            logger$4.trace(`running ${pending.pendingOperations.length} 
pending operations`);
-            for (const p of pending.pendingOperations) {
-                if (!isTimestampExpired(p.timestampDue)) {
-                    continue;
-                }
-                try {
-                    await processOnePendingOperation(ws, p);
-                }
-                catch (e) {
-                    if (e instanceof OperationFailedAndReportedError) {
-                        logger$4.warn("operation processed resulted in 
reported error");
-                    }
-                    else {
-                        logger$4.error("Uncaught exception", e);
-                        ws.notify({
-                            type: NotificationType.InternalError,
-                            message: "uncaught exception",
-                            exception: e,
-                        });
-                    }
-                }
-                ws.notify({
-                    type: NotificationType.PendingOperationProcessed,
-                });
-            }
-        }
-    }
-    logger$4.trace("exiting wallet retry loop");
-}
-/**
- * Create a reserve, but do not flag it as confirmed yet.
- *
- * Adds the corresponding exchange as a trusted exchange if it is neither
- * audited nor trusted already.
- */
-async function acceptManualWithdrawal(ws, exchangeBaseUrl, amount) {
-    try {
-        const resp = await createReserve(ws, {
-            amount,
-            exchange: exchangeBaseUrl,
-        });
-        const exchangePaytoUris = await ws.db
-            .mktx((x) => ({
-            exchanges: x.exchanges,
-            exchangeDetails: x.exchangeDetails,
-            reserves: x.reserves,
-        }))
-            .runReadWrite((tx) => getFundingPaytoUris(tx, resp.reservePub));
-        return {
-            reservePub: resp.reservePub,
-            exchangePaytoUris,
-        };
-    }
-    finally {
-        ws.latch.trigger();
-    }
-}
-async function getExchangeTos(ws, exchangeBaseUrl) {
-    const { exchange, exchangeDetails } = await updateExchangeFromUrl(ws, 
exchangeBaseUrl);
-    const tos = exchangeDetails.termsOfServiceText;
-    const currentEtag = exchangeDetails.termsOfServiceLastEtag;
-    if (!tos || !currentEtag) {
-        throw Error("exchange is in invalid state");
-    }
-    return {
-        acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag,
-        currentEtag,
-        tos,
-    };
-}
-async function getExchanges(ws) {
-    const exchanges = [];
-    await ws.db
-        .mktx((x) => ({
-        exchanges: x.exchanges,
-        exchangeDetails: x.exchangeDetails,
-    }))
-        .runReadOnly(async (tx) => {
-        const exchangeRecords = await tx.exchanges.iter().toArray();
-        for (const r of exchangeRecords) {
-            const dp = r.detailsPointer;
-            if (!dp) {
-                continue;
-            }
-            const { currency, masterPublicKey } = dp;
-            const exchangeDetails = await getExchangeDetails(tx, r.baseUrl);
-            if (!exchangeDetails) {
-                continue;
-            }
-            exchanges.push({
-                exchangeBaseUrl: r.baseUrl,
-                currency,
-                paytoUris: exchangeDetails.wireInfo.accounts.map((x) => 
x.payto_uri),
-            });
-        }
-    });
-    return { exchanges };
-}
-async function acceptWithdrawal(ws, talerWithdrawUri, selectedExchange) {
-    try {
-        return createTalerWithdrawReserve(ws, talerWithdrawUri, 
selectedExchange);
-    }
-    finally {
-        ws.latch.trigger();
-    }
-}
-async function setCoinSuspended(ws, coinPub, suspended) {
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-    }))
-        .runReadWrite(async (tx) => {
-        const c = await tx.coins.get(coinPub);
-        if (!c) {
-            logger$4.warn(`coin ${coinPub} not found, won't suspend`);
-            return;
-        }
-        c.suspended = suspended;
-        await tx.coins.put(c);
-    });
-}
-/**
- * Dump the public information of coins we have in an easy-to-process format.
- */
-async function dumpCoins(ws) {
-    const coinsJson = { coins: [] };
-    await ws.db
-        .mktx((x) => ({
-        coins: x.coins,
-        denominations: x.denominations,
-        withdrawalGroups: x.withdrawalGroups,
-    }))
-        .runReadOnly(async (tx) => {
-        const coins = await tx.coins.iter().toArray();
-        for (const c of coins) {
-            const denom = await tx.denominations.get([
-                c.exchangeBaseUrl,
-                c.denomPubHash,
-            ]);
-            if (!denom) {
-                console.error("no denom session found for coin");
-                continue;
-            }
-            const cs = c.coinSource;
-            let refreshParentCoinPub;
-            if (cs.type == CoinSourceType.Refresh) {
-                refreshParentCoinPub = cs.oldCoinPub;
-            }
-            let withdrawalReservePub;
-            if (cs.type == CoinSourceType.Withdraw) {
-                const ws = await tx.withdrawalGroups.get(cs.withdrawalGroupId);
-                if (!ws) {
-                    console.error("no withdrawal session found for coin");
-                    continue;
-                }
-                withdrawalReservePub = ws.reservePub;
-            }
-            coinsJson.coins.push({
-                coin_pub: c.coinPub,
-                denom_pub: c.denomPub,
-                denom_pub_hash: c.denomPubHash,
-                denom_value: Amounts.stringify(denom.value),
-                exchange_base_url: c.exchangeBaseUrl,
-                refresh_parent_coin_pub: refreshParentCoinPub,
-                remaining_value: Amounts.stringify(c.currentAmount),
-                withdrawal_reserve_pub: withdrawalReservePub,
-                coin_suspended: c.suspended,
-            });
-        }
-    });
-    return coinsJson;
-}
-/**
- * Get an API client from an internal wallet state object.
- */
-async function getClientFromWalletState(ws) {
-    let id = 0;
-    const client = {
-        async call(op, payload) {
-            const res = await handleCoreApiRequest(ws, op, `${id++}`, payload);
-            switch (res.type) {
-                case "error":
-                    throw new OperationFailedError(res.error);
-                case "response":
-                    return res.result;
-            }
-        },
-    };
-    return client;
-}
-/**
- * Implementation of the "wallet-core" API.
- */
-async function dispatchRequestInternal(ws, operation, payload) {
-    if (ws.initCalled && operation !== "initWallet") {
-        throw Error(`wallet must be initialized before running operation 
${operation}`);
-    }
-    switch (operation) {
-        case "initWallet": {
-            ws.initCalled = true;
-            return {};
-        }
-        case "withdrawTestkudos": {
-            await withdrawTestBalance(ws, "TESTKUDOS:10", 
"https://bank.test.taler.net/";, "https://exchange.test.taler.net/";);
-            return {};
-        }
-        case "withdrawTestBalance": {
-            const req = codecForWithdrawTestBalance().decode(payload);
-            await withdrawTestBalance(ws, req.amount, req.bankBaseUrl, 
req.exchangeBaseUrl);
-            return {};
-        }
-        case "runIntegrationTest": {
-            const req = codecForIntegrationTestArgs().decode(payload);
-            await runIntegrationTest(ws, req);
-            return {};
-        }
-        case "testPay": {
-            const req = codecForTestPayArgs().decode(payload);
-            await testPay(ws, req);
-            return {};
-        }
-        case "getTransactions": {
-            const req = codecForTransactionsRequest().decode(payload);
-            return await getTransactions(ws, req);
-        }
-        case "addExchange": {
-            const req = codecForAddExchangeRequest().decode(payload);
-            await updateExchangeFromUrl(ws, req.exchangeBaseUrl, 
req.forceUpdate);
-            return {};
-        }
-        case "listExchanges": {
-            return await getExchanges(ws);
-        }
-        case "getWithdrawalDetailsForUri": {
-            const req = codecForGetWithdrawalDetailsForUri().decode(payload);
-            return await getWithdrawalDetailsForUri(ws, req.talerWithdrawUri);
-        }
-        case "acceptManualWithdrawal": {
-            const req = codecForAcceptManualWithdrawalRequet().decode(payload);
-            const res = await acceptManualWithdrawal(ws, req.exchangeBaseUrl, 
Amounts.parseOrThrow(req.amount));
-            return res;
-        }
-        case "getWithdrawalDetailsForAmount": {
-            const req = 
codecForGetWithdrawalDetailsForAmountRequest().decode(payload);
-            return await getWithdrawalDetailsForAmount(ws, 
req.exchangeBaseUrl, Amounts.parseOrThrow(req.amount));
-        }
-        case "getBalances": {
-            return await getBalances(ws);
-        }
-        case "getPendingOperations": {
-            return await getPendingOperations(ws);
-        }
-        case "setExchangeTosAccepted": {
-            const req = codecForAcceptExchangeTosRequest().decode(payload);
-            await acceptExchangeTermsOfService(ws, req.exchangeBaseUrl, 
req.etag);
-            return {};
-        }
-        case "applyRefund": {
-            const req = codecForApplyRefundRequest().decode(payload);
-            return await applyRefund(ws, req.talerRefundUri);
-        }
-        case "acceptBankIntegratedWithdrawal": {
-            const req = 
codecForAcceptBankIntegratedWithdrawalRequest().decode(payload);
-            return await acceptWithdrawal(ws, req.talerWithdrawUri, 
req.exchangeBaseUrl);
-        }
-        case "getExchangeTos": {
-            const req = codecForGetExchangeTosRequest().decode(payload);
-            return getExchangeTos(ws, req.exchangeBaseUrl);
-        }
-        case "retryPendingNow": {
-            await runPending(ws, true);
-            return {};
-        }
-        // FIXME: Deprecate one of the aliases!
-        case "preparePayForUri":
-        case "preparePay": {
-            const req = codecForPreparePayRequest().decode(payload);
-            return await preparePayForUri(ws, req.talerPayUri);
-        }
-        case "confirmPay": {
-            const req = codecForConfirmPayRequest().decode(payload);
-            return await confirmPay(ws, req.proposalId, req.sessionId);
-        }
-        case "abortFailedPayWithRefund": {
-            const req = codecForAbortPayWithRefundRequest().decode(payload);
-            await abortFailedPayWithRefund(ws, req.proposalId);
-            return {};
-        }
-        case "dumpCoins": {
-            return await dumpCoins(ws);
-        }
-        case "setCoinSuspended": {
-            const req = codecForSetCoinSuspendedRequest().decode(payload);
-            await setCoinSuspended(ws, req.coinPub, req.suspended);
-            return {};
-        }
-        case "forceRefresh": {
-            const req = codecForForceRefreshRequest().decode(payload);
-            const coinPubs = req.coinPubList.map((x) => ({ coinPub: x }));
-            const refreshGroupId = await ws.db
-                .mktx((x) => ({
-                refreshGroups: x.refreshGroups,
-                denominations: x.denominations,
-                coins: x.coins,
-            }))
-                .runReadWrite(async (tx) => {
-                return await createRefreshGroup(ws, tx, coinPubs, 
RefreshReason.Manual);
-            });
-            processRefreshGroup(ws, refreshGroupId.refreshGroupId, 
true).catch((x) => {
-                logger$4.error(x);
-            });
-            return {
-                refreshGroupId,
-            };
-        }
-        case "prepareTip": {
-            const req = codecForPrepareTipRequest().decode(payload);
-            return await prepareTip(ws, req.talerTipUri);
-        }
-        case "acceptTip": {
-            const req = codecForAcceptTipRequest().decode(payload);
-            await acceptTip(ws, req.walletTipId);
-            return {};
-        }
-        case "exportBackupPlain": {
-            return exportBackup(ws);
-        }
-        case "addBackupProvider": {
-            const req = codecForAddBackupProviderRequest().decode(payload);
-            await addBackupProvider(ws, req);
-            return {};
-        }
-        case "runBackupCycle": {
-            await runBackupCycle(ws);
-            return {};
-        }
-        case "exportBackupRecovery": {
-            const resp = await getBackupRecovery(ws);
-            return resp;
-        }
-        case "importBackupRecovery": {
-            const req = codecForAny().decode(payload);
-            await loadBackupRecovery(ws, req);
-            return {};
-        }
-        case "getBackupInfo": {
-            const resp = await getBackupInfo(ws);
-            return resp;
-        }
-        case "createDepositGroup": {
-            const req = codecForCreateDepositGroupRequest().decode(payload);
-            return await createDepositGroup(ws, req);
-        }
-        case "trackDepositGroup": {
-            const req = codecForTrackDepositGroupRequest().decode(payload);
-            return trackDepositGroup(ws, req);
-        }
-        case "deleteTransaction": {
-            const req = codecForDeleteTransactionRequest().decode(payload);
-            await deleteTransaction(ws, req.transactionId);
-            return {};
-        }
-        case "retryTransaction": {
-            const req = codecForRetryTransactionRequest().decode(payload);
-            await retryTransaction(ws, req.transactionId);
-            return {};
-        }
-        case "setWalletDeviceId": {
-            const req = codecForSetWalletDeviceIdRequest().decode(payload);
-            await setWalletDeviceId(ws, req.walletDeviceId);
-            return {};
-        }
-        case "listCurrencies": {
-            return await ws.db
-                .mktx((x) => ({
-                auditorTrust: x.auditorTrust,
-                exchangeTrust: x.exchangeTrust,
-            }))
-                .runReadOnly(async (tx) => {
-                const trustedAuditors = await tx.auditorTrust.iter().toArray();
-                const trustedExchanges = await 
tx.exchangeTrust.iter().toArray();
-                return {
-                    trustedAuditors: trustedAuditors.map((x) => ({
-                        currency: x.currency,
-                        auditorBaseUrl: x.auditorBaseUrl,
-                        auditorPub: x.auditorPub,
-                    })),
-                    trustedExchanges: trustedExchanges.map((x) => ({
-                        currency: x.currency,
-                        exchangeBaseUrl: x.exchangeBaseUrl,
-                        exchangeMasterPub: x.exchangeMasterPub,
-                    })),
-                };
-            });
-        }
-    }
-    throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN, 
"unknown operation", {
-        operation,
-    });
-}
-/**
- * Handle a request to the wallet-core API.
- */
-async function handleCoreApiRequest(ws, operation, id, payload) {
-    try {
-        const result = await dispatchRequestInternal(ws, operation, payload);
-        return {
-            type: "response",
-            operation,
-            id,
-            result,
-        };
-    }
-    catch (e) {
-        if (e instanceof OperationFailedError ||
-            e instanceof OperationFailedAndReportedError) {
-            return {
-                type: "error",
-                operation,
-                id,
-                error: e.operationError,
-            };
-        }
-        else {
-            return {
-                type: "error",
-                operation,
-                id,
-                error: 
makeErrorDetails(TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, `unexpected 
exception: ${e}`, {}),
-            };
-        }
-    }
-}
-/**
- * Public handle to a running wallet.
- */
-class Wallet {
-    constructor(db, http, cryptoWorkerFactory) {
-        this.ws = new InternalWalletStateImpl(db, http, cryptoWorkerFactory);
-    }
-    get client() {
-        return this._client;
-    }
-    static async create(db, http, cryptoWorkerFactory) {
-        const w = new Wallet(db, http, cryptoWorkerFactory);
-        w._client = await getClientFromWalletState(w.ws);
-        return w;
-    }
-    addNotificationListener(f) {
-        return this.ws.addNotificationListener(f);
-    }
-    stop() {
-        this.ws.stop();
-    }
-    runRetryLoop() {
-        return runRetryLoop(this.ws);
-    }
-    runPending(forceNow = false) {
-        return runPending(this.ws, forceNow);
-    }
-    runUntilDone(req = {}) {
-        return runUntilDone(this.ws, req);
-    }
-    handleCoreApiRequest(operation, id, payload) {
-        return handleCoreApiRequest(this.ws, operation, id, payload);
-    }
-}
-/**
- * Internal state of the wallet.
- *
- * This ties together all the operation implementations.
- */
-class InternalWalletStateImpl {
-    constructor(
-    // FIXME: Make this a getter and make
-    // the actual value nullable.
-    // Check if we are in a DB migration / garbage collection
-    // and throw an error in that case.
-    db, http, cryptoWorkerFactory) {
-        this.db = db;
-        this.http = http;
-        this.memoProcessReserve = new AsyncOpMemoMap();
-        this.memoMakePlanchet = new AsyncOpMemoMap();
-        this.memoGetPending = new AsyncOpMemoSingle();
-        this.memoGetBalance = new AsyncOpMemoSingle();
-        this.memoProcessRefresh = new AsyncOpMemoMap();
-        this.memoProcessRecoup = new AsyncOpMemoMap();
-        this.memoProcessDeposit = new AsyncOpMemoMap();
-        this.timerGroup = new TimerGroup();
-        this.latch = new AsyncCondition$1();
-        this.stopped = false;
-        this.memoRunRetryLoop = new AsyncOpMemoSingle();
-        this.listeners = [];
-        this.initCalled = false;
-        this.exchangeOps = {
-            getExchangeDetails,
-            getExchangeTrust,
-            updateExchangeFromUrl,
-        };
-        /**
-         * Promises that are waiting for a particular resource.
-         */
-        this.resourceWaiters = {};
-        /**
-         * Resources that are currently locked.
-         */
-        this.resourceLocks = new Set();
-        this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
-    }
-    notify(n) {
-        logger$4.trace("Notification", n);
-        for (const l of this.listeners) {
-            const nc = JSON.parse(JSON.stringify(n));
-            setTimeout(() => {
-                l(nc);
-            }, 0);
-        }
-    }
-    addNotificationListener(f) {
-        this.listeners.push(f);
-    }
-    /**
-     * Stop ongoing processing.
-     */
-    stop() {
-        this.stopped = true;
-        this.timerGroup.stopCurrentAndFutureTimers();
-        this.cryptoApi.stop();
-    }
-    /**
-     * Run an async function after acquiring a list of locks, identified
-     * by string tokens.
-     */
-    async runSequentialized(tokens, f) {
-        var _a;
-        // Make sure locks are always acquired in the same order
-        tokens = [...tokens].sort();
-        for (const token of tokens) {
-            if (this.resourceLocks.has(token)) {
-                const p = openPromise$1();
-                let waitList = this.resourceWaiters[token];
-                if (!waitList) {
-                    waitList = this.resourceWaiters[token] = [];
-                }
-                waitList.push(p);
-                await p.promise;
-            }
-            this.resourceLocks.add(token);
-        }
-        try {
-            logger$4.trace(`begin exclusive execution on 
${JSON.stringify(tokens)}`);
-            const result = await f();
-            logger$4.trace(`end exclusive execution on 
${JSON.stringify(tokens)}`);
-            return result;
-        }
-        finally {
-            for (const token of tokens) {
-                this.resourceLocks.delete(token);
-                let waiter = ((_a = this.resourceWaiters[token]) !== null && 
_a !== void 0 ? _a : []).shift();
-                if (waiter) {
-                    waiter.resolve();
-                }
-            }
-        }
-    }
-}
-
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-const logger$3 = new Logger("RequestThrottler.ts");
-/**
- * Maximum request per second, per origin.
- */
-const MAX_PER_SECOND = 100;
-/**
- * Maximum request per minute, per origin.
- */
-const MAX_PER_MINUTE = 500;
-/**
- * Maximum request per hour, per origin.
- */
-const MAX_PER_HOUR = 2000;
-/**
- * Throttling state for one origin.
- */
-class OriginState {
-    constructor() {
-        this.tokensSecond = MAX_PER_SECOND;
-        this.tokensMinute = MAX_PER_MINUTE;
-        this.tokensHour = MAX_PER_HOUR;
-        this.lastUpdate = getTimestampNow();
-    }
-    refill() {
-        const now = getTimestampNow();
-        if (timestampCmp(now, this.lastUpdate) < 0) {
-            // Did the system time change?
-            this.lastUpdate = now;
-            return;
-        }
-        const d = timestampDifference(now, this.lastUpdate);
-        if (d.d_ms === "forever") {
-            throw Error("assertion failed");
-        }
-        this.tokensSecond = Math.min(MAX_PER_SECOND, this.tokensSecond + 
d.d_ms / 1000);
-        this.tokensMinute = Math.min(MAX_PER_MINUTE, this.tokensMinute + 
d.d_ms / 1000 / 60);
-        this.tokensHour = Math.min(MAX_PER_HOUR, this.tokensHour + d.d_ms / 
1000 / 60 / 60);
-        this.lastUpdate = now;
-    }
-    /**
-     * Return true if the request for this origin should be throttled.
-     * Otherwise, take a token out of the respective buckets.
-     */
-    applyThrottle() {
-        this.refill();
-        if (this.tokensSecond < 1) {
-            logger$3.warn("request throttled (per second limit exceeded)");
-            return true;
-        }
-        if (this.tokensMinute < 1) {
-            logger$3.warn("request throttled (per minute limit exceeded)");
-            return true;
-        }
-        if (this.tokensHour < 1) {
-            logger$3.warn("request throttled (per hour limit exceeded)");
-            return true;
-        }
-        this.tokensSecond--;
-        this.tokensMinute--;
-        this.tokensHour--;
-        return false;
-    }
-}
-/**
- * Request throttler, used as a "last layer of defense" when some
- * other part of the re-try logic is broken and we're sending too
- * many requests to the same exchange/bank/merchant.
- */
-class RequestThrottler {
-    constructor() {
-        this.perOriginInfo = {};
-    }
-    /**
-     * Get the throttling state for an origin, or
-     * initialize if no state is associated with the
-     * origin yet.
-     */
-    getState(origin) {
-        const s = this.perOriginInfo[origin];
-        if (s) {
-            return s;
-        }
-        const ns = (this.perOriginInfo[origin] = new OriginState());
-        return ns;
-    }
-    /**
-     * Apply throttling to a request.
-     *
-     * @returns whether the request should be throttled.
-     */
-    applyThrottle(requestUrl) {
-        const origin = new URL$1(requestUrl).origin;
-        return this.getState(origin).applyThrottle();
-    }
-    /**
-     * Get the throttle statistics for a particular URL.
-     */
-    getThrottleStats(requestUrl) {
-        const origin = new URL$1(requestUrl).origin;
-        const state = this.getState(origin);
-        return {
-            tokensHour: state.tokensHour,
-            tokensMinute: state.tokensMinute,
-            tokensSecond: state.tokensSecond,
-            maxTokensHour: MAX_PER_HOUR,
-            maxTokensMinute: MAX_PER_MINUTE,
-            maxTokensSecond: MAX_PER_SECOND,
-        };
-    }
-}
-
-var bind = function bind(fn, thisArg) {
-  return function wrap() {
-    var args = new Array(arguments.length);
-    for (var i = 0; i < args.length; i++) {
-      args[i] = arguments[i];
-    }
-    return fn.apply(thisArg, args);
-  };
-};
-
-/*global toString:true*/
-
-// utils is a library of generic helper functions non-specific to axios
-
-var toString = Object.prototype.toString;
-
-/**
- * Determine if a value is an Array
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is an Array, otherwise false
- */
-function isArray(val) {
-  return toString.call(val) === '[object Array]';
-}
-
-/**
- * Determine if a value is undefined
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if the value is undefined, otherwise false
- */
-function isUndefined(val) {
-  return typeof val === 'undefined';
-}
-
-/**
- * Determine if a value is a Buffer
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a Buffer, otherwise false
- */
-function isBuffer(val) {
-  return val !== null && !isUndefined(val) && val.constructor !== null && 
!isUndefined(val.constructor)
-    && typeof val.constructor.isBuffer === 'function' && 
val.constructor.isBuffer(val);
-}
-
-/**
- * Determine if a value is an ArrayBuffer
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is an ArrayBuffer, otherwise false
- */
-function isArrayBuffer(val) {
-  return toString.call(val) === '[object ArrayBuffer]';
-}
-
-/**
- * Determine if a value is a FormData
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is an FormData, otherwise false
- */
-function isFormData(val) {
-  return (typeof FormData !== 'undefined') && (val instanceof FormData);
-}
-
-/**
- * Determine if a value is a view on an ArrayBuffer
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise 
false
- */
-function isArrayBufferView(val) {
-  var result;
-  if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
-    result = ArrayBuffer.isView(val);
-  } else {
-    result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);
-  }
-  return result;
-}
-
-/**
- * Determine if a value is a String
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a String, otherwise false
- */
-function isString(val) {
-  return typeof val === 'string';
-}
-
-/**
- * Determine if a value is a Number
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a Number, otherwise false
- */
-function isNumber(val) {
-  return typeof val === 'number';
-}
-
-/**
- * Determine if a value is an Object
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is an Object, otherwise false
- */
-function isObject(val) {
-  return val !== null && typeof val === 'object';
-}
-
-/**
- * Determine if a value is a plain Object
- *
- * @param {Object} val The value to test
- * @return {boolean} True if value is a plain Object, otherwise false
- */
-function isPlainObject$1(val) {
-  if (toString.call(val) !== '[object Object]') {
-    return false;
-  }
-
-  var prototype = Object.getPrototypeOf(val);
-  return prototype === null || prototype === Object.prototype;
-}
-
-/**
- * Determine if a value is a Date
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a Date, otherwise false
- */
-function isDate(val) {
-  return toString.call(val) === '[object Date]';
-}
-
-/**
- * Determine if a value is a File
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a File, otherwise false
- */
-function isFile(val) {
-  return toString.call(val) === '[object File]';
-}
-
-/**
- * Determine if a value is a Blob
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a Blob, otherwise false
- */
-function isBlob(val) {
-  return toString.call(val) === '[object Blob]';
-}
-
-/**
- * Determine if a value is a Function
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a Function, otherwise false
- */
-function isFunction(val) {
-  return toString.call(val) === '[object Function]';
-}
-
-/**
- * Determine if a value is a Stream
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a Stream, otherwise false
- */
-function isStream(val) {
-  return isObject(val) && isFunction(val.pipe);
-}
-
-/**
- * Determine if a value is a URLSearchParams object
- *
- * @param {Object} val The value to test
- * @returns {boolean} True if value is a URLSearchParams object, otherwise 
false
- */
-function isURLSearchParams(val) {
-  return typeof URLSearchParams !== 'undefined' && val instanceof 
URLSearchParams;
-}
-
-/**
- * Trim excess whitespace off the beginning and end of a string
- *
- * @param {String} str The String to trim
- * @returns {String} The String freed of excess whitespace
- */
-function trim(str) {
-  return str.replace(/^\s*/, '').replace(/\s*$/, '');
-}
-
-/**
- * Determine if we're running in a standard browser environment
- *
- * This allows axios to run in a web worker, and react-native.
- * Both environments support XMLHttpRequest, but not fully standard globals.
- *
- * web workers:
- *  typeof window -> undefined
- *  typeof document -> undefined
- *
- * react-native:
- *  navigator.product -> 'ReactNative'
- * nativescript
- *  navigator.product -> 'NativeScript' or 'NS'
- */
-function isStandardBrowserEnv() {
-  if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' 
||
-                                           navigator.product === 
'NativeScript' ||
-                                           navigator.product === 'NS')) {
-    return false;
-  }
-  return (
-    typeof window !== 'undefined' &&
-    typeof document !== 'undefined'
-  );
-}
-
-/**
- * Iterate over an Array or an Object invoking a function for each item.
- *
- * If `obj` is an Array callback will be called passing
- * the value, index, and complete array for each item.
- *
- * If 'obj' is an Object callback will be called passing
- * the value, key, and complete object for each property.
- *
- * @param {Object|Array} obj The object to iterate
- * @param {Function} fn The callback to invoke for each item
- */
-function forEach(obj, fn) {
-  // Don't bother if no value provided
-  if (obj === null || typeof obj === 'undefined') {
-    return;
-  }
-
-  // Force an array if not already something iterable
-  if (typeof obj !== 'object') {
-    /*eslint no-param-reassign:0*/
-    obj = [obj];
-  }
-
-  if (isArray(obj)) {
-    // Iterate over array values
-    for (var i = 0, l = obj.length; i < l; i++) {
-      fn.call(null, obj[i], i, obj);
-    }
-  } else {
-    // Iterate over object keys
-    for (var key in obj) {
-      if (Object.prototype.hasOwnProperty.call(obj, key)) {
-        fn.call(null, obj[key], key, obj);
-      }
-    }
-  }
-}
-
-/**
- * Accepts varargs expecting each argument to be an object, then
- * immutably merges the properties of each object and returns result.
- *
- * When multiple objects contain the same key the later object in
- * the arguments list will take precedence.
- *
- * Example:
- *
- * ```js
- * var result = merge({foo: 123}, {foo: 456});
- * console.log(result.foo); // outputs 456
- * ```
- *
- * @param {Object} obj1 Object to merge
- * @returns {Object} Result of all merge properties
- */
-function merge(/* obj1, obj2, obj3, ... */) {
-  var result = {};
-  function assignValue(val, key) {
-    if (isPlainObject$1(result[key]) && isPlainObject$1(val)) {
-      result[key] = merge(result[key], val);
-    } else if (isPlainObject$1(val)) {
-      result[key] = merge({}, val);
-    } else if (isArray(val)) {
-      result[key] = val.slice();
-    } else {
-      result[key] = val;
-    }
-  }
-
-  for (var i = 0, l = arguments.length; i < l; i++) {
-    forEach(arguments[i], assignValue);
-  }
-  return result;
-}
-
-/**
- * Extends object a by mutably adding to it the properties of object b.
- *
- * @param {Object} a The object to be extended
- * @param {Object} b The object to copy properties from
- * @param {Object} thisArg The object to bind function to
- * @return {Object} The resulting value of object a
- */
-function extend(a, b, thisArg) {
-  forEach(b, function assignValue(val, key) {
-    if (thisArg && typeof val === 'function') {
-      a[key] = bind(val, thisArg);
-    } else {
-      a[key] = val;
-    }
-  });
-  return a;
-}
-
-/**
- * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
- *
- * @param {string} content with BOM
- * @return {string} content value without BOM
- */
-function stripBOM(content) {
-  if (content.charCodeAt(0) === 0xFEFF) {
-    content = content.slice(1);
-  }
-  return content;
-}
-
-var utils = {
-  isArray: isArray,
-  isArrayBuffer: isArrayBuffer,
-  isBuffer: isBuffer,
-  isFormData: isFormData,
-  isArrayBufferView: isArrayBufferView,
-  isString: isString,
-  isNumber: isNumber,
-  isObject: isObject,
-  isPlainObject: isPlainObject$1,
-  isUndefined: isUndefined,
-  isDate: isDate,
-  isFile: isFile,
-  isBlob: isBlob,
-  isFunction: isFunction,
-  isStream: isStream,
-  isURLSearchParams: isURLSearchParams,
-  isStandardBrowserEnv: isStandardBrowserEnv,
-  forEach: forEach,
-  merge: merge,
-  extend: extend,
-  trim: trim,
-  stripBOM: stripBOM
-};
-
-function encode(val) {
-  return encodeURIComponent(val).
-    replace(/%3A/gi, ':').
-    replace(/%24/g, '$').
-    replace(/%2C/gi, ',').
-    replace(/%20/g, '+').
-    replace(/%5B/gi, '[').
-    replace(/%5D/gi, ']');
-}
-
-/**
- * Build a URL by appending params to the end
- *
- * @param {string} url The base of the url (e.g., http://www.google.com)
- * @param {object} [params] The params to be appended
- * @returns {string} The formatted url
- */
-var buildURL = function buildURL(url, params, paramsSerializer) {
-  /*eslint no-param-reassign:0*/
-  if (!params) {
-    return url;
-  }
-
-  var serializedParams;
-  if (paramsSerializer) {
-    serializedParams = paramsSerializer(params);
-  } else if (utils.isURLSearchParams(params)) {
-    serializedParams = params.toString();
-  } else {
-    var parts = [];
-
-    utils.forEach(params, function serialize(val, key) {
-      if (val === null || typeof val === 'undefined') {
-        return;
-      }
-
-      if (utils.isArray(val)) {
-        key = key + '[]';
-      } else {
-        val = [val];
-      }
-
-      utils.forEach(val, function parseValue(v) {
-        if (utils.isDate(v)) {
-          v = v.toISOString();
-        } else if (utils.isObject(v)) {
-          v = JSON.stringify(v);
-        }
-        parts.push(encode(key) + '=' + encode(v));
-      });
-    });
-
-    serializedParams = parts.join('&');
-  }
-
-  if (serializedParams) {
-    var hashmarkIndex = url.indexOf('#');
-    if (hashmarkIndex !== -1) {
-      url = url.slice(0, hashmarkIndex);
-    }
-
-    url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
-  }
-
-  return url;
-};
-
-function InterceptorManager() {
-  this.handlers = [];
-}
-
-/**
- * Add a new interceptor to the stack
- *
- * @param {Function} fulfilled The function to handle `then` for a `Promise`
- * @param {Function} rejected The function to handle `reject` for a `Promise`
- *
- * @return {Number} An ID used to remove interceptor later
- */
-InterceptorManager.prototype.use = function use(fulfilled, rejected) {
-  this.handlers.push({
-    fulfilled: fulfilled,
-    rejected: rejected
-  });
-  return this.handlers.length - 1;
-};
-
-/**
- * Remove an interceptor from the stack
- *
- * @param {Number} id The ID that was returned by `use`
- */
-InterceptorManager.prototype.eject = function eject(id) {
-  if (this.handlers[id]) {
-    this.handlers[id] = null;
-  }
-};
-
-/**
- * Iterate over all the registered interceptors
- *
- * This method is particularly useful for skipping over any
- * interceptors that may have become `null` calling `eject`.
- *
- * @param {Function} fn The function to call for each interceptor
- */
-InterceptorManager.prototype.forEach = function forEach(fn) {
-  utils.forEach(this.handlers, function forEachHandler(h) {
-    if (h !== null) {
-      fn(h);
-    }
-  });
-};
-
-var InterceptorManager_1 = InterceptorManager;
-
-/**
- * Transform the data for a request or a response
- *
- * @param {Object|String} data The data to be transformed
- * @param {Array} headers The headers for the request or response
- * @param {Array|Function} fns A single function or Array of functions
- * @returns {*} The resulting transformed data
- */
-var transformData = function transformData(data, headers, fns) {
-  /*eslint no-param-reassign:0*/
-  utils.forEach(fns, function transform(fn) {
-    data = fn(data, headers);
-  });
-
-  return data;
-};
-
-var isCancel = function isCancel(value) {
-  return !!(value && value.__CANCEL__);
-};
-
-var normalizeHeaderName = function normalizeHeaderName(headers, 
normalizedName) {
-  utils.forEach(headers, function processHeader(value, name) {
-    if (name !== normalizedName && name.toUpperCase() === 
normalizedName.toUpperCase()) {
-      headers[normalizedName] = value;
-      delete headers[name];
-    }
-  });
-};
-
-/**
- * Update an Error with the specified config, error code, and response.
- *
- * @param {Error} error The error to update.
- * @param {Object} config The config.
- * @param {string} [code] The error code (for example, 'ECONNABORTED').
- * @param {Object} [request] The request.
- * @param {Object} [response] The response.
- * @returns {Error} The error.
- */
-var enhanceError = function enhanceError(error, config, code, request, 
response) {
-  error.config = config;
-  if (code) {
-    error.code = code;
-  }
-
-  error.request = request;
-  error.response = response;
-  error.isAxiosError = true;
-
-  error.toJSON = function toJSON() {
-    return {
-      // Standard
-      message: this.message,
-      name: this.name,
-      // Microsoft
-      description: this.description,
-      number: this.number,
-      // Mozilla
-      fileName: this.fileName,
-      lineNumber: this.lineNumber,
-      columnNumber: this.columnNumber,
-      stack: this.stack,
-      // Axios
-      config: this.config,
-      code: this.code
-    };
-  };
-  return error;
-};
-
-/**
- * Create an Error with the specified message, config, error code, request and 
response.
- *
- * @param {string} message The error message.
- * @param {Object} config The config.
- * @param {string} [code] The error code (for example, 'ECONNABORTED').
- * @param {Object} [request] The request.
- * @param {Object} [response] The response.
- * @returns {Error} The created error.
- */
-var createError = function createError(message, config, code, request, 
response) {
-  var error = new Error(message);
-  return enhanceError(error, config, code, request, response);
-};
-
-/**
- * Resolve or reject a Promise based on response status.
- *
- * @param {Function} resolve A function that resolves the promise.
- * @param {Function} reject A function that rejects the promise.
- * @param {object} response The response.
- */
-var settle = function settle(resolve, reject, response) {
-  var validateStatus = response.config.validateStatus;
-  if (!response.status || !validateStatus || validateStatus(response.status)) {
-    resolve(response);
-  } else {
-    reject(createError(
-      'Request failed with status code ' + response.status,
-      response.config,
-      null,
-      response.request,
-      response
-    ));
-  }
-};
-
-var cookies = (
-  utils.isStandardBrowserEnv() ?
-
-  // Standard browser envs support document.cookie
-    (function standardBrowserEnv() {
-      return {
-        write: function write(name, value, expires, path, domain, secure) {
-          var cookie = [];
-          cookie.push(name + '=' + encodeURIComponent(value));
-
-          if (utils.isNumber(expires)) {
-            cookie.push('expires=' + new Date(expires).toGMTString());
-          }
-
-          if (utils.isString(path)) {
-            cookie.push('path=' + path);
-          }
-
-          if (utils.isString(domain)) {
-            cookie.push('domain=' + domain);
-          }
-
-          if (secure === true) {
-            cookie.push('secure');
-          }
-
-          document.cookie = cookie.join('; ');
-        },
-
-        read: function read(name) {
-          var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + 
')=([^;]*)'));
-          return (match ? decodeURIComponent(match[3]) : null);
-        },
-
-        remove: function remove(name) {
-          this.write(name, '', Date.now() - 86400000);
-        }
-      };
-    })() :
-
-  // Non standard browser env (web workers, react-native) lack needed support.
-    (function nonStandardBrowserEnv() {
-      return {
-        write: function write() {},
-        read: function read() { return null; },
-        remove: function remove() {}
-      };
-    })()
-);
-
-/**
- * Determines whether the specified URL is absolute
- *
- * @param {string} url The URL to test
- * @returns {boolean} True if the specified URL is absolute, otherwise false
- */
-var isAbsoluteURL = function isAbsoluteURL(url) {
-  // A URL is considered absolute if it begins with "<scheme>://" or "//" 
(protocol-relative URL).
-  // RFC 3986 defines scheme name as a sequence of characters beginning with a 
letter and followed
-  // by any combination of letters, digits, plus, period, or hyphen.
-  return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
-};
-
-/**
- * Creates a new URL by combining the specified URLs
- *
- * @param {string} baseURL The base URL
- * @param {string} relativeURL The relative URL
- * @returns {string} The combined URL
- */
-var combineURLs = function combineURLs(baseURL, relativeURL) {
-  return relativeURL
-    ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
-    : baseURL;
-};
-
-/**
- * Creates a new URL by combining the baseURL with the requestedURL,
- * only when the requestedURL is not already an absolute URL.
- * If the requestURL is absolute, this function returns the requestedURL 
untouched.
- *
- * @param {string} baseURL The base URL
- * @param {string} requestedURL Absolute or relative URL to combine
- * @returns {string} The combined full path
- */
-var buildFullPath = function buildFullPath(baseURL, requestedURL) {
-  if (baseURL && !isAbsoluteURL(requestedURL)) {
-    return combineURLs(baseURL, requestedURL);
-  }
-  return requestedURL;
-};
-
-// Headers whose duplicates are ignored by node
-// c.f. https://nodejs.org/api/http.html#http_message_headers
-var ignoreDuplicateOf = [
-  'age', 'authorization', 'content-length', 'content-type', 'etag',
-  'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',
-  'last-modified', 'location', 'max-forwards', 'proxy-authorization',
-  'referer', 'retry-after', 'user-agent'
-];
-
-/**
- * Parse headers into an object
- *
- * ```
- * Date: Wed, 27 Aug 2014 08:58:49 GMT
- * Content-Type: application/json
- * Connection: keep-alive
- * Transfer-Encoding: chunked
- * ```
- *
- * @param {String} headers Headers needing to be parsed
- * @returns {Object} Headers parsed into an object
- */
-var parseHeaders = function parseHeaders(headers) {
-  var parsed = {};
-  var key;
-  var val;
-  var i;
-
-  if (!headers) { return parsed; }
-
-  utils.forEach(headers.split('\n'), function parser(line) {
-    i = line.indexOf(':');
-    key = utils.trim(line.substr(0, i)).toLowerCase();
-    val = utils.trim(line.substr(i + 1));
-
-    if (key) {
-      if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {
-        return;
-      }
-      if (key === 'set-cookie') {
-        parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);
-      } else {
-        parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
-      }
-    }
-  });
-
-  return parsed;
-};
-
-var isURLSameOrigin = (
-  utils.isStandardBrowserEnv() ?
-
-  // Standard browser envs have full support of the APIs needed to test
-  // whether the request URL is of the same origin as current location.
-    (function standardBrowserEnv() {
-      var msie = /(msie|trident)/i.test(navigator.userAgent);
-      var urlParsingNode = document.createElement('a');
-      var originURL;
-
-      /**
-    * Parse a URL to discover it's components
-    *
-    * @param {String} url The URL to be parsed
-    * @returns {Object}
-    */
-      function resolveURL(url) {
-        var href = url;
-
-        if (msie) {
-        // IE needs attribute set twice to normalize properties
-          urlParsingNode.setAttribute('href', href);
-          href = urlParsingNode.href;
-        }
-
-        urlParsingNode.setAttribute('href', href);
-
-        // urlParsingNode provides the UrlUtils interface - 
http://url.spec.whatwg.org/#urlutils
-        return {
-          href: urlParsingNode.href,
-          protocol: urlParsingNode.protocol ? 
urlParsingNode.protocol.replace(/:$/, '') : '',
-          host: urlParsingNode.host,
-          search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, 
'') : '',
-          hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : 
'',
-          hostname: urlParsingNode.hostname,
-          port: urlParsingNode.port,
-          pathname: (urlParsingNode.pathname.charAt(0) === '/') ?
-            urlParsingNode.pathname :
-            '/' + urlParsingNode.pathname
-        };
-      }
-
-      originURL = resolveURL(window.location.href);
-
-      /**
-    * Determine if a URL shares the same origin as the current location
-    *
-    * @param {String} requestURL The URL to test
-    * @returns {boolean} True if URL shares the same origin, otherwise false
-    */
-      return function isURLSameOrigin(requestURL) {
-        var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : 
requestURL;
-        return (parsed.protocol === originURL.protocol &&
-            parsed.host === originURL.host);
-      };
-    })() :
-
-  // Non standard browser envs (web workers, react-native) lack needed support.
-    (function nonStandardBrowserEnv() {
-      return function isURLSameOrigin() {
-        return true;
-      };
-    })()
-);
-
-var xhr = function xhrAdapter(config) {
-  return new Promise(function dispatchXhrRequest(resolve, reject) {
-    var requestData = config.data;
-    var requestHeaders = config.headers;
-
-    if (utils.isFormData(requestData)) {
-      delete requestHeaders['Content-Type']; // Let the browser set it
-    }
-
-    var request = new XMLHttpRequest();
-
-    // HTTP basic authentication
-    if (config.auth) {
-      var username = config.auth.username || '';
-      var password = config.auth.password ? 
unescape(encodeURIComponent(config.auth.password)) : '';
-      requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + 
password);
-    }
-
-    var fullPath = buildFullPath(config.baseURL, config.url);
-    request.open(config.method.toUpperCase(), buildURL(fullPath, 
config.params, config.paramsSerializer), true);
-
-    // Set the request timeout in MS
-    request.timeout = config.timeout;
-
-    // Listen for ready state
-    request.onreadystatechange = function handleLoad() {
-      if (!request || request.readyState !== 4) {
-        return;
-      }
-
-      // The request errored out and we didn't get a response, this will be
-      // handled by onerror instead
-      // With one exception: request that using file: protocol, most browsers
-      // will return status as 0 even though it's a successful request
-      if (request.status === 0 && !(request.responseURL && 
request.responseURL.indexOf('file:') === 0)) {
-        return;
-      }
-
-      // Prepare the response
-      var responseHeaders = 'getAllResponseHeaders' in request ? 
parseHeaders(request.getAllResponseHeaders()) : null;
-      var responseData = !config.responseType || config.responseType === 
'text' ? request.responseText : request.response;
-      var response = {
-        data: responseData,
-        status: request.status,
-        statusText: request.statusText,
-        headers: responseHeaders,
-        config: config,
-        request: request
-      };
-
-      settle(resolve, reject, response);
-
-      // Clean up request
-      request = null;
-    };
-
-    // Handle browser request cancellation (as opposed to a manual 
cancellation)
-    request.onabort = function handleAbort() {
-      if (!request) {
-        return;
-      }
-
-      reject(createError('Request aborted', config, 'ECONNABORTED', request));
-
-      // Clean up request
-      request = null;
-    };
-
-    // Handle low level network errors
-    request.onerror = function handleError() {
-      // Real errors are hidden from us by the browser
-      // onerror should only fire if it's a network error
-      reject(createError('Network Error', config, null, request));
-
-      // Clean up request
-      request = null;
-    };
-
-    // Handle timeout
-    request.ontimeout = function handleTimeout() {
-      var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';
-      if (config.timeoutErrorMessage) {
-        timeoutErrorMessage = config.timeoutErrorMessage;
-      }
-      reject(createError(timeoutErrorMessage, config, 'ECONNABORTED',
-        request));
-
-      // Clean up request
-      request = null;
-    };
-
-    // Add xsrf header
-    // This is only done if running in a standard browser environment.
-    // Specifically not if we're in a web worker, or react-native.
-    if (utils.isStandardBrowserEnv()) {
-      // Add xsrf header
-      var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && 
config.xsrfCookieName ?
-        cookies.read(config.xsrfCookieName) :
-        undefined;
-
-      if (xsrfValue) {
-        requestHeaders[config.xsrfHeaderName] = xsrfValue;
-      }
-    }
-
-    // Add headers to the request
-    if ('setRequestHeader' in request) {
-      utils.forEach(requestHeaders, function setRequestHeader(val, key) {
-        if (typeof requestData === 'undefined' && key.toLowerCase() === 
'content-type') {
-          // Remove Content-Type if data is undefined
-          delete requestHeaders[key];
-        } else {
-          // Otherwise add header to the request
-          request.setRequestHeader(key, val);
-        }
-      });
-    }
-
-    // Add withCredentials to request if needed
-    if (!utils.isUndefined(config.withCredentials)) {
-      request.withCredentials = !!config.withCredentials;
-    }
-
-    // Add responseType to request if needed
-    if (config.responseType) {
-      try {
-        request.responseType = config.responseType;
-      } catch (e) {
-        // Expected DOMException thrown by browsers not compatible 
XMLHttpRequest Level 2.
-        // But, this can be suppressed for 'json' type as it can be parsed by 
default 'transformResponse' function.
-        if (config.responseType !== 'json') {
-          throw e;
-        }
-      }
-    }
-
-    // Handle progress if needed
-    if (typeof config.onDownloadProgress === 'function') {
-      request.addEventListener('progress', config.onDownloadProgress);
-    }
-
-    // Not all browsers support upload events
-    if (typeof config.onUploadProgress === 'function' && request.upload) {
-      request.upload.addEventListener('progress', config.onUploadProgress);
-    }
-
-    if (config.cancelToken) {
-      // Handle cancellation
-      config.cancelToken.promise.then(function onCanceled(cancel) {
-        if (!request) {
-          return;
-        }
-
-        request.abort();
-        reject(cancel);
-        // Clean up request
-        request = null;
-      });
-    }
-
-    if (!requestData) {
-      requestData = null;
-    }
-
-    // Send the request
-    request.send(requestData);
-  });
-};
-
-/**
- * Helpers.
- */
-var s = 1000;
-var m = s * 60;
-var h = m * 60;
-var d = h * 24;
-var w = d * 7;
-var y = d * 365.25;
-
-/**
- * Parse or format the given `val`.
- *
- * Options:
- *
- *  - `long` verbose formatting [false]
- *
- * @param {String|Number} val
- * @param {Object} [options]
- * @throws {Error} throw an error if val is not a non-empty string or a number
- * @return {String|Number}
- * @api public
- */
-
-var ms = function(val, options) {
-  options = options || {};
-  var type = typeof val;
-  if (type === 'string' && val.length > 0) {
-    return parse(val);
-  } else if (type === 'number' && isFinite(val)) {
-    return options.long ? fmtLong(val) : fmtShort(val);
-  }
-  throw new Error(
-    'val is not a non-empty string or a valid number. val=' +
-      JSON.stringify(val)
-  );
-};
-
-/**
- * Parse the given `str` and return milliseconds.
- *
- * @param {String} str
- * @return {Number}
- * @api private
- */
-
-function parse(str) {
-  str = String(str);
-  if (str.length > 100) {
-    return;
-  }
-  var match = /^(-?(?:\d+)?\.?\d+) 
*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
-    str
-  );
-  if (!match) {
-    return;
-  }
-  var n = parseFloat(match[1]);
-  var type = (match[2] || 'ms').toLowerCase();
-  switch (type) {
-    case 'years':
-    case 'year':
-    case 'yrs':
-    case 'yr':
-    case 'y':
-      return n * y;
-    case 'weeks':
-    case 'week':
-    case 'w':
-      return n * w;
-    case 'days':
-    case 'day':
-    case 'd':
-      return n * d;
-    case 'hours':
-    case 'hour':
-    case 'hrs':
-    case 'hr':
-    case 'h':
-      return n * h;
-    case 'minutes':
-    case 'minute':
-    case 'mins':
-    case 'min':
-    case 'm':
-      return n * m;
-    case 'seconds':
-    case 'second':
-    case 'secs':
-    case 'sec':
-    case 's':
-      return n * s;
-    case 'milliseconds':
-    case 'millisecond':
-    case 'msecs':
-    case 'msec':
-    case 'ms':
-      return n;
-    default:
-      return undefined;
-  }
-}
-
-/**
- * Short format for `ms`.
- *
- * @param {Number} ms
- * @return {String}
- * @api private
- */
-
-function fmtShort(ms) {
-  var msAbs = Math.abs(ms);
-  if (msAbs >= d) {
-    return Math.round(ms / d) + 'd';
-  }
-  if (msAbs >= h) {
-    return Math.round(ms / h) + 'h';
-  }
-  if (msAbs >= m) {
-    return Math.round(ms / m) + 'm';
-  }
-  if (msAbs >= s) {
-    return Math.round(ms / s) + 's';
-  }
-  return ms + 'ms';
-}
-
-/**
- * Long format for `ms`.
- *
- * @param {Number} ms
- * @return {String}
- * @api private
- */
-
-function fmtLong(ms) {
-  var msAbs = Math.abs(ms);
-  if (msAbs >= d) {
-    return plural(ms, msAbs, d, 'day');
-  }
-  if (msAbs >= h) {
-    return plural(ms, msAbs, h, 'hour');
-  }
-  if (msAbs >= m) {
-    return plural(ms, msAbs, m, 'minute');
-  }
-  if (msAbs >= s) {
-    return plural(ms, msAbs, s, 'second');
-  }
-  return ms + ' ms';
-}
-
-/**
- * Pluralization helper.
- */
-
-function plural(ms, msAbs, n, name) {
-  var isPlural = msAbs >= n * 1.5;
-  return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');
-}
-
-/**
- * This is the common logic for both the Node.js and web browser
- * implementations of `debug()`.
- */
-
-function setup(env) {
-       createDebug.debug = createDebug;
-       createDebug.default = createDebug;
-       createDebug.coerce = coerce;
-       createDebug.disable = disable;
-       createDebug.enable = enable;
-       createDebug.enabled = enabled;
-       createDebug.humanize = ms;
-       createDebug.destroy = destroy;
-
-       Object.keys(env).forEach(key => {
-               createDebug[key] = env[key];
-       });
-
-       /**
-       * The currently active debug mode names, and names to skip.
-       */
-
-       createDebug.names = [];
-       createDebug.skips = [];
-
-       /**
-       * Map of special "%n" handling functions, for the debug "format" 
argument.
-       *
-       * Valid key names are a single, lower or upper-case letter, i.e. "n" 
and "N".
-       */
-       createDebug.formatters = {};
-
-       /**
-       * Selects a color for a debug namespace
-       * @param {String} namespace The namespace string for the for the debug 
instance to be colored
-       * @return {Number|String} An ANSI color code for the given namespace
-       * @api private
-       */
-       function selectColor(namespace) {
-               let hash = 0;
-
-               for (let i = 0; i < namespace.length; i++) {
-                       hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
-                       hash |= 0; // Convert to 32bit integer
-               }
-
-               return createDebug.colors[Math.abs(hash) % 
createDebug.colors.length];
-       }
-       createDebug.selectColor = selectColor;
-
-       /**
-       * Create a debugger with the given `namespace`.
-       *
-       * @param {String} namespace
-       * @return {Function}
-       * @api public
-       */
-       function createDebug(namespace) {
-               let prevTime;
-               let enableOverride = null;
-
-               function debug(...args) {
-                       // Disabled?
-                       if (!debug.enabled) {
-                               return;
-                       }
-
-                       const self = debug;
-
-                       // Set `diff` timestamp
-                       const curr = Number(new Date());
-                       const ms = curr - (prevTime || curr);
-                       self.diff = ms;
-                       self.prev = prevTime;
-                       self.curr = curr;
-                       prevTime = curr;
-
-                       args[0] = createDebug.coerce(args[0]);
-
-                       if (typeof args[0] !== 'string') {
-                               // Anything else let's inspect with %O
-                               args.unshift('%O');
-                       }
-
-                       // Apply any `formatters` transformations
-                       let index = 0;
-                       args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, 
format) => {
-                               // If we encounter an escaped % then don't 
increase the array index
-                               if (match === '%%') {
-                                       return '%';
-                               }
-                               index++;
-                               const formatter = 
createDebug.formatters[format];
-                               if (typeof formatter === 'function') {
-                                       const val = args[index];
-                                       match = formatter.call(self, val);
-
-                                       // Now we need to remove `args[index]` 
since it's inlined in the `format`
-                                       args.splice(index, 1);
-                                       index--;
-                               }
-                               return match;
-                       });
-
-                       // Apply env-specific formatting (colors, etc.)
-                       createDebug.formatArgs.call(self, args);
-
-                       const logFn = self.log || createDebug.log;
-                       logFn.apply(self, args);
-               }
-
-               debug.namespace = namespace;
-               debug.useColors = createDebug.useColors();
-               debug.color = createDebug.selectColor(namespace);
-               debug.extend = extend;
-               debug.destroy = createDebug.destroy; // XXX Temporary. Will be 
removed in the next major release.
-
-               Object.defineProperty(debug, 'enabled', {
-                       enumerable: true,
-                       configurable: false,
-                       get: () => enableOverride === null ? 
createDebug.enabled(namespace) : enableOverride,
-                       set: v => {
-                               enableOverride = v;
-                       }
-               });
-
-               // Env-specific initialization logic for debug instances
-               if (typeof createDebug.init === 'function') {
-                       createDebug.init(debug);
-               }
-
-               return debug;
-       }
-
-       function extend(namespace, delimiter) {
-               const newDebug = createDebug(this.namespace + (typeof delimiter 
=== 'undefined' ? ':' : delimiter) + namespace);
-               newDebug.log = this.log;
-               return newDebug;
-       }
-
-       /**
-       * Enables a debug mode by namespaces. This can include modes
-       * separated by a colon and wildcards.
-       *
-       * @param {String} namespaces
-       * @api public
-       */
-       function enable(namespaces) {
-               createDebug.save(namespaces);
-
-               createDebug.names = [];
-               createDebug.skips = [];
-
-               let i;
-               const split = (typeof namespaces === 'string' ? namespaces : 
'').split(/[\s,]+/);
-               const len = split.length;
-
-               for (i = 0; i < len; i++) {
-                       if (!split[i]) {
-                               // ignore empty strings
-                               continue;
-                       }
-
-                       namespaces = split[i].replace(/\*/g, '.*?');
-
-                       if (namespaces[0] === '-') {
-                               createDebug.skips.push(new RegExp('^' + 
namespaces.substr(1) + '$'));
-                       } else {
-                               createDebug.names.push(new RegExp('^' + 
namespaces + '$'));
-                       }
-               }
-       }
-
-       /**
-       * Disable debug output.
-       *
-       * @return {String} namespaces
-       * @api public
-       */
-       function disable() {
-               const namespaces = [
-                       ...createDebug.names.map(toNamespace),
-                       ...createDebug.skips.map(toNamespace).map(namespace => 
'-' + namespace)
-               ].join(',');
-               createDebug.enable('');
-               return namespaces;
-       }
-
-       /**
-       * Returns true if the given mode name is enabled, false otherwise.
-       *
-       * @param {String} name
-       * @return {Boolean}
-       * @api public
-       */
-       function enabled(name) {
-               if (name[name.length - 1] === '*') {
-                       return true;
-               }
-
-               let i;
-               let len;
-
-               for (i = 0, len = createDebug.skips.length; i < len; i++) {
-                       if (createDebug.skips[i].test(name)) {
-                               return false;
-                       }
-               }
-
-               for (i = 0, len = createDebug.names.length; i < len; i++) {
-                       if (createDebug.names[i].test(name)) {
-                               return true;
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-       * Convert regexp to namespace
-       *
-       * @param {RegExp} regxep
-       * @return {String} namespace
-       * @api private
-       */
-       function toNamespace(regexp) {
-               return regexp.toString()
-                       .substring(2, regexp.toString().length - 2)
-                       .replace(/\.\*\?$/, '*');
-       }
-
-       /**
-       * Coerce `val`.
-       *
-       * @param {Mixed} val
-       * @return {Mixed}
-       * @api private
-       */
-       function coerce(val) {
-               if (val instanceof Error) {
-                       return val.stack || val.message;
-               }
-               return val;
-       }
-
-       /**
-       * XXX DO NOT USE. This is a temporary stub function.
-       * XXX It WILL be removed in the next major release.
-       */
-       function destroy() {
-               console.warn('Instance method `debug.destroy()` is deprecated 
and no longer does anything. It will be removed in the next major version of 
`debug`.');
-       }
-
-       createDebug.enable(createDebug.load());
-
-       return createDebug;
-}
-
-var common = setup;
-
-/* eslint-env browser */
-
-var browser$1 = createCommonjsModule(function (module, exports) {
-/**
- * This is the web browser implementation of `debug()`.
- */
-
-exports.formatArgs = formatArgs;
-exports.save = save;
-exports.load = load;
-exports.useColors = useColors;
-exports.storage = localstorage();
-exports.destroy = (() => {
-       let warned = false;
-
-       return () => {
-               if (!warned) {
-                       warned = true;
-                       console.warn('Instance method `debug.destroy()` is 
deprecated and no longer does anything. It will be removed in the next major 
version of `debug`.');
-               }
-       };
-})();
-
-/**
- * Colors.
- */
-
-exports.colors = [
-       '#0000CC',
-       '#0000FF',
-       '#0033CC',
-       '#0033FF',
-       '#0066CC',
-       '#0066FF',
-       '#0099CC',
-       '#0099FF',
-       '#00CC00',
-       '#00CC33',
-       '#00CC66',
-       '#00CC99',
-       '#00CCCC',
-       '#00CCFF',
-       '#3300CC',
-       '#3300FF',
-       '#3333CC',
-       '#3333FF',
-       '#3366CC',
-       '#3366FF',
-       '#3399CC',
-       '#3399FF',
-       '#33CC00',
-       '#33CC33',
-       '#33CC66',
-       '#33CC99',
-       '#33CCCC',
-       '#33CCFF',
-       '#6600CC',
-       '#6600FF',
-       '#6633CC',
-       '#6633FF',
-       '#66CC00',
-       '#66CC33',
-       '#9900CC',
-       '#9900FF',
-       '#9933CC',
-       '#9933FF',
-       '#99CC00',
-       '#99CC33',
-       '#CC0000',
-       '#CC0033',
-       '#CC0066',
-       '#CC0099',
-       '#CC00CC',
-       '#CC00FF',
-       '#CC3300',
-       '#CC3333',
-       '#CC3366',
-       '#CC3399',
-       '#CC33CC',
-       '#CC33FF',
-       '#CC6600',
-       '#CC6633',
-       '#CC9900',
-       '#CC9933',
-       '#CCCC00',
-       '#CCCC33',
-       '#FF0000',
-       '#FF0033',
-       '#FF0066',
-       '#FF0099',
-       '#FF00CC',
-       '#FF00FF',
-       '#FF3300',
-       '#FF3333',
-       '#FF3366',
-       '#FF3399',
-       '#FF33CC',
-       '#FF33FF',
-       '#FF6600',
-       '#FF6633',
-       '#FF9900',
-       '#FF9933',
-       '#FFCC00',
-       '#FFCC33'
-];
-
-/**
- * Currently only WebKit-based Web Inspectors, Firefox >= v31,
- * and the Firebug extension (any Firefox version) are known
- * to support "%c" CSS customizations.
- *
- * TODO: add a `localStorage` variable to explicitly enable/disable colors
- */
-
-// eslint-disable-next-line complexity
-function useColors() {
-       // NB: In an Electron preload script, document will be defined but not 
fully
-       // initialized. Since we know we're in Chrome, we'll just detect this 
case
-       // explicitly
-       if (typeof window !== 'undefined' && window.process && 
(window.process.type === 'renderer' || window.process.__nwjs)) {
-               return true;
-       }
-
-       // Internet Explorer and Edge do not support colors.
-       if (typeof navigator !== 'undefined' && navigator.userAgent && 
navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
-               return false;
-       }
-
-       // Is webkit? http://stackoverflow.com/a/16459606/376773
-       // document is undefined in react-native: 
https://github.com/facebook/react-native/pull/1632
-       return (typeof document !== 'undefined' && document.documentElement && 
document.documentElement.style && 
document.documentElement.style.WebkitAppearance) ||
-               // Is firebug? http://stackoverflow.com/a/398120/376773
-               (typeof window !== 'undefined' && window.console && 
(window.console.firebug || (window.console.exception && window.console.table))) 
||
-               // Is firefox >= v31?
-               // 
https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
-               (typeof navigator !== 'undefined' && navigator.userAgent && 
navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && 
parseInt(RegExp.$1, 10) >= 31) ||
-               // Double check webkit in userAgent just in case we are in a 
worker
-               (typeof navigator !== 'undefined' && navigator.userAgent && 
navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
-}
-
-/**
- * Colorize log arguments if enabled.
- *
- * @api public
- */
-
-function formatArgs(args) {
-       args[0] = (this.useColors ? '%c' : '') +
-               this.namespace +
-               (this.useColors ? ' %c' : ' ') +
-               args[0] +
-               (this.useColors ? '%c ' : ' ') +
-               '+' + module.exports.humanize(this.diff);
-
-       if (!this.useColors) {
-               return;
-       }
-
-       const c = 'color: ' + this.color;
-       args.splice(1, 0, c, 'color: inherit');
-
-       // The final "%c" is somewhat tricky, because there could be other
-       // arguments passed either before or after the %c, so we need to
-       // figure out the correct index to insert the CSS into
-       let index = 0;
-       let lastC = 0;
-       args[0].replace(/%[a-zA-Z%]/g, match => {
-               if (match === '%%') {
-                       return;
-               }
-               index++;
-               if (match === '%c') {
-                       // We only are interested in the *last* %c
-                       // (the user may have provided their own)
-                       lastC = index;
-               }
-       });
-
-       args.splice(lastC, 0, c);
-}
-
-/**
- * Invokes `console.debug()` when available.
- * No-op when `console.debug` is not a "function".
- * If `console.debug` is not available, falls back
- * to `console.log`.
- *
- * @api public
- */
-exports.log = console.debug || console.log || (() => {});
-
-/**
- * Save `namespaces`.
- *
- * @param {String} namespaces
- * @api private
- */
-function save(namespaces) {
-       try {
-               if (namespaces) {
-                       exports.storage.setItem('debug', namespaces);
-               } else {
-                       exports.storage.removeItem('debug');
-               }
-       } catch (error) {
-               // Swallow
-               // XXX (@Qix-) should we be logging these?
-       }
-}
-
-/**
- * Load `namespaces`.
- *
- * @return {String} returns the previously persisted debug modes
- * @api private
- */
-function load() {
-       let r;
-       try {
-               r = exports.storage.getItem('debug');
-       } catch (error) {
-               // Swallow
-               // XXX (@Qix-) should we be logging these?
-       }
-
-       // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
-       if (!r && typeof process !== 'undefined' && 'env' in process) {
-               r = process.env.DEBUG;
-       }
-
-       return r;
-}
-
-/**
- * Localstorage attempts to return the localstorage.
- *
- * This is necessary because safari throws
- * when a user disables cookies/localstorage
- * and you attempt to access it.
- *
- * @return {LocalStorage}
- * @api private
- */
-
-function localstorage() {
-       try {
-               // TVMLKit (Apple TV JS Runtime) does not have a window object, 
just localStorage in the global context
-               // The Browser also has localStorage in the global context.
-               return localStorage;
-       } catch (error) {
-               // Swallow
-               // XXX (@Qix-) should we be logging these?
-       }
-}
-
-module.exports = common(exports);
-
-const {formatters} = module.exports;
-
-/**
- * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
- */
-
-formatters.j = function (v) {
-       try {
-               return JSON.stringify(v);
-       } catch (error) {
-               return '[UnexpectedJSONParseError]: ' + error.message;
-       }
-};
-});
-
-var hasFlag = (flag, argv) => {
-       argv = argv || process.argv;
-       const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : 
'--');
-       const pos = argv.indexOf(prefix + flag);
-       const terminatorPos = argv.indexOf('--');
-       return pos !== -1 && (terminatorPos === -1 ? true : pos < 
terminatorPos);
-};
-
-const {env} = process;
-
-let forceColor;
-if (hasFlag('no-color') ||
-       hasFlag('no-colors') ||
-       hasFlag('color=false') ||
-       hasFlag('color=never')) {
-       forceColor = 0;
-} else if (hasFlag('color') ||
-       hasFlag('colors') ||
-       hasFlag('color=true') ||
-       hasFlag('color=always')) {
-       forceColor = 1;
-}
-if ('FORCE_COLOR' in env) {
-       if (env.FORCE_COLOR === true || env.FORCE_COLOR === 'true') {
-               forceColor = 1;
-       } else if (env.FORCE_COLOR === false || env.FORCE_COLOR === 'false') {
-               forceColor = 0;
-       } else {
-               forceColor = env.FORCE_COLOR.length === 0 ? 1 : 
Math.min(parseInt(env.FORCE_COLOR, 10), 3);
-       }
-}
-
-function translateLevel(level) {
-       if (level === 0) {
-               return false;
-       }
-
-       return {
-               level,
-               hasBasic: true,
-               has256: level >= 2,
-               has16m: level >= 3
-       };
-}
-
-function supportsColor(stream) {
-       if (forceColor === 0) {
-               return 0;
-       }
-
-       if (hasFlag('color=16m') ||
-               hasFlag('color=full') ||
-               hasFlag('color=truecolor')) {
-               return 3;
-       }
-
-       if (hasFlag('color=256')) {
-               return 2;
-       }
-
-       if (stream && !stream.isTTY && forceColor === undefined) {
-               return 0;
-       }
-
-       const min = forceColor || 0;
-
-       if (env.TERM === 'dumb') {
-               return min;
-       }
-
-       if (process.platform === 'win32') {
-               // Node.js 7.5.0 is the first version of Node.js to include a 
patch to
-               // libuv that enables 256 color output on Windows. Anything 
earlier and it
-               // won't work. However, here we target Node.js 8 at minimum as 
it is an LTS
-               // release, and Node.js 7 is not. Windows 10 build 10586 is the 
first Windows
-               // release that supports 256 colors. Windows 10 build 14931 is 
the first release
-               // that supports 16m/TrueColor.
-               const osRelease = os__default['default'].release().split('.');
-               if (
-                       Number(process.versions.node.split('.')[0]) >= 8 &&
-                       Number(osRelease[0]) >= 10 &&
-                       Number(osRelease[2]) >= 10586
-               ) {
-                       return Number(osRelease[2]) >= 14931 ? 3 : 2;
-               }
-
-               return 1;
-       }
-
-       if ('CI' in env) {
-               if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign 
=> sign in env) || env.CI_NAME === 'codeship') {
-                       return 1;
-               }
-
-               return min;
-       }
-
-       if ('TEAMCITY_VERSION' in env) {
-               return 
/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
-       }
-
-       if (env.COLORTERM === 'truecolor') {
-               return 3;
-       }
-
-       if ('TERM_PROGRAM' in env) {
-               const version = parseInt((env.TERM_PROGRAM_VERSION || 
'').split('.')[0], 10);
-
-               switch (env.TERM_PROGRAM) {
-                       case 'iTerm.app':
-                               return version >= 3 ? 3 : 2;
-                       case 'Apple_Terminal':
-                               return 2;
-                       // No default
-               }
-       }
-
-       if (/-256(color)?$/i.test(env.TERM)) {
-               return 2;
-       }
-
-       if 
(/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
-               return 1;
-       }
-
-       if ('COLORTERM' in env) {
-               return 1;
-       }
-
-       return min;
-}
-
-function getSupportLevel(stream) {
-       const level = supportsColor(stream);
-       return translateLevel(level);
-}
-
-var supportsColor_1 = {
-       supportsColor: getSupportLevel,
-       stdout: getSupportLevel(process.stdout),
-       stderr: getSupportLevel(process.stderr)
-};
-
-/**
- * Module dependencies.
- */
-
-var node = createCommonjsModule(function (module, exports) {
-/**
- * This is the Node.js implementation of `debug()`.
- */
-
-exports.init = init;
-exports.log = log;
-exports.formatArgs = formatArgs;
-exports.save = save;
-exports.load = load;
-exports.useColors = useColors;
-exports.destroy = util__default['default'].deprecate(
-       () => {},
-       'Instance method `debug.destroy()` is deprecated and no longer does 
anything. It will be removed in the next major version of `debug`.'
-);
-
-/**
- * Colors.
- */
-
-exports.colors = [6, 2, 3, 4, 5, 1];
-
-try {
-       // Optional dependency (as in, doesn't need to be installed, NOT like 
optionalDependencies in package.json)
-       // eslint-disable-next-line import/no-extraneous-dependencies
-       const supportsColor = supportsColor_1;
-
-       if (supportsColor && (supportsColor.stderr || supportsColor).level >= 
2) {
-               exports.colors = [
-                       20,
-                       21,
-                       26,
-                       27,
-                       32,
-                       33,
-                       38,
-                       39,
-                       40,
-                       41,
-                       42,
-                       43,
-                       44,
-                       45,
-                       56,
-                       57,
-                       62,
-                       63,
-                       68,
-                       69,
-                       74,
-                       75,
-                       76,
-                       77,
-                       78,
-                       79,
-                       80,
-                       81,
-                       92,
-                       93,
-                       98,
-                       99,
-                       112,
-                       113,
-                       128,
-                       129,
-                       134,
-                       135,
-                       148,
-                       149,
-                       160,
-                       161,
-                       162,
-                       163,
-                       164,
-                       165,
-                       166,
-                       167,
-                       168,
-                       169,
-                       170,
-                       171,
-                       172,
-                       173,
-                       178,
-                       179,
-                       184,
-                       185,
-                       196,
-                       197,
-                       198,
-                       199,
-                       200,
-                       201,
-                       202,
-                       203,
-                       204,
-                       205,
-                       206,
-                       207,
-                       208,
-                       209,
-                       214,
-                       215,
-                       220,
-                       221
-               ];
-       }
-} catch (error) {
-       // Swallow - we only care if `supports-color` is available; it doesn't 
have to be.
-}
-
-/**
- * Build up the default `inspectOpts` object from the environment variables.
- *
- *   $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
- */
-
-exports.inspectOpts = Object.keys(process.env).filter(key => {
-       return /^debug_/i.test(key);
-}).reduce((obj, key) => {
-       // Camel-case
-       const prop = key
-               .substring(6)
-               .toLowerCase()
-               .replace(/_([a-z])/g, (_, k) => {
-                       return k.toUpperCase();
-               });
-
-       // Coerce string value into JS value
-       let val = process.env[key];
-       if (/^(yes|on|true|enabled)$/i.test(val)) {
-               val = true;
-       } else if (/^(no|off|false|disabled)$/i.test(val)) {
-               val = false;
-       } else if (val === 'null') {
-               val = null;
-       } else {
-               val = Number(val);
-       }
-
-       obj[prop] = val;
-       return obj;
-}, {});
-
-/**
- * Is stdout a TTY? Colored output is enabled when `true`.
- */
-
-function useColors() {
-       return 'colors' in exports.inspectOpts ?
-               Boolean(exports.inspectOpts.colors) :
-               tty__default['default'].isatty(process.stderr.fd);
-}
-
-/**
- * Adds ANSI color escape codes if enabled.
- *
- * @api public
- */
-
-function formatArgs(args) {
-       const {namespace: name, useColors} = this;
-
-       if (useColors) {
-               const c = this.color;
-               const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
-               const prefix = `  ${colorCode};1m${name} \u001B[0m`;
-
-               args[0] = prefix + args[0].split('\n').join('\n' + prefix);
-               args.push(colorCode + 'm+' + module.exports.humanize(this.diff) 
+ '\u001B[0m');
-       } else {
-               args[0] = getDate() + name + ' ' + args[0];
-       }
-}
-
-function getDate() {
-       if (exports.inspectOpts.hideDate) {
-               return '';
-       }
-       return new Date().toISOString() + ' ';
-}
-
-/**
- * Invokes `util.format()` with the specified arguments and writes to stderr.
- */
-
-function log(...args) {
-       return process.stderr.write(util__default['default'].format(...args) + 
'\n');
-}
-
-/**
- * Save `namespaces`.
- *
- * @param {String} namespaces
- * @api private
- */
-function save(namespaces) {
-       if (namespaces) {
-               process.env.DEBUG = namespaces;
-       } else {
-               // If you set a process.env field to null or undefined, it gets 
cast to the
-               // string 'null' or 'undefined'. Just delete instead.
-               delete process.env.DEBUG;
-       }
-}
-
-/**
- * Load `namespaces`.
- *
- * @return {String} returns the previously persisted debug modes
- * @api private
- */
-
-function load() {
-       return process.env.DEBUG;
-}
-
-/**
- * Init logic for `debug` instances.
- *
- * Create a new `inspectOpts` object in case `useColors` is set
- * differently for a particular `debug` instance.
- */
-
-function init(debug) {
-       debug.inspectOpts = {};
-
-       const keys = Object.keys(exports.inspectOpts);
-       for (let i = 0; i < keys.length; i++) {
-               debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
-       }
-}
-
-module.exports = common(exports);
-
-const {formatters} = module.exports;
-
-/**
- * Map %o to `util.inspect()`, all on a single line.
- */
-
-formatters.o = function (v) {
-       this.inspectOpts.colors = this.useColors;
-       return util__default['default'].inspect(v, this.inspectOpts)
-               .split('\n')
-               .map(str => str.trim())
-               .join(' ');
-};
-
-/**
- * Map %O to `util.inspect()`, allowing multiple lines if needed.
- */
-
-formatters.O = function (v) {
-       this.inspectOpts.colors = this.useColors;
-       return util__default['default'].inspect(v, this.inspectOpts);
-};
-});
-
-/**
- * Detect Electron renderer / nwjs process, which is node, but we should
- * treat as a browser.
- */
-
-var src = createCommonjsModule(function (module) {
-if (typeof process === 'undefined' || process.type === 'renderer' || 
process.browser === true || process.__nwjs) {
-       module.exports = browser$1;
-} else {
-       module.exports = node;
-}
-});
-
-var debug;
-
-var debug_1 = function () {
-  if (!debug) {
-    try {
-      /* eslint global-require: off */
-      debug = src("follow-redirects");
-    }
-    catch (error) {
-      debug = function () { /* */ };
-    }
-  }
-  debug.apply(null, arguments);
-};
-
-var URL = url__default['default'].URL;
-
-
-var Writable = require$$0__default['default'].Writable;
-
-
-
-// Create handlers that pass events from native requests
-var events = ["abort", "aborted", "connect", "error", "socket", "timeout"];
-var eventHandlers = Object.create(null);
-events.forEach(function (event) {
-  eventHandlers[event] = function (arg1, arg2, arg3) {
-    this._redirectable.emit(event, arg1, arg2, arg3);
-  };
-});
-
-// Error types with codes
-var RedirectionError = createErrorType(
-  "ERR_FR_REDIRECTION_FAILURE",
-  ""
-);
-var TooManyRedirectsError = createErrorType(
-  "ERR_FR_TOO_MANY_REDIRECTS",
-  "Maximum number of redirects exceeded"
-);
-var MaxBodyLengthExceededError = createErrorType(
-  "ERR_FR_MAX_BODY_LENGTH_EXCEEDED",
-  "Request body larger than maxBodyLength limit"
-);
-var WriteAfterEndError = createErrorType(
-  "ERR_STREAM_WRITE_AFTER_END",
-  "write after end"
-);
-
-// An HTTP(S) request that can be redirected
-function RedirectableRequest(options, responseCallback) {
-  // Initialize the request
-  Writable.call(this);
-  this._sanitizeOptions(options);
-  this._options = options;
-  this._ended = false;
-  this._ending = false;
-  this._redirectCount = 0;
-  this._redirects = [];
-  this._requestBodyLength = 0;
-  this._requestBodyBuffers = [];
-
-  // Attach a callback if passed
-  if (responseCallback) {
-    this.on("response", responseCallback);
-  }
-
-  // React to responses of native requests
-  var self = this;
-  this._onNativeResponse = function (response) {
-    self._processResponse(response);
-  };
-
-  // Perform the first request
-  this._performRequest();
-}
-RedirectableRequest.prototype = Object.create(Writable.prototype);
-
-RedirectableRequest.prototype.abort = function () {
-  // Abort the internal request
-  abortRequest(this._currentRequest);
-
-  // Abort this request
-  this.emit("abort");
-  this.removeAllListeners();
-};
-
-// Writes buffered data to the current native request
-RedirectableRequest.prototype.write = function (data, encoding, callback) {
-  // Writing is not allowed if end has been called
-  if (this._ending) {
-    throw new WriteAfterEndError();
-  }
-
-  // Validate input and shift parameters if necessary
-  if (!(typeof data === "string" || typeof data === "object" && ("length" in 
data))) {
-    throw new TypeError("data should be a string, Buffer or Uint8Array");
-  }
-  if (typeof encoding === "function") {
-    callback = encoding;
-    encoding = null;
-  }
-
-  // Ignore empty buffers, since writing them doesn't invoke the callback
-  // https://github.com/nodejs/node/issues/22066
-  if (data.length === 0) {
-    if (callback) {
-      callback();
-    }
-    return;
-  }
-  // Only write when we don't exceed the maximum body length
-  if (this._requestBodyLength + data.length <= this._options.maxBodyLength) {
-    this._requestBodyLength += data.length;
-    this._requestBodyBuffers.push({ data: data, encoding: encoding });
-    this._currentRequest.write(data, encoding, callback);
-  }
-  // Error when we exceed the maximum body length
-  else {
-    this.emit("error", new MaxBodyLengthExceededError());
-    this.abort();
-  }
-};
-
-// Ends the current native request
-RedirectableRequest.prototype.end = function (data, encoding, callback) {
-  // Shift parameters if necessary
-  if (typeof data === "function") {
-    callback = data;
-    data = encoding = null;
-  }
-  else if (typeof encoding === "function") {
-    callback = encoding;
-    encoding = null;
-  }
-
-  // Write data if needed and end
-  if (!data) {
-    this._ended = this._ending = true;
-    this._currentRequest.end(null, null, callback);
-  }
-  else {
-    var self = this;
-    var currentRequest = this._currentRequest;
-    this.write(data, encoding, function () {
-      self._ended = true;
-      currentRequest.end(null, null, callback);
-    });
-    this._ending = true;
-  }
-};
-
-// Sets a header value on the current native request
-RedirectableRequest.prototype.setHeader = function (name, value) {
-  this._options.headers[name] = value;
-  this._currentRequest.setHeader(name, value);
-};
-
-// Clears a header value on the current native request
-RedirectableRequest.prototype.removeHeader = function (name) {
-  delete this._options.headers[name];
-  this._currentRequest.removeHeader(name);
-};
-
-// Global timeout for all underlying requests
-RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
-  var self = this;
-  if (callback) {
-    this.on("timeout", callback);
-  }
-
-  function destroyOnTimeout(socket) {
-    socket.setTimeout(msecs);
-    socket.removeListener("timeout", socket.destroy);
-    socket.addListener("timeout", socket.destroy);
-  }
-
-  // Sets up a timer to trigger a timeout event
-  function startTimer(socket) {
-    if (self._timeout) {
-      clearTimeout(self._timeout);
-    }
-    self._timeout = setTimeout(function () {
-      self.emit("timeout");
-      clearTimer();
-    }, msecs);
-    destroyOnTimeout(socket);
-  }
-
-  // Prevent a timeout from triggering
-  function clearTimer() {
-    clearTimeout(this._timeout);
-    if (callback) {
-      self.removeListener("timeout", callback);
-    }
-    if (!this.socket) {
-      self._currentRequest.removeListener("socket", startTimer);
-    }
-  }
-
-  // Start the timer when the socket is opened
-  if (this.socket) {
-    startTimer(this.socket);
-  }
-  else {
-    this._currentRequest.once("socket", startTimer);
-  }
-
-  this.on("socket", destroyOnTimeout);
-  this.once("response", clearTimer);
-  this.once("error", clearTimer);
-
-  return this;
-};
-
-// Proxy all other public ClientRequest methods
-[
-  "flushHeaders", "getHeader",
-  "setNoDelay", "setSocketKeepAlive",
-].forEach(function (method) {
-  RedirectableRequest.prototype[method] = function (a, b) {
-    return this._currentRequest[method](a, b);
-  };
-});
-
-// Proxy all public ClientRequest properties
-["aborted", "connection", "socket"].forEach(function (property) {
-  Object.defineProperty(RedirectableRequest.prototype, property, {
-    get: function () { return this._currentRequest[property]; },
-  });
-});
-
-RedirectableRequest.prototype._sanitizeOptions = function (options) {
-  // Ensure headers are always present
-  if (!options.headers) {
-    options.headers = {};
-  }
-
-  // Since http.request treats host as an alias of hostname,
-  // but the url module interprets host as hostname plus port,
-  // eliminate the host property to avoid confusion.
-  if (options.host) {
-    // Use hostname if set, because it has precedence
-    if (!options.hostname) {
-      options.hostname = options.host;
-    }
-    delete options.host;
-  }
-
-  // Complete the URL object when necessary
-  if (!options.pathname && options.path) {
-    var searchPos = options.path.indexOf("?");
-    if (searchPos < 0) {
-      options.pathname = options.path;
-    }
-    else {
-      options.pathname = options.path.substring(0, searchPos);
-      options.search = options.path.substring(searchPos);
-    }
-  }
-};
-
-
-// Executes the next native request (initial or redirect)
-RedirectableRequest.prototype._performRequest = function () {
-  // Load the native protocol
-  var protocol = this._options.protocol;
-  var nativeProtocol = this._options.nativeProtocols[protocol];
-  if (!nativeProtocol) {
-    this.emit("error", new TypeError("Unsupported protocol " + protocol));
-    return;
-  }
-
-  // If specified, use the agent corresponding to the protocol
-  // (HTTP and HTTPS use different types of agents)
-  if (this._options.agents) {
-    var scheme = protocol.substr(0, protocol.length - 1);
-    this._options.agent = this._options.agents[scheme];
-  }
-
-  // Create the native request
-  var request = this._currentRequest =
-        nativeProtocol.request(this._options, this._onNativeResponse);
-  this._currentUrl = url__default['default'].format(this._options);
-
-  // Set up event handlers
-  request._redirectable = this;
-  for (var e = 0; e < events.length; e++) {
-    request.on(events[e], eventHandlers[events[e]]);
-  }
-
-  // End a redirected request
-  // (The first request must be ended explicitly with RedirectableRequest#end)
-  if (this._isRedirect) {
-    // Write the request entity and end.
-    var i = 0;
-    var self = this;
-    var buffers = this._requestBodyBuffers;
-    (function writeNext(error) {
-      // Only write if this request has not been redirected yet
-      /* istanbul ignore else */
-      if (request === self._currentRequest) {
-        // Report any write errors
-        /* istanbul ignore if */
-        if (error) {
-          self.emit("error", error);
-        }
-        // Write the next buffer if there are still left
-        else if (i < buffers.length) {
-          var buffer = buffers[i++];
-          /* istanbul ignore else */
-          if (!request.finished) {
-            request.write(buffer.data, buffer.encoding, writeNext);
-          }
-        }
-        // End the request if `end` has been called on us
-        else if (self._ended) {
-          request.end();
-        }
-      }
-    }());
-  }
-};
-
-// Processes a response from the current native request
-RedirectableRequest.prototype._processResponse = function (response) {
-  // Store the redirected response
-  var statusCode = response.statusCode;
-  if (this._options.trackRedirects) {
-    this._redirects.push({
-      url: this._currentUrl,
-      headers: response.headers,
-      statusCode: statusCode,
-    });
-  }
-
-  // RFC7231§6.4: The 3xx (Redirection) class of status code indicates
-  // that further action needs to be taken by the user agent in order to
-  // fulfill the request. If a Location header field is provided,
-  // the user agent MAY automatically redirect its request to the URI
-  // referenced by the Location field value,
-  // even if the specific status code is not understood.
-  var location = response.headers.location;
-  if (location && this._options.followRedirects !== false &&
-      statusCode >= 300 && statusCode < 400) {
-    // Abort the current request
-    abortRequest(this._currentRequest);
-    // Discard the remainder of the response to avoid waiting for data
-    response.destroy();
-
-    // RFC7231§6.4: A client SHOULD detect and intervene
-    // in cyclical redirections (i.e., "infinite" redirection loops).
-    if (++this._redirectCount > this._options.maxRedirects) {
-      this.emit("error", new TooManyRedirectsError());
-      return;
-    }
-
-    // RFC7231§6.4: Automatic redirection needs to done with
-    // care for methods not known to be safe, […]
-    // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
-    // the request method from POST to GET for the subsequent request.
-    if ((statusCode === 301 || statusCode === 302) && this._options.method === 
"POST" ||
-        // RFC7231§6.4.4: The 303 (See Other) status code indicates that
-        // the server is redirecting the user agent to a different resource […]
-        // A user agent can perform a retrieval request targeting that URI
-        // (a GET or HEAD request if using HTTP) […]
-        (statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) {
-      this._options.method = "GET";
-      // Drop a possible entity and headers related to it
-      this._requestBodyBuffers = [];
-      removeMatchingHeaders(/^content-/i, this._options.headers);
-    }
-
-    // Drop the Host header, as the redirect might lead to a different host
-    var previousHostName = removeMatchingHeaders(/^host$/i, 
this._options.headers) ||
-      url__default['default'].parse(this._currentUrl).hostname;
-
-    // Create the redirected request
-    var redirectUrl = url__default['default'].resolve(this._currentUrl, 
location);
-    debug_1("redirecting to", redirectUrl);
-    this._isRedirect = true;
-    var redirectUrlParts = url__default['default'].parse(redirectUrl);
-    Object.assign(this._options, redirectUrlParts);
-
-    // Drop the Authorization header if redirecting to another host
-    if (redirectUrlParts.hostname !== previousHostName) {
-      removeMatchingHeaders(/^authorization$/i, this._options.headers);
-    }
-
-    // Evaluate the beforeRedirect callback
-    if (typeof this._options.beforeRedirect === "function") {
-      var responseDetails = { headers: response.headers };
-      try {
-        this._options.beforeRedirect.call(null, this._options, 
responseDetails);
-      }
-      catch (err) {
-        this.emit("error", err);
-        return;
-      }
-      this._sanitizeOptions(this._options);
-    }
-
-    // Perform the redirected request
-    try {
-      this._performRequest();
-    }
-    catch (cause) {
-      var error = new RedirectionError("Redirected request failed: " + 
cause.message);
-      error.cause = cause;
-      this.emit("error", error);
-    }
-  }
-  else {
-    // The response is not a redirect; return it as-is
-    response.responseUrl = this._currentUrl;
-    response.redirects = this._redirects;
-    this.emit("response", response);
-
-    // Clean up
-    this._requestBodyBuffers = [];
-  }
-};
-
-// Wraps the key/value object of protocols with redirect functionality
-function wrap(protocols) {
-  // Default settings
-  var exports = {
-    maxRedirects: 21,
-    maxBodyLength: 10 * 1024 * 1024,
-  };
-
-  // Wrap each protocol
-  var nativeProtocols = {};
-  Object.keys(protocols).forEach(function (scheme) {
-    var protocol = scheme + ":";
-    var nativeProtocol = nativeProtocols[protocol] = protocols[scheme];
-    var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol);
-
-    // Executes a request, following redirects
-    function request(input, options, callback) {
-      // Parse parameters
-      if (typeof input === "string") {
-        var urlStr = input;
-        try {
-          input = urlToOptions(new URL(urlStr));
-        }
-        catch (err) {
-          /* istanbul ignore next */
-          input = url__default['default'].parse(urlStr);
-        }
-      }
-      else if (URL && (input instanceof URL)) {
-        input = urlToOptions(input);
-      }
-      else {
-        callback = options;
-        options = input;
-        input = { protocol: protocol };
-      }
-      if (typeof options === "function") {
-        callback = options;
-        options = null;
-      }
-
-      // Set defaults
-      options = Object.assign({
-        maxRedirects: exports.maxRedirects,
-        maxBodyLength: exports.maxBodyLength,
-      }, input, options);
-      options.nativeProtocols = nativeProtocols;
-
-      assert__default['default'].equal(options.protocol, protocol, "protocol 
mismatch");
-      debug_1("options", options);
-      return new RedirectableRequest(options, callback);
-    }
-
-    // Executes a GET request, following redirects
-    function get(input, options, callback) {
-      var wrappedRequest = wrappedProtocol.request(input, options, callback);
-      wrappedRequest.end();
-      return wrappedRequest;
-    }
-
-    // Expose the properties on the wrapped protocol
-    Object.defineProperties(wrappedProtocol, {
-      request: { value: request, configurable: true, enumerable: true, 
writable: true },
-      get: { value: get, configurable: true, enumerable: true, writable: true 
},
-    });
-  });
-  return exports;
-}
-
-/* istanbul ignore next */
-function noop() { /* empty */ }
-
-// from https://github.com/nodejs/node/blob/master/lib/internal/url.js
-function urlToOptions(urlObject) {
-  var options = {
-    protocol: urlObject.protocol,
-    hostname: urlObject.hostname.startsWith("[") ?
-      /* istanbul ignore next */
-      urlObject.hostname.slice(1, -1) :
-      urlObject.hostname,
-    hash: urlObject.hash,
-    search: urlObject.search,
-    pathname: urlObject.pathname,
-    path: urlObject.pathname + urlObject.search,
-    href: urlObject.href,
-  };
-  if (urlObject.port !== "") {
-    options.port = Number(urlObject.port);
-  }
-  return options;
-}
-
-function removeMatchingHeaders(regex, headers) {
-  var lastValue;
-  for (var header in headers) {
-    if (regex.test(header)) {
-      lastValue = headers[header];
-      delete headers[header];
-    }
-  }
-  return lastValue;
-}
-
-function createErrorType(code, defaultMessage) {
-  function CustomError(message) {
-    Error.captureStackTrace(this, this.constructor);
-    this.message = message || defaultMessage;
-  }
-  CustomError.prototype = new Error();
-  CustomError.prototype.constructor = CustomError;
-  CustomError.prototype.name = "Error [" + code + "]";
-  CustomError.prototype.code = code;
-  return CustomError;
-}
-
-function abortRequest(request) {
-  for (var e = 0; e < events.length; e++) {
-    request.removeListener(events[e], eventHandlers[events[e]]);
-  }
-  request.on("error", noop);
-  request.abort();
-}
-
-// Exports
-var followRedirects = wrap({ http: http__default['default'], https: 
https__default['default'] });
-var wrap_1 = wrap;
-followRedirects.wrap = wrap_1;
-
-var name = "axios";
-var version = "0.21.1";
-var description = "Promise based HTTP client for the browser and node.js";
-var main = "index.js";
-var scripts = {
-       test: "grunt test && bundlesize",
-       start: "node ./sandbox/server.js",
-       build: "NODE_ENV=production grunt build",
-       preversion: "npm test",
-       version: "npm run build && grunt version && git add -A dist && git add 
CHANGELOG.md bower.json package.json",
-       postversion: "git push && git push --tags",
-       examples: "node ./examples/server.js",
-       coveralls: "cat coverage/lcov.info | 
./node_modules/coveralls/bin/coveralls.js",
-       fix: "eslint --fix lib/**/*.js"
-};
-var repository = {
-       type: "git",
-       url: "https://github.com/axios/axios.git";
-};
-var keywords = [
-       "xhr",
-       "http",
-       "ajax",
-       "promise",
-       "node"
-];
-var author = "Matt Zabriskie";
-var license = "MIT";
-var bugs = {
-       url: "https://github.com/axios/axios/issues";
-};
-var homepage = "https://github.com/axios/axios";;
-var devDependencies = {
-       bundlesize: "^0.17.0",
-       coveralls: "^3.0.0",
-       "es6-promise": "^4.2.4",
-       grunt: "^1.0.2",
-       "grunt-banner": "^0.6.0",
-       "grunt-cli": "^1.2.0",
-       "grunt-contrib-clean": "^1.1.0",
-       "grunt-contrib-watch": "^1.0.0",
-       "grunt-eslint": "^20.1.0",
-       "grunt-karma": "^2.0.0",
-       "grunt-mocha-test": "^0.13.3",
-       "grunt-ts": "^6.0.0-beta.19",
-       "grunt-webpack": "^1.0.18",
-       "istanbul-instrumenter-loader": "^1.0.0",
-       "jasmine-core": "^2.4.1",
-       karma: "^1.3.0",
-       "karma-chrome-launcher": "^2.2.0",
-       "karma-coverage": "^1.1.1",
-       "karma-firefox-launcher": "^1.1.0",
-       "karma-jasmine": "^1.1.1",
-       "karma-jasmine-ajax": "^0.1.13",
-       "karma-opera-launcher": "^1.0.0",
-       "karma-safari-launcher": "^1.0.0",
-       "karma-sauce-launcher": "^1.2.0",
-       "karma-sinon": "^1.0.5",
-       "karma-sourcemap-loader": "^0.3.7",
-       "karma-webpack": "^1.7.0",
-       "load-grunt-tasks": "^3.5.2",
-       minimist: "^1.2.0",
-       mocha: "^5.2.0",
-       sinon: "^4.5.0",
-       typescript: "^2.8.1",
-       "url-search-params": "^0.10.0",
-       webpack: "^1.13.1",
-       "webpack-dev-server": "^1.14.1"
-};
-var browser = {
-       "./lib/adapters/http.js": "./lib/adapters/xhr.js"
-};
-var jsdelivr = "dist/axios.min.js";
-var unpkg = "dist/axios.min.js";
-var typings = "./index.d.ts";
-var dependencies = {
-       "follow-redirects": "^1.10.0"
-};
-var bundlesize = [
-       {
-               path: "./dist/axios.min.js",
-               threshold: "5kB"
-       }
-];
-var pkg = {
-       name: name,
-       version: version,
-       description: description,
-       main: main,
-       scripts: scripts,
-       repository: repository,
-       keywords: keywords,
-       author: author,
-       license: license,
-       bugs: bugs,
-       homepage: homepage,
-       devDependencies: devDependencies,
-       browser: browser,
-       jsdelivr: jsdelivr,
-       unpkg: unpkg,
-       typings: typings,
-       dependencies: dependencies,
-       bundlesize: bundlesize
-};
-
-var httpFollow = followRedirects.http;
-var httpsFollow = followRedirects.https;
-
-
-
-
-
-
-var isHttps = /https:?/;
-
-/**
- *
- * @param {http.ClientRequestArgs} options
- * @param {AxiosProxyConfig} proxy
- * @param {string} location
- */
-function setProxy(options, proxy, location) {
-  options.hostname = proxy.host;
-  options.host = proxy.host;
-  options.port = proxy.port;
-  options.path = location;
-
-  // Basic proxy authorization
-  if (proxy.auth) {
-    var base64 = Buffer.from(proxy.auth.username + ':' + proxy.auth.password, 
'utf8').toString('base64');
-    options.headers['Proxy-Authorization'] = 'Basic ' + base64;
-  }
-
-  // If a proxy is used, any redirects must also pass through the proxy
-  options.beforeRedirect = function beforeRedirect(redirection) {
-    redirection.headers.host = redirection.host;
-    setProxy(redirection, proxy, redirection.href);
-  };
-}
-
-/*eslint consistent-return:0*/
-var http_1 = function httpAdapter(config) {
-  return new Promise(function dispatchHttpRequest(resolvePromise, 
rejectPromise) {
-    var resolve = function resolve(value) {
-      resolvePromise(value);
-    };
-    var reject = function reject(value) {
-      rejectPromise(value);
-    };
-    var data = config.data;
-    var headers = config.headers;
-
-    // Set User-Agent (required by some servers)
-    // Only set header if it hasn't been set in config
-    // See https://github.com/axios/axios/issues/69
-    if (!headers['User-Agent'] && !headers['user-agent']) {
-      headers['User-Agent'] = 'axios/' + pkg.version;
-    }
-
-    if (data && !utils.isStream(data)) {
-      if (Buffer.isBuffer(data)) ; else if (utils.isArrayBuffer(data)) {
-        data = Buffer.from(new Uint8Array(data));
-      } else if (utils.isString(data)) {
-        data = Buffer.from(data, 'utf-8');
-      } else {
-        return reject(createError(
-          'Data after transformation must be a string, an ArrayBuffer, a 
Buffer, or a Stream',
-          config
-        ));
-      }
-
-      // Add Content-Length header if data exists
-      headers['Content-Length'] = data.length;
-    }
-
-    // HTTP basic authentication
-    var auth = undefined;
-    if (config.auth) {
-      var username = config.auth.username || '';
-      var password = config.auth.password || '';
-      auth = username + ':' + password;
-    }
-
-    // Parse url
-    var fullPath = buildFullPath(config.baseURL, config.url);
-    var parsed = url__default['default'].parse(fullPath);
-    var protocol = parsed.protocol || 'http:';
-
-    if (!auth && parsed.auth) {
-      var urlAuth = parsed.auth.split(':');
-      var urlUsername = urlAuth[0] || '';
-      var urlPassword = urlAuth[1] || '';
-      auth = urlUsername + ':' + urlPassword;
-    }
-
-    if (auth) {
-      delete headers.Authorization;
-    }
-
-    var isHttpsRequest = isHttps.test(protocol);
-    var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
-
-    var options = {
-      path: buildURL(parsed.path, config.params, 
config.paramsSerializer).replace(/^\?/, ''),
-      method: config.method.toUpperCase(),
-      headers: headers,
-      agent: agent,
-      agents: { http: config.httpAgent, https: config.httpsAgent },
-      auth: auth
-    };
-
-    if (config.socketPath) {
-      options.socketPath = config.socketPath;
-    } else {
-      options.hostname = parsed.hostname;
-      options.port = parsed.port;
-    }
-
-    var proxy = config.proxy;
-    if (!proxy && proxy !== false) {
-      var proxyEnv = protocol.slice(0, -1) + '_proxy';
-      var proxyUrl = process.env[proxyEnv] || 
process.env[proxyEnv.toUpperCase()];
-      if (proxyUrl) {
-        var parsedProxyUrl = url__default['default'].parse(proxyUrl);
-        var noProxyEnv = process.env.no_proxy || process.env.NO_PROXY;
-        var shouldProxy = true;
-
-        if (noProxyEnv) {
-          var noProxy = noProxyEnv.split(',').map(function trim(s) {
-            return s.trim();
-          });
-
-          shouldProxy = !noProxy.some(function proxyMatch(proxyElement) {
-            if (!proxyElement) {
-              return false;
-            }
-            if (proxyElement === '*') {
-              return true;
-            }
-            if (proxyElement[0] === '.' &&
-                parsed.hostname.substr(parsed.hostname.length - 
proxyElement.length) === proxyElement) {
-              return true;
-            }
-
-            return parsed.hostname === proxyElement;
-          });
-        }
-
-        if (shouldProxy) {
-          proxy = {
-            host: parsedProxyUrl.hostname,
-            port: parsedProxyUrl.port,
-            protocol: parsedProxyUrl.protocol
-          };
-
-          if (parsedProxyUrl.auth) {
-            var proxyUrlAuth = parsedProxyUrl.auth.split(':');
-            proxy.auth = {
-              username: proxyUrlAuth[0],
-              password: proxyUrlAuth[1]
-            };
-          }
-        }
-      }
-    }
-
-    if (proxy) {
-      options.headers.host = parsed.hostname + (parsed.port ? ':' + 
parsed.port : '');
-      setProxy(options, proxy, protocol + '//' + parsed.hostname + 
(parsed.port ? ':' + parsed.port : '') + options.path);
-    }
-
-    var transport;
-    var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) 
: true);
-    if (config.transport) {
-      transport = config.transport;
-    } else if (config.maxRedirects === 0) {
-      transport = isHttpsProxy ? https__default['default'] : 
http__default['default'];
-    } else {
-      if (config.maxRedirects) {
-        options.maxRedirects = config.maxRedirects;
-      }
-      transport = isHttpsProxy ? httpsFollow : httpFollow;
-    }
-
-    if (config.maxBodyLength > -1) {
-      options.maxBodyLength = config.maxBodyLength;
-    }
-
-    // Create the request
-    var req = transport.request(options, function handleResponse(res) {
-      if (req.aborted) return;
-
-      // uncompress the response body transparently if required
-      var stream = res;
-
-      // return the last request in case of redirects
-      var lastRequest = res.req || req;
-
-
-      // if no content, is HEAD request or decompress disabled we should not 
decompress
-      if (res.statusCode !== 204 && lastRequest.method !== 'HEAD' && 
config.decompress !== false) {
-        switch (res.headers['content-encoding']) {
-        /*eslint default-case:0*/
-        case 'gzip':
-        case 'compress':
-        case 'deflate':
-        // add the unzipper to the body stream processing pipeline
-          stream = stream.pipe(zlib__default['default'].createUnzip());
-
-          // remove the content-encoding in order to not confuse downstream 
operations
-          delete res.headers['content-encoding'];
-          break;
-        }
-      }
-
-      var response = {
-        status: res.statusCode,
-        statusText: res.statusMessage,
-        headers: res.headers,
-        config: config,
-        request: lastRequest
-      };
-
-      if (config.responseType === 'stream') {
-        response.data = stream;
-        settle(resolve, reject, response);
-      } else {
-        var responseBuffer = [];
-        stream.on('data', function handleStreamData(chunk) {
-          responseBuffer.push(chunk);
-
-          // make sure the content length is not over the maxContentLength if 
specified
-          if (config.maxContentLength > -1 && 
Buffer.concat(responseBuffer).length > config.maxContentLength) {
-            stream.destroy();
-            reject(createError('maxContentLength size of ' + 
config.maxContentLength + ' exceeded',
-              config, null, lastRequest));
-          }
-        });
-
-        stream.on('error', function handleStreamError(err) {
-          if (req.aborted) return;
-          reject(enhanceError(err, config, null, lastRequest));
-        });
-
-        stream.on('end', function handleStreamEnd() {
-          var responseData = Buffer.concat(responseBuffer);
-          if (config.responseType !== 'arraybuffer') {
-            responseData = responseData.toString(config.responseEncoding);
-            if (!config.responseEncoding || config.responseEncoding === 
'utf8') {
-              responseData = utils.stripBOM(responseData);
-            }
-          }
-
-          response.data = responseData;
-          settle(resolve, reject, response);
-        });
-      }
-    });
-
-    // Handle errors
-    req.on('error', function handleRequestError(err) {
-      if (req.aborted && err.code !== 'ERR_FR_TOO_MANY_REDIRECTS') return;
-      reject(enhanceError(err, config, null, req));
-    });
-
-    // Handle request timeout
-    if (config.timeout) {
-      // Sometime, the response will be very slow, and does not respond, the 
connect event will be block by event loop system.
-      // And timer callback will be fired, and abort() will be invoked before 
connection, then get "socket hang up" and code ECONNRESET.
-      // At this time, if we have a large number of request, nodejs will hang 
up some socket on background. and the number will up and up.
-      // And then these socket which be hang up will devoring CPU little by 
little.
-      // ClientRequest.setTimeout will be fired on the specify milliseconds, 
and can make sure that abort() will be fired after connect.
-      req.setTimeout(config.timeout, function handleRequestTimeout() {
-        req.abort();
-        reject(createError('timeout of ' + config.timeout + 'ms exceeded', 
config, 'ECONNABORTED', req));
-      });
-    }
-
-    if (config.cancelToken) {
-      // Handle cancellation
-      config.cancelToken.promise.then(function onCanceled(cancel) {
-        if (req.aborted) return;
-
-        req.abort();
-        reject(cancel);
-      });
-    }
-
-    // Send the request
-    if (utils.isStream(data)) {
-      data.on('error', function handleStreamError(err) {
-        reject(enhanceError(err, config, null, req));
-      }).pipe(req);
-    } else {
-      req.end(data);
-    }
-  });
-};
-
-var DEFAULT_CONTENT_TYPE = {
-  'Content-Type': 'application/x-www-form-urlencoded'
-};
-
-function setContentTypeIfUnset(headers, value) {
-  if (!utils.isUndefined(headers) && 
utils.isUndefined(headers['Content-Type'])) {
-    headers['Content-Type'] = value;
-  }
-}
-
-function getDefaultAdapter() {
-  var adapter;
-  if (typeof XMLHttpRequest !== 'undefined') {
-    // For browsers use XHR adapter
-    adapter = xhr;
-  } else if (typeof process !== 'undefined' && 
Object.prototype.toString.call(process) === '[object process]') {
-    // For node use HTTP adapter
-    adapter = http_1;
-  }
-  return adapter;
-}
-
-var defaults = {
-  adapter: getDefaultAdapter(),
-
-  transformRequest: [function transformRequest(data, headers) {
-    normalizeHeaderName(headers, 'Accept');
-    normalizeHeaderName(headers, 'Content-Type');
-    if (utils.isFormData(data) ||
-      utils.isArrayBuffer(data) ||
-      utils.isBuffer(data) ||
-      utils.isStream(data) ||
-      utils.isFile(data) ||
-      utils.isBlob(data)
-    ) {
-      return data;
-    }
-    if (utils.isArrayBufferView(data)) {
-      return data.buffer;
-    }
-    if (utils.isURLSearchParams(data)) {
-      setContentTypeIfUnset(headers, 
'application/x-www-form-urlencoded;charset=utf-8');
-      return data.toString();
-    }
-    if (utils.isObject(data)) {
-      setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
-      return JSON.stringify(data);
-    }
-    return data;
-  }],
-
-  transformResponse: [function transformResponse(data) {
-    /*eslint no-param-reassign:0*/
-    if (typeof data === 'string') {
-      try {
-        data = JSON.parse(data);
-      } catch (e) { /* Ignore */ }
-    }
-    return data;
-  }],
-
-  /**
-   * A timeout in milliseconds to abort a request. If set to 0 (default) a
-   * timeout is not created.
-   */
-  timeout: 0,
-
-  xsrfCookieName: 'XSRF-TOKEN',
-  xsrfHeaderName: 'X-XSRF-TOKEN',
-
-  maxContentLength: -1,
-  maxBodyLength: -1,
-
-  validateStatus: function validateStatus(status) {
-    return status >= 200 && status < 300;
-  }
-};
-
-defaults.headers = {
-  common: {
-    'Accept': 'application/json, text/plain, */*'
-  }
-};
-
-utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
-  defaults.headers[method] = {};
-});
-
-utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) 
{
-  defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
-});
-
-var defaults_1 = defaults;
-
-/**
- * Throws a `Cancel` if cancellation has been requested.
- */
-function throwIfCancellationRequested(config) {
-  if (config.cancelToken) {
-    config.cancelToken.throwIfRequested();
-  }
-}
-
-/**
- * Dispatch a request to the server using the configured adapter.
- *
- * @param {object} config The config that is to be used for the request
- * @returns {Promise} The Promise to be fulfilled
- */
-var dispatchRequest = function dispatchRequest(config) {
-  throwIfCancellationRequested(config);
-
-  // Ensure headers exist
-  config.headers = config.headers || {};
-
-  // Transform request data
-  config.data = transformData(
-    config.data,
-    config.headers,
-    config.transformRequest
-  );
-
-  // Flatten headers
-  config.headers = utils.merge(
-    config.headers.common || {},
-    config.headers[config.method] || {},
-    config.headers
-  );
-
-  utils.forEach(
-    ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
-    function cleanHeaderConfig(method) {
-      delete config.headers[method];
-    }
-  );
-
-  var adapter = config.adapter || defaults_1.adapter;
-
-  return adapter(config).then(function onAdapterResolution(response) {
-    throwIfCancellationRequested(config);
-
-    // Transform response data
-    response.data = transformData(
-      response.data,
-      response.headers,
-      config.transformResponse
-    );
-
-    return response;
-  }, function onAdapterRejection(reason) {
-    if (!isCancel(reason)) {
-      throwIfCancellationRequested(config);
-
-      // Transform response data
-      if (reason && reason.response) {
-        reason.response.data = transformData(
-          reason.response.data,
-          reason.response.headers,
-          config.transformResponse
-        );
-      }
-    }
-
-    return Promise.reject(reason);
-  });
-};
-
-/**
- * Config-specific merge-function which creates a new config-object
- * by merging two configuration objects together.
- *
- * @param {Object} config1
- * @param {Object} config2
- * @returns {Object} New object resulting from merging config2 to config1
- */
-var mergeConfig = function mergeConfig(config1, config2) {
-  // eslint-disable-next-line no-param-reassign
-  config2 = config2 || {};
-  var config = {};
-
-  var valueFromConfig2Keys = ['url', 'method', 'data'];
-  var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params'];
-  var defaultToConfig2Keys = [
-    'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',
-    'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 
'xsrfCookieName',
-    'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress',
-    'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 
'httpAgent',
-    'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding'
-  ];
-  var directMergeKeys = ['validateStatus'];
-
-  function getMergedValue(target, source) {
-    if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
-      return utils.merge(target, source);
-    } else if (utils.isPlainObject(source)) {
-      return utils.merge({}, source);
-    } else if (utils.isArray(source)) {
-      return source.slice();
-    }
-    return source;
-  }
-
-  function mergeDeepProperties(prop) {
-    if (!utils.isUndefined(config2[prop])) {
-      config[prop] = getMergedValue(config1[prop], config2[prop]);
-    } else if (!utils.isUndefined(config1[prop])) {
-      config[prop] = getMergedValue(undefined, config1[prop]);
-    }
-  }
-
-  utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {
-    if (!utils.isUndefined(config2[prop])) {
-      config[prop] = getMergedValue(undefined, config2[prop]);
-    }
-  });
-
-  utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties);
-
-  utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
-    if (!utils.isUndefined(config2[prop])) {
-      config[prop] = getMergedValue(undefined, config2[prop]);
-    } else if (!utils.isUndefined(config1[prop])) {
-      config[prop] = getMergedValue(undefined, config1[prop]);
-    }
-  });
-
-  utils.forEach(directMergeKeys, function merge(prop) {
-    if (prop in config2) {
-      config[prop] = getMergedValue(config1[prop], config2[prop]);
-    } else if (prop in config1) {
-      config[prop] = getMergedValue(undefined, config1[prop]);
-    }
-  });
-
-  var axiosKeys = valueFromConfig2Keys
-    .concat(mergeDeepPropertiesKeys)
-    .concat(defaultToConfig2Keys)
-    .concat(directMergeKeys);
-
-  var otherKeys = Object
-    .keys(config1)
-    .concat(Object.keys(config2))
-    .filter(function filterAxiosKeys(key) {
-      return axiosKeys.indexOf(key) === -1;
-    });
-
-  utils.forEach(otherKeys, mergeDeepProperties);
-
-  return config;
-};
-
-/**
- * Create a new instance of Axios
- *
- * @param {Object} instanceConfig The default config for the instance
- */
-function Axios(instanceConfig) {
-  this.defaults = instanceConfig;
-  this.interceptors = {
-    request: new InterceptorManager_1(),
-    response: new InterceptorManager_1()
-  };
-}
-
-/**
- * Dispatch a request
- *
- * @param {Object} config The config specific for this request (merged with 
this.defaults)
- */
-Axios.prototype.request = function request(config) {
-  /*eslint no-param-reassign:0*/
-  // Allow for axios('example/url'[, config]) a la fetch API
-  if (typeof config === 'string') {
-    config = arguments[1] || {};
-    config.url = arguments[0];
-  } else {
-    config = config || {};
-  }
-
-  config = mergeConfig(this.defaults, config);
-
-  // Set config.method
-  if (config.method) {
-    config.method = config.method.toLowerCase();
-  } else if (this.defaults.method) {
-    config.method = this.defaults.method.toLowerCase();
-  } else {
-    config.method = 'get';
-  }
-
-  // Hook up interceptors middleware
-  var chain = [dispatchRequest, undefined];
-  var promise = Promise.resolve(config);
-
-  this.interceptors.request.forEach(function 
unshiftRequestInterceptors(interceptor) {
-    chain.unshift(interceptor.fulfilled, interceptor.rejected);
-  });
-
-  this.interceptors.response.forEach(function 
pushResponseInterceptors(interceptor) {
-    chain.push(interceptor.fulfilled, interceptor.rejected);
-  });
-
-  while (chain.length) {
-    promise = promise.then(chain.shift(), chain.shift());
-  }
-
-  return promise;
-};
-
-Axios.prototype.getUri = function getUri(config) {
-  config = mergeConfig(this.defaults, config);
-  return buildURL(config.url, config.params, 
config.paramsSerializer).replace(/^\?/, '');
-};
-
-// Provide aliases for supported request methods
-utils.forEach(['delete', 'get', 'head', 'options'], function 
forEachMethodNoData(method) {
-  /*eslint func-names:0*/
-  Axios.prototype[method] = function(url, config) {
-    return this.request(mergeConfig(config || {}, {
-      method: method,
-      url: url,
-      data: (config || {}).data
-    }));
-  };
-});
-
-utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) 
{
-  /*eslint func-names:0*/
-  Axios.prototype[method] = function(url, data, config) {
-    return this.request(mergeConfig(config || {}, {
-      method: method,
-      url: url,
-      data: data
-    }));
-  };
-});
-
-var Axios_1 = Axios;
-
-/**
- * A `Cancel` is an object that is thrown when an operation is canceled.
- *
- * @class
- * @param {string=} message The message.
- */
-function Cancel(message) {
-  this.message = message;
-}
-
-Cancel.prototype.toString = function toString() {
-  return 'Cancel' + (this.message ? ': ' + this.message : '');
-};
-
-Cancel.prototype.__CANCEL__ = true;
-
-var Cancel_1 = Cancel;
-
-/**
- * A `CancelToken` is an object that can be used to request cancellation of an 
operation.
- *
- * @class
- * @param {Function} executor The executor function.
- */
-function CancelToken(executor) {
-  if (typeof executor !== 'function') {
-    throw new TypeError('executor must be a function.');
-  }
-
-  var resolvePromise;
-  this.promise = new Promise(function promiseExecutor(resolve) {
-    resolvePromise = resolve;
-  });
-
-  var token = this;
-  executor(function cancel(message) {
-    if (token.reason) {
-      // Cancellation has already been requested
-      return;
-    }
-
-    token.reason = new Cancel_1(message);
-    resolvePromise(token.reason);
-  });
-}
-
-/**
- * Throws a `Cancel` if cancellation has been requested.
- */
-CancelToken.prototype.throwIfRequested = function throwIfRequested() {
-  if (this.reason) {
-    throw this.reason;
-  }
-};
-
-/**
- * Returns an object that contains a new `CancelToken` and a function that, 
when called,
- * cancels the `CancelToken`.
- */
-CancelToken.source = function source() {
-  var cancel;
-  var token = new CancelToken(function executor(c) {
-    cancel = c;
-  });
-  return {
-    token: token,
-    cancel: cancel
-  };
-};
-
-var CancelToken_1 = CancelToken;
-
-/**
- * Syntactic sugar for invoking a function and expanding an array for 
arguments.
- *
- * Common use case would be to use `Function.prototype.apply`.
- *
- *  ```js
- *  function f(x, y, z) {}
- *  var args = [1, 2, 3];
- *  f.apply(null, args);
- *  ```
- *
- * With `spread` this example can be re-written.
- *
- *  ```js
- *  spread(function(x, y, z) {})([1, 2, 3]);
- *  ```
- *
- * @param {Function} callback
- * @returns {Function}
- */
-var spread = function spread(callback) {
-  return function wrap(arr) {
-    return callback.apply(null, arr);
-  };
-};
-
-/**
- * Determines whether the payload is an error thrown by Axios
- *
- * @param {*} payload The value to test
- * @returns {boolean} True if the payload is an error thrown by Axios, 
otherwise false
- */
-var isAxiosError = function isAxiosError(payload) {
-  return (typeof payload === 'object') && (payload.isAxiosError === true);
-};
-
-/**
- * Create an instance of Axios
- *
- * @param {Object} defaultConfig The default config for the instance
- * @return {Axios} A new instance of Axios
- */
-function createInstance(defaultConfig) {
-  var context = new Axios_1(defaultConfig);
-  var instance = bind(Axios_1.prototype.request, context);
-
-  // Copy axios.prototype to instance
-  utils.extend(instance, Axios_1.prototype, context);
-
-  // Copy context to instance
-  utils.extend(instance, context);
-
-  return instance;
-}
-
-// Create the default instance to be exported
-var axios$1 = createInstance(defaults_1);
-
-// Expose Axios class to allow class inheritance
-axios$1.Axios = Axios_1;
-
-// Factory for creating new instances
-axios$1.create = function create(instanceConfig) {
-  return createInstance(mergeConfig(axios$1.defaults, instanceConfig));
-};
-
-// Expose Cancel & CancelToken
-axios$1.Cancel = Cancel_1;
-axios$1.CancelToken = CancelToken_1;
-axios$1.isCancel = isCancel;
-
-// Expose all/spread
-axios$1.all = function all(promises) {
-  return Promise.all(promises);
-};
-axios$1.spread = spread;
-
-// Expose isAxiosError
-axios$1.isAxiosError = isAxiosError;
-
-var axios_1 = axios$1;
-
-// Allow use of default import syntax in TypeScript
-var _default = axios$1;
-axios_1.default = _default;
-
-var axios = axios_1;
-
-/*
- This file is part of GNU Taler
- (C) 2019 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-
- SPDX-License-Identifier: AGPL3.0-or-later
-*/
-const logger$2 = new Logger("NodeHttpLib.ts");
-/**
- * Implementation of the HTTP request library interface for node.
- */
-class NodeHttpLib {
-    constructor() {
-        this.throttle = new RequestThrottler();
-        this.throttlingEnabled = true;
-    }
-    /**
-     * Set whether requests should be throttled.
-     */
-    setThrottling(enabled) {
-        this.throttlingEnabled = enabled;
-    }
-    async fetch(url, opt) {
-        var _a, _b;
-        const method = (_a = opt === null || opt === void 0 ? void 0 : 
opt.method) !== null && _a !== void 0 ? _a : "GET";
-        let body = opt === null || opt === void 0 ? void 0 : opt.body;
-        const parsedUrl = new URL$1(url);
-        if (this.throttlingEnabled && this.throttle.applyThrottle(url)) {
-            throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED, 
`request to origin ${parsedUrl.origin} was throttled`, {
-                requestMethod: method,
-                requestUrl: url,
-                throttleStats: this.throttle.getThrottleStats(url),
-            });
-        }
-        let timeout;
-        if (typeof ((_b = opt === null || opt === void 0 ? void 0 : 
opt.timeout) === null || _b === void 0 ? void 0 : _b.d_ms) === "number") {
-            timeout = opt.timeout.d_ms;
-        }
-        let resp;
-        try {
-            resp = await axios({
-                method,
-                url: url,
-                responseType: "arraybuffer",
-                headers: opt === null || opt === void 0 ? void 0 : opt.headers,
-                validateStatus: () => true,
-                transformResponse: (x) => x,
-                data: body,
-                timeout,
-            });
-        }
-        catch (e) {
-            throw 
OperationFailedError.fromCode(TalerErrorCode.WALLET_NETWORK_ERROR, 
`${e.message}`, {
-                requestUrl: url,
-                requestMethod: method,
-            });
-        }
-        const makeText = async () => {
-            const respText = new Uint8Array(resp.data);
-            return bytesToString(respText);
-        };
-        const makeJson = async () => {
-            let responseJson;
-            const respText = await makeText();
-            try {
-                responseJson = JSON.parse(respText);
-            }
-            catch (e) {
-                logger$2.trace(`invalid json: '${resp.data}'`);
-                throw new 
OperationFailedError(makeErrorDetails(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
 "invalid JSON", {
-                    httpStatusCode: resp.status,
-                    requestUrl: url,
-                    requestMethod: method,
-                }));
-            }
-            if (responseJson === null || typeof responseJson !== "object") {
-                logger$2.trace(`invalid json (not an object): '${respText}'`);
-                throw new 
OperationFailedError(makeErrorDetails(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
 "invalid JSON", {
-                    httpStatusCode: resp.status,
-                    requestUrl: url,
-                    requestMethod: method,
-                }));
-            }
-            return responseJson;
-        };
-        const makeBytes = async () => {
-            if (typeof resp.data.byteLength !== "number") {
-                throw Error("expected array buffer");
-            }
-            const buf = resp.data;
-            return buf;
-        };
-        const headers = new Headers();
-        for (const hn of Object.keys(resp.headers)) {
-            headers.set(hn, resp.headers[hn]);
-        }
-        return {
-            requestUrl: url,
-            requestMethod: method,
-            headers,
-            status: resp.status,
-            text: makeText,
-            json: makeJson,
-            bytes: makeBytes,
-        };
-    }
-    async get(url, opt) {
-        return this.fetch(url, Object.assign({ method: "GET" }, opt));
-    }
-    async postJson(url, body, opt) {
-        return this.fetch(url, Object.assign({ method: "POST", body }, opt));
-    }
-}
-
-/*
- Copyright 2019 Florian Dold
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- or implied. See the License for the specific language governing
- permissions and limitations under the License.
- */
-/** @public */
-var ResultLevel;
-(function (ResultLevel) {
-    ResultLevel[ResultLevel["OnlyCount"] = 0] = "OnlyCount";
-    ResultLevel[ResultLevel["OnlyKeys"] = 1] = "OnlyKeys";
-    ResultLevel[ResultLevel["Full"] = 2] = "Full";
-})(ResultLevel || (ResultLevel = {}));
-/** @public */
-var StoreLevel;
-(function (StoreLevel) {
-    StoreLevel[StoreLevel["NoOverwrite"] = 0] = "NoOverwrite";
-    StoreLevel[StoreLevel["AllowOverwrite"] = 1] = "AllowOverwrite";
-    StoreLevel[StoreLevel["UpdateExisting"] = 2] = "UpdateExisting";
-})(StoreLevel || (StoreLevel = {}));
-
-/*
- Copyright 2017 Jeremy Scheff
- Copyright 20201 Taler Systems SA
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- or implied. See the License for the specific language governing
- permissions and limitations under the License.
-*/
-/**
- * Check that a key could be injected into a value.
- *
- * 
https://www.w3.org/TR/IndexedDB/#check-that-a-key-could-be-injected-into-a-value
- */
-function canInjectKey(keyPath, value) {
-    if (Array.isArray(keyPath)) {
-        throw new Error("The key paths used in this section are always strings 
and never sequences, since it is not possible to create a object store which 
has a key generator and also has a key path that is a sequence.");
-    }
-    const identifiers = keyPath.split(".");
-    if (identifiers.length === 0) {
-        throw new Error("Assert: identifiers is not empty");
-    }
-    identifiers.pop();
-    for (const identifier of identifiers) {
-        if (value === null ||
-            (typeof value !== "object" && !Array.isArray(value))) {
-            return false;
-        }
-        const hop = value.hasOwnProperty(identifier);
-        if (!hop) {
-            return true;
-        }
-        value = value[identifier];
-    }
-    return value !== null && (typeof value === "object" || 
Array.isArray(value));
-}
-
-/*
- Copyright 2017 Jeremy Scheff
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- or implied. See the License for the specific language governing
- permissions and limitations under the License.
- */
-/* tslint:disable: max-classes-per-file max-line-length */
-const messages = {
-    AbortError: "A request was aborted, for example through a call to 
IDBTransaction.abort.",
-    ConstraintError: "A mutation operation in the transaction failed because a 
constraint was not satisfied. For example, an object such as an object store or 
index already exists and a request attempted to create a new one.",
-    DataCloneError: "The data being stored could not be cloned by the internal 
structured cloning algorithm.",
-    DataError: "Data provided to an operation does not meet requirements.",
-    InvalidAccessError: "An invalid operation was performed on an object. For 
example transaction creation attempt was made, but an empty scope was 
provided.",
-    InvalidStateError: "An operation was called on an object on which it is 
not allowed or at a time when it is not allowed. Also occurs if a request is 
made on a source object that has been deleted or removed. Use 
TransactionInactiveError or ReadOnlyError when possible, as they are more 
specific variations of InvalidStateError.",
-    NotFoundError: "The operation failed because the requested database object 
could not be found. For example, an object store did not exist but was being 
opened.",
-    ReadOnlyError: 'The mutating operation was attempted in a "readonly" 
transaction.',
-    TransactionInactiveError: "A request was placed against a transaction 
which is currently not active, or which is finished.",
-    VersionError: "An attempt was made to open a database using a lower 
version than the existing version.",
-};
-class AbortError extends Error {
-    constructor(message = messages.AbortError) {
-        super();
-        Object.setPrototypeOf(this, ConstraintError.prototype);
-        this.name = "AbortError";
-        this.message = message;
-    }
-}
-class ConstraintError extends Error {
-    constructor(message = messages.ConstraintError) {
-        super();
-        Object.setPrototypeOf(this, ConstraintError.prototype);
-        this.name = "ConstraintError";
-        this.message = message;
-    }
-}
-class DataCloneError extends Error {
-    constructor(message = messages.DataCloneError) {
-        super();
-        Object.setPrototypeOf(this, DataCloneError.prototype);
-        this.name = "DataCloneError";
-        this.message = message;
-    }
-}
-class DataError extends Error {
-    constructor(message = messages.DataError) {
-        super();
-        Object.setPrototypeOf(this, DataError.prototype);
-        this.name = "DataError";
-        this.message = message;
-    }
-}
-class InvalidAccessError extends Error {
-    constructor(message = messages.InvalidAccessError) {
-        super();
-        Object.setPrototypeOf(this, InvalidAccessError.prototype);
-        this.name = "InvalidAccessError";
-        this.message = message;
-    }
-}
-class InvalidStateError extends Error {
-    constructor(message = messages.InvalidStateError) {
-        super();
-        Object.setPrototypeOf(this, InvalidStateError.prototype);
-        this.name = "InvalidStateError";
-        this.message = message;
-    }
-}
-class NotFoundError extends Error {
-    constructor(message = messages.NotFoundError) {
-        super();
-        Object.setPrototypeOf(this, NotFoundError.prototype);
-        this.name = "NotFoundError";
-        this.message = message;
-    }
-}
-class ReadOnlyError extends Error {
-    constructor(message = messages.ReadOnlyError) {
-        super();
-        Object.setPrototypeOf(this, ReadOnlyError.prototype);
-        this.name = "ReadOnlyError";
-        this.message = message;
-    }
-}
-class TransactionInactiveError extends Error {
-    constructor(message = messages.TransactionInactiveError) {
-        super();
-        Object.setPrototypeOf(this, TransactionInactiveError.prototype);
-        this.name = "TransactionInactiveError";
-        this.message = message;
-    }
-}
-class VersionError extends Error {
-    constructor(message = messages.VersionError) {
-        super();
-        Object.setPrototypeOf(this, VersionError.prototype);
-        this.name = "VersionError";
-        this.message = message;
-    }
-}
-
-/*
- Copyright 2017 Jeremy Scheff
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- or implied. See the License for the specific language governing
- permissions and limitations under the License.
- */
-// https://www.w3.org/TR/IndexedDB-2/#convert-a-value-to-a-key
-function valueToKey(input, seen) {
-    if (typeof input === "number") {
-        if (isNaN(input)) {
-            throw new DataError();
-        }
-        return input;
-    }
-    else if (input instanceof Date) {
-        const ms = input.valueOf();
-        if (isNaN(ms)) {
-            throw new DataError();
-        }
-        return new Date(ms);
-    }
-    else if (typeof input === "string") {
-        return input;
-    }
-    else if (input instanceof ArrayBuffer ||
-        (typeof ArrayBuffer !== "undefined" &&
-            ArrayBuffer.isView &&
-            ArrayBuffer.isView(input))) {
-        if (input instanceof ArrayBuffer) {
-            return new Uint8Array(input).buffer;
-        }
-        return new Uint8Array(input.buffer).buffer;
-    }
-    else if (Array.isArray(input)) {
-        if (seen === undefined) {
-            seen = new Set();
-        }
-        else if (seen.has(input)) {
-            throw new DataError();
-        }
-        seen.add(input);
-        const keys = [];
-        for (let i = 0; i < input.length; i++) {
-            const hop = input.hasOwnProperty(i);
-            if (!hop) {
-                throw new DataError();
-            }
-            const entry = input[i];
-            const key = valueToKey(entry, seen);
-            keys.push(key);
-        }
-        return keys;
-    }
-    else {
-        throw new DataError();
-    }
-}
-
-/*
- Copyright 2017 Jeremy Scheff
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- or implied. See the License for the specific language go