gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] branch master updated: add config for


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated: add config for code formatter (prettier)
Date: Wed, 26 Jun 2019 15:22:30 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 0e1836ae add config for code formatter (prettier)
0e1836ae is described below

commit 0e1836aefffce4e636d2b5f1bfacf7c47c5843d1
Author: Florian Dold <address@hidden>
AuthorDate: Wed Jun 26 15:22:24 2019 +0200

    add config for code formatter (prettier)
---
 .prettierrc                              |    6 +
 packages/idb-bridge/src/MemoryBackend.ts |    2 -
 src/crypto/cryptoApi.ts                  |  147 +--
 src/crypto/cryptoWorker.ts               |  299 ++++---
 src/crypto/emscInterface.ts              |   32 +-
 src/wallet.ts                            | 1436 +++++++++++++++++++-----------
 6 files changed, 1224 insertions(+), 698 deletions(-)

diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..50063da9
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,6 @@
+{
+  "trailingComma": "all",
+  "tabWidth": 2,
+  "semi": true,
+  "singleQuote": false
+}
\ No newline at end of file
diff --git a/packages/idb-bridge/src/MemoryBackend.ts 
b/packages/idb-bridge/src/MemoryBackend.ts
index bd9b8996..1a85a739 100644
--- a/packages/idb-bridge/src/MemoryBackend.ts
+++ b/packages/idb-bridge/src/MemoryBackend.ts
@@ -881,8 +881,6 @@ export class MemoryBackend implements Backend {
       // if requested.
       if (req.resultLevel === ResultLevel.Full) {
         for (let i = 0; i < numResults; i++) {
-          console.log("getting value for index", i);
-          console.log("with key", primaryKeys[i]);
           const result = storeData.get(primaryKeys[i]);
           if (!result) {
             throw Error("invariant violated");
diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts
index 03c2a675..43a3bc22 100644
--- a/src/crypto/cryptoApi.ts
+++ b/src/crypto/cryptoApi.ts
@@ -14,7 +14,6 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-
 /**
  * API to access the Taler crypto worker thread.
  * @author Florian Dold
@@ -35,22 +34,14 @@ import {
   WireFee,
 } from "../dbTypes";
 
-import {
-  ContractTerms,
-  PaybackRequest,
-} from "../talerTypes";
+import { ContractTerms, PaybackRequest } from "../talerTypes";
 
-import {
-  BenchmarkResult,
-  CoinWithDenom,
-  PayCoinInfo,
-} from "../walletTypes";
+import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
 
 import * as timer from "../timer";
 
 import { startWorker } from "./startWorker";
 
-
 /**
  * State of a crypto worker.
  */
@@ -58,17 +49,17 @@ interface WorkerState {
   /**
    * The actual worker thread.
    */
-  w: Worker|null;
+  w: Worker | null;
 
   /**
    * Work we're currently executing or null if not busy.
    */
-  currentWorkItem: WorkItem|null;
+  currentWorkItem: WorkItem | null;
 
   /**
    * Timer to terminate the worker if it's not busy enough.
    */
-  terminationTimerHandle: timer.TimerHandle|null;
+  terminationTimerHandle: timer.TimerHandle | null;
 }
 
 interface WorkItem {
@@ -88,7 +79,6 @@ interface WorkItem {
   startTime: number;
 }
 
-
 /**
  * Number of different priorities. Each priority p
  * must be 0 <= p < NUM_PRIO.
@@ -151,8 +141,10 @@ export class CryptoApi {
 
   handleWorkerError(ws: WorkerState, e: ErrorEvent) {
     if (ws.currentWorkItem) {
-      console.error(`error in worker during ${ws.currentWorkItem!.operation}`,
-                    e);
+      console.error(
+        `error in worker during ${ws.currentWorkItem!.operation}`,
+        e,
+      );
     } else {
       console.error("error in worker", e);
     }
@@ -201,7 +193,10 @@ export class CryptoApi {
       console.error(`RPC with id ${id} has no registry entry`);
       return;
     }
-    console.log(`rpc ${currentWorkItem.operation} took 
${timer.performanceNow() - currentWorkItem.startTime}ms`);
+    console.log(
+      `rpc ${currentWorkItem.operation} took ${timer.performanceNow() -
+        currentWorkItem.startTime}ms`,
+    );
     currentWorkItem.resolve(msg.data.result);
   }
 
@@ -230,12 +225,21 @@ export class CryptoApi {
     }
   }
 
-  private doRpc<T>(operation: string, priority: number,
-                   ...args: any[]): Promise<T> {
-
+  private doRpc<T>(
+    operation: string,
+    priority: number,
+    ...args: any[]
+  ): Promise<T> {
     const p: Promise<T> = new Promise<T>((resolve, reject) => {
       const rpcId = this.nextRpcId++;
-      const workItem: WorkItem = {operation, args, resolve, reject, rpcId, 
startTime: 0};
+      const workItem: WorkItem = {
+        operation,
+        args,
+        resolve,
+        reject,
+        rpcId,
+        startTime: 0,
+      };
 
       if (this.numBusy === this.workers.length) {
         const q = this.workQueues[priority];
@@ -263,8 +267,10 @@ export class CryptoApi {
     });
   }
 
-
-  createPreCoin(denom: DenominationRecord, reserve: ReserveRecord): 
Promise<PreCoinRecord> {
+  createPreCoin(
+    denom: DenominationRecord,
+    reserve: ReserveRecord,
+  ): Promise<PreCoinRecord> {
     return this.doRpc<PreCoinRecord>("createPreCoin", 1, denom, reserve);
   }
 
@@ -280,27 +286,48 @@ export class CryptoApi {
     return this.doRpc<string>("hashDenomPub", 1, denomPub);
   }
 
-  isValidDenom(denom: DenominationRecord,
-               masterPub: string): Promise<boolean> {
+  isValidDenom(denom: DenominationRecord, masterPub: string): Promise<boolean> 
{
     return this.doRpc<boolean>("isValidDenom", 2, denom, masterPub);
   }
 
-  isValidWireFee(type: string, wf: WireFee, masterPub: string): 
Promise<boolean> {
+  isValidWireFee(
+    type: string,
+    wf: WireFee,
+    masterPub: string,
+  ): Promise<boolean> {
     return this.doRpc<boolean>("isValidWireFee", 2, type, wf, masterPub);
   }
 
-  isValidPaymentSignature(sig: string, contractHash: string, merchantPub: 
string): Promise<boolean> {
-    return this.doRpc<boolean>("isValidPaymentSignature", 1, sig, 
contractHash, merchantPub);
+  isValidPaymentSignature(
+    sig: string,
+    contractHash: string,
+    merchantPub: string,
+  ): Promise<boolean> {
+    return this.doRpc<boolean>(
+      "isValidPaymentSignature",
+      1,
+      sig,
+      contractHash,
+      merchantPub,
+    );
   }
 
-  signDeposit(contractTerms: ContractTerms,
-              cds: CoinWithDenom[],
-              totalAmount: AmountJson): Promise<PayCoinInfo> {
-    return this.doRpc<PayCoinInfo>("signDeposit", 3, contractTerms, cds, 
totalAmount);
+  signDeposit(
+    contractTerms: ContractTerms,
+    cds: CoinWithDenom[],
+    totalAmount: AmountJson,
+  ): Promise<PayCoinInfo> {
+    return this.doRpc<PayCoinInfo>(
+      "signDeposit",
+      3,
+      contractTerms,
+      cds,
+      totalAmount,
+    );
   }
 
-  createEddsaKeypair(): Promise<{priv: string, pub: string}> {
-    return this.doRpc<{priv: string, pub: string}>("createEddsaKeypair", 1);
+  createEddsaKeypair(): Promise<{ priv: string; pub: string }> {
+    return this.doRpc<{ priv: string; pub: string }>("createEddsaKeypair", 1);
   }
 
   rsaUnblind(sig: string, bk: string, pk: string): Promise<string> {
@@ -311,23 +338,43 @@ export class CryptoApi {
     return this.doRpc<PaybackRequest>("createPaybackRequest", 1, coin);
   }
 
-  createRefreshSession(exchangeBaseUrl: string,
-                       kappa: number,
-                       meltCoin: CoinRecord,
-                       newCoinDenoms: DenominationRecord[],
-                       meltFee: AmountJson): Promise<RefreshSessionRecord> {
-    return this.doRpc<RefreshSessionRecord>("createRefreshSession",
-                      4,
-                      exchangeBaseUrl,
-                      kappa,
-                      meltCoin,
-                      newCoinDenoms,
-                      meltFee);
+  createRefreshSession(
+    exchangeBaseUrl: string,
+    kappa: number,
+    meltCoin: CoinRecord,
+    newCoinDenoms: DenominationRecord[],
+    meltFee: AmountJson,
+  ): Promise<RefreshSessionRecord> {
+    return this.doRpc<RefreshSessionRecord>(
+      "createRefreshSession",
+      4,
+      exchangeBaseUrl,
+      kappa,
+      meltCoin,
+      newCoinDenoms,
+      meltFee,
+    );
+  }
+
+  signCoinLink(
+    oldCoinPriv: string,
+    newDenomHash: string,
+    oldCoinPub: string,
+    transferPub: string,
+    coinEv: string,
+  ): Promise<string> {
+    return this.doRpc<string>(
+      "signCoinLink",
+      4,
+      oldCoinPriv,
+      newDenomHash,
+      oldCoinPub,
+      transferPub,
+      coinEv,
+    );
   }
 
   benchmark(repetitions: number): Promise<BenchmarkResult> {
-    return this.doRpc<BenchmarkResult>("benchmark",
-                      1,
-                      repetitions);
+    return this.doRpc<BenchmarkResult>("benchmark", 1, repetitions);
   }
 }
diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts
index 5013e3ac..9c5263a6 100644
--- a/src/crypto/cryptoWorker.ts
+++ b/src/crypto/cryptoWorker.ts
@@ -18,7 +18,6 @@
  * Web worker for crypto operations.
  */
 
-
 /**
  * Imports.
  */
@@ -39,17 +38,9 @@ import {
   WireFee,
 } from "../dbTypes";
 
-import {
-  CoinPaySig,
-  ContractTerms,
-  PaybackRequest,
-} from "../talerTypes";
+import { CoinPaySig, ContractTerms, PaybackRequest } from "../talerTypes";
 
-import {
-  BenchmarkResult,
-  CoinWithDenom,
-  PayCoinInfo,
-} from "../walletTypes";
+import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
 
 import { canonicalJson } from "../helpers";
 
@@ -64,15 +55,15 @@ import {
 } from "./emscInterface";
 import * as native from "./emscInterface";
 
-
 namespace RpcFunctions {
-
   /**
    * Create a pre-coin of the given denomination to be withdrawn from then 
given
    * reserve.
    */
-  export function createPreCoin(denom: DenominationRecord,
-                                reserve: ReserveRecord): PreCoinRecord {
+  export function createPreCoin(
+    denom: DenominationRecord,
+    reserve: ReserveRecord,
+  ): PreCoinRecord {
     const reservePriv = new native.EddsaPrivateKey();
     reservePriv.loadCrock(reserve.reserve_priv);
     const reservePub = new native.EddsaPublicKey();
@@ -125,7 +116,6 @@ namespace RpcFunctions {
     return preCoin;
   }
 
-
   /**
    * Create a planchet used for tipping, including the private keys.
    */
@@ -152,12 +142,14 @@ namespace RpcFunctions {
       coinPub: coinPub.toCrock(),
       coinValue: denom.value,
       denomPub: denomPub.encode().toCrock(),
-      denomPubHash: denomPub.encode().hash().toCrock(),
+      denomPubHash: denomPub
+        .encode()
+        .hash()
+        .toCrock(),
     };
     return tipPlanchet;
   }
 
-
   /**
    * Create and sign a message to request payback for a coin.
    */
@@ -165,7 +157,9 @@ namespace RpcFunctions {
     const p = new native.PaybackRequestPS({
       coin_blind: native.RsaBlindingKeySecret.fromCrock(coin.blindingKey),
       coin_pub: native.EddsaPublicKey.fromCrock(coin.coinPub),
-      h_denom_pub: 
native.RsaPublicKey.fromCrock(coin.denomPub).encode().hash(),
+      h_denom_pub: native.RsaPublicKey.fromCrock(coin.denomPub)
+        .encode()
+        .hash(),
     });
     const coinPriv = native.EddsaPrivateKey.fromCrock(coin.coinPriv);
     const coinSig = native.eddsaSign(p.toPurpose(), coinPriv);
@@ -179,63 +173,83 @@ namespace RpcFunctions {
     return paybackRequest;
   }
 
-
   /**
    * Check if a payment signature is valid.
    */
-  export function isValidPaymentSignature(sig: string, contractHash: string, 
merchantPub: string): boolean {
+  export function isValidPaymentSignature(
+    sig: string,
+    contractHash: string,
+    merchantPub: string,
+  ): boolean {
     const p = new native.PaymentSignaturePS({
       contract_hash: native.HashCode.fromCrock(contractHash),
     });
     const nativeSig = new native.EddsaSignature();
     nativeSig.loadCrock(sig);
     const nativePub = native.EddsaPublicKey.fromCrock(merchantPub);
-    return native.eddsaVerify(native.SignaturePurpose.MERCHANT_PAYMENT_OK,
-                              p.toPurpose(),
-                              nativeSig,
-                              nativePub);
+    return native.eddsaVerify(
+      native.SignaturePurpose.MERCHANT_PAYMENT_OK,
+      p.toPurpose(),
+      nativeSig,
+      nativePub,
+    );
   }
 
   /**
    * Check if a wire fee is correctly signed.
    */
-  export function isValidWireFee(type: string, wf: WireFee, masterPub: 
string): boolean {
+  export function isValidWireFee(
+    type: string,
+    wf: WireFee,
+    masterPub: string,
+  ): boolean {
     const p = new native.MasterWireFeePS({
-      closing_fee: (new native.Amount(wf.closingFee)).toNbo(),
+      closing_fee: new native.Amount(wf.closingFee).toNbo(),
       end_date: native.AbsoluteTimeNbo.fromStampSeconds(wf.endStamp),
       h_wire_method: native.ByteArray.fromStringWithNull(type).hash(),
       start_date: native.AbsoluteTimeNbo.fromStampSeconds(wf.startStamp),
-      wire_fee: (new native.Amount(wf.wireFee)).toNbo(),
+      wire_fee: new native.Amount(wf.wireFee).toNbo(),
     });
 
     const nativeSig = new native.EddsaSignature();
     nativeSig.loadCrock(wf.sig);
     const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
 
-    return native.eddsaVerify(native.SignaturePurpose.MASTER_WIRE_FEES,
-                              p.toPurpose(),
-                              nativeSig,
-                              nativePub);
+    return native.eddsaVerify(
+      native.SignaturePurpose.MASTER_WIRE_FEES,
+      p.toPurpose(),
+      nativeSig,
+      nativePub,
+    );
   }
 
-
   /**
    * Check if the signature of a denomination is valid.
    */
-  export function isValidDenom(denom: DenominationRecord,
-                               masterPub: string): boolean {
+  export function isValidDenom(
+    denom: DenominationRecord,
+    masterPub: string,
+  ): boolean {
     const p = new native.DenominationKeyValidityPS({
-      denom_hash: native.RsaPublicKey.fromCrock(denom.denomPub) .encode() 
.hash(),
-      expire_legal: 
native.AbsoluteTimeNbo.fromTalerString(denom.stampExpireLegal),
-      expire_spend: 
native.AbsoluteTimeNbo.fromTalerString(denom.stampExpireDeposit),
-      expire_withdraw: 
native.AbsoluteTimeNbo.fromTalerString(denom.stampExpireWithdraw),
-      fee_deposit: (new native.Amount(denom.feeDeposit)).toNbo(),
-      fee_refresh: (new native.Amount(denom.feeRefresh)).toNbo(),
-      fee_refund: (new native.Amount(denom.feeRefund)).toNbo(),
-      fee_withdraw: (new native.Amount(denom.feeWithdraw)).toNbo(),
+      denom_hash: native.RsaPublicKey.fromCrock(denom.denomPub)
+        .encode()
+        .hash(),
+      expire_legal: native.AbsoluteTimeNbo.fromTalerString(
+        denom.stampExpireLegal,
+      ),
+      expire_spend: native.AbsoluteTimeNbo.fromTalerString(
+        denom.stampExpireDeposit,
+      ),
+      expire_withdraw: native.AbsoluteTimeNbo.fromTalerString(
+        denom.stampExpireWithdraw,
+      ),
+      fee_deposit: new native.Amount(denom.feeDeposit).toNbo(),
+      fee_refresh: new native.Amount(denom.feeRefresh).toNbo(),
+      fee_refund: new native.Amount(denom.feeRefund).toNbo(),
+      fee_withdraw: new native.Amount(denom.feeWithdraw).toNbo(),
       master: native.EddsaPublicKey.fromCrock(masterPub),
       start: native.AbsoluteTimeNbo.fromTalerString(denom.stampStart),
-      value: (new native.Amount(denom.value)).toNbo(),
+      value: new native.Amount(denom.value).toNbo(),
     });
 
     const nativeSig = new native.EddsaSignature();
@@ -243,42 +257,44 @@ namespace RpcFunctions {
 
     const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
 
-    return 
native.eddsaVerify(native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
-                              p.toPurpose(),
-                              nativeSig,
-                              nativePub);
-
+    return native.eddsaVerify(
+      native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
+      p.toPurpose(),
+      nativeSig,
+      nativePub,
+    );
   }
 
-
   /**
    * Create a new EdDSA key pair.
    */
-  export function createEddsaKeypair(): {priv: string, pub: string} {
+  export function createEddsaKeypair(): { priv: string; pub: string } {
     const priv = native.EddsaPrivateKey.create();
     const pub = priv.getPublicKey();
-    return {priv: priv.toCrock(), pub: pub.toCrock()};
+    return { priv: priv.toCrock(), pub: pub.toCrock() };
   }
 
-
   /**
    * Unblind a blindly signed value.
    */
   export function rsaUnblind(sig: string, bk: string, pk: string): string {
-    const denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(sig),
-                                     native.RsaBlindingKeySecret.fromCrock(bk),
-                                     native.RsaPublicKey.fromCrock(pk));
+    const denomSig = native.rsaUnblind(
+      native.RsaSignature.fromCrock(sig),
+      native.RsaBlindingKeySecret.fromCrock(bk),
+      native.RsaPublicKey.fromCrock(pk),
+    );
     return denomSig.encode().toCrock();
   }
 
-
   /**
    * Generate updated coins (to store in the database)
    * and deposit permissions for each given coin.
    */
-  export function signDeposit(contractTerms: ContractTerms,
-                              cds: CoinWithDenom[],
-                              totalAmount: AmountJson): PayCoinInfo {
+  export function signDeposit(
+    contractTerms: ContractTerms,
+    cds: CoinWithDenom[],
+    totalAmount: AmountJson,
+  ): PayCoinInfo {
     const ret: PayCoinInfo = {
       originalCoins: [],
       sigs: [],
@@ -287,17 +303,21 @@ namespace RpcFunctions {
 
     const contractTermsHash = hashString(canonicalJson(contractTerms));
 
-    const feeList: AmountJson[] = cds.map((x) => x.denom.feeDeposit);
-    let fees = Amounts.add(Amounts.getZero(feeList[0].currency), 
...feeList).amount;
+    const feeList: AmountJson[] = cds.map(x => x.denom.feeDeposit);
+    let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList)
+      .amount;
     // okay if saturates
-    fees = Amounts.sub(fees, 
Amounts.parseOrThrow(contractTerms.max_fee)).amount;
+    fees = Amounts.sub(fees, Amounts.parseOrThrow(contractTerms.max_fee))
+      .amount;
     const total = Amounts.add(fees, totalAmount).amount;
 
-    const amountSpent = 
native.Amount.getZero(cds[0].coin.currentAmount.currency);
+    const amountSpent = native.Amount.getZero(
+      cds[0].coin.currentAmount.currency,
+    );
     const amountRemaining = new native.Amount(total);
     for (const cd of cds) {
       let coinSpend: Amount;
-      const originalCoin = { ...(cd.coin) };
+      const originalCoin = { ...cd.coin };
 
       if (amountRemaining.value === 0 && amountRemaining.fraction === 0) {
         break;
@@ -332,13 +352,20 @@ namespace RpcFunctions {
         h_contract: native.HashCode.fromCrock(contractTermsHash),
         h_wire: native.HashCode.fromCrock(contractTerms.H_wire),
         merchant: native.EddsaPublicKey.fromCrock(contractTerms.merchant_pub),
-        refund_deadline: 
native.AbsoluteTimeNbo.fromTalerString(contractTerms.refund_deadline),
-        timestamp: 
native.AbsoluteTimeNbo.fromTalerString(contractTerms.timestamp),
+        refund_deadline: native.AbsoluteTimeNbo.fromTalerString(
+          contractTerms.refund_deadline,
+        ),
+        timestamp: native.AbsoluteTimeNbo.fromTalerString(
+          contractTerms.timestamp,
+        ),
       });
 
-      const coinSig = native.eddsaSign(d.toPurpose(),
-                                     
native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv))
-                          .toCrock();
+      const coinSig = native
+        .eddsaSign(
+          d.toPurpose(),
+          native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv),
+        )
+        .toCrock();
 
       const s: CoinPaySig = {
         coin_pub: cd.coin.coinPub,
@@ -355,22 +382,21 @@ namespace RpcFunctions {
     return ret;
   }
 
-
   /**
    * Create a new refresh session.
    */
-  export function createRefreshSession(exchangeBaseUrl: string,
-                                       kappa: number,
-                                       meltCoin: CoinRecord,
-                                       newCoinDenoms: DenominationRecord[],
-                                       meltFee: AmountJson): 
RefreshSessionRecord {
-
+  export function createRefreshSession(
+    exchangeBaseUrl: string,
+    kappa: number,
+    meltCoin: CoinRecord,
+    newCoinDenoms: DenominationRecord[],
+    meltFee: AmountJson,
+  ): RefreshSessionRecord {
     let valueWithFee = Amounts.getZero(newCoinDenoms[0].value.currency);
 
     for (const ncd of newCoinDenoms) {
-      valueWithFee = Amounts.add(valueWithFee,
-                                 ncd.value,
-                                 ncd.feeWithdraw).amount;
+      valueWithFee = Amounts.add(valueWithFee, ncd.value, ncd.feeWithdraw)
+        .amount;
     }
 
     // melt fee
@@ -397,12 +423,11 @@ namespace RpcFunctions {
     }
 
     sessionHc.read(native.EddsaPublicKey.fromCrock(meltCoin.coinPub));
-    sessionHc.read((new native.Amount(valueWithFee)).toNbo());
+    sessionHc.read(new native.Amount(valueWithFee).toNbo());
 
     for (let i = 0; i < kappa; i++) {
       const preCoins: RefreshPreCoinRecord[] = [];
       for (let j = 0; j < newCoinDenoms.length; j++) {
-
         const transferPriv = 
native.EcdhePrivateKey.fromCrock(transferPrivs[i]);
         const oldCoinPub = native.EddsaPublicKey.fromCrock(meltCoin.coinPub);
         const transferSecret = native.ecdhEddsa(transferPriv, oldCoinPub);
@@ -413,10 +438,10 @@ namespace RpcFunctions {
         const coinPub = coinPriv.getPublicKey();
         const blindingFactor = fresh.blindingKey;
         const pubHash: native.HashCode = coinPub.hash();
-        const denomPub = 
native.RsaPublicKey.fromCrock(newCoinDenoms[j].denomPub);
-        const ev = native.rsaBlind(pubHash,
-                                 blindingFactor,
-                                 denomPub);
+        const denomPub = native.RsaPublicKey.fromCrock(
+          newCoinDenoms[j].denomPub,
+        );
+        const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
         if (!ev) {
           throw Error("couldn't blind (malicious exchange key?)");
         }
@@ -437,16 +462,18 @@ namespace RpcFunctions {
     sessionHc.finish(sessionHash);
 
     const confirmData = new RefreshMeltCoinAffirmationPS({
-      amount_with_fee: (new Amount(valueWithFee)).toNbo(),
+      amount_with_fee: new Amount(valueWithFee).toNbo(),
       coin_pub: EddsaPublicKey.fromCrock(meltCoin.coinPub),
-      melt_fee: (new Amount(meltFee)).toNbo(),
+      melt_fee: new Amount(meltFee).toNbo(),
       session_hash: sessionHash,
     });
 
-
-    const confirmSig: string = native.eddsaSign(confirmData.toPurpose(),
-                                                
native.EddsaPrivateKey.fromCrock(
-                                                meltCoin.coinPriv)).toCrock();
+    const confirmSig: string = native
+      .eddsaSign(
+        confirmData.toPurpose(),
+        native.EddsaPrivateKey.fromCrock(meltCoin.coinPriv),
+      )
+      .toCrock();
 
     let valueOutput = Amounts.getZero(newCoinDenoms[0].value.currency);
     for (const denom of newCoinDenoms) {
@@ -459,8 +486,8 @@ namespace RpcFunctions {
       finished: false,
       hash: sessionHash.toCrock(),
       meltCoinPub: meltCoin.coinPub,
-      newDenomHashes: newCoinDenoms.map((d) => d.denomPubHash),
-      newDenoms: newCoinDenoms.map((d) => d.denomPub),
+      newDenomHashes: newCoinDenoms.map(d => d.denomPubHash),
+      newDenoms: newCoinDenoms.map(d => d.denomPub),
       norevealIndex: undefined,
       preCoinsForGammas,
       transferPrivs,
@@ -484,7 +511,33 @@ namespace RpcFunctions {
    * Hash a denomination public key.
    */
   export function hashDenomPub(denomPub: string): string {
-    return native.RsaPublicKey.fromCrock(denomPub).encode().hash().toCrock();
+    return native.RsaPublicKey.fromCrock(denomPub)
+      .encode()
+      .hash()
+      .toCrock();
+  }
+
+  export function signCoinLink(
+    oldCoinPriv: string,
+    newDenomHash: string,
+    oldCoinPub: string,
+    transferPub: string,
+    coinEv: string,
+  ): string {
+    const coinEvHash = native.ByteArray.fromCrock(coinEv).hash();
+
+    const coinLink = new native.CoinLinkSignaturePS({
+      coin_envelope_hash: coinEvHash,
+      h_denom_pub: native.HashCode.fromCrock(newDenomHash),
+      old_coin_pub: native.EddsaPublicKey.fromCrock(oldCoinPub),
+      transfer_pub: native.EcdhePublicKey.fromCrock(transferPub),
+    });
+
+    const coinPriv = native.EddsaPrivateKey.fromCrock(oldCoinPriv);
+
+    const sig = native.eddsaSign(coinLink.toPurpose(), coinPriv);
+
+    return sig.toCrock();
   }
 
   export function benchmark(repetitions: number): BenchmarkResult {
@@ -500,7 +553,7 @@ namespace RpcFunctions {
     for (let i = 0; i < repetitions; i++) {
       ba.randomize(native.RandomQuality.WEAK);
       const start = timer.performanceNow();
-      ba.hash(); 
+      ba.hash();
       time_hash_big += timer.performanceNow() - start;
     }
 
@@ -508,7 +561,7 @@ namespace RpcFunctions {
     for (let i = 0; i < repetitions; i++) {
       const start = timer.performanceNow();
       const priv: native.EddsaPrivateKey = native.EddsaPrivateKey.create();
-      time_eddsa_create +=  timer.performanceNow() - start;
+      time_eddsa_create += timer.performanceNow() - start;
       priv.destroy();
     }
 
@@ -541,14 +594,15 @@ namespace RpcFunctions {
       priv.destroy();
     }
 
-
     let time_eddsa_verify = 0;
     for (let i = 0; i < repetitions; i++) {
       const start = timer.performanceNow();
-      native.eddsaVerify(native.SignaturePurpose.MERCHANT_PAYMENT_OK,
-                         p,
-                         eddsaSig,
-                         eddsaPub);
+      native.eddsaVerify(
+        native.SignaturePurpose.MERCHANT_PAYMENT_OK,
+        p,
+        eddsaSig,
+        eddsaPub,
+      );
       time_eddsa_verify += timer.performanceNow() - start;
     }
 
@@ -564,11 +618,18 @@ namespace RpcFunctions {
       time_rsa_2048_blind += timer.performanceNow() - start;
     }
 
-    const blindedMessage2048 = native.rsaBlind(h, blindingSecret2048, 
rsaPub2048);
+    const blindedMessage2048 = native.rsaBlind(
+      h,
+      blindingSecret2048,
+      rsaPub2048,
+    );
     if (!blindedMessage2048) {
       throw Error("should not happen");
     }
-    const rsaBlindSig2048 = native.rsaSignBlinded(rsaPriv2048, 
blindedMessage2048);
+    const rsaBlindSig2048 = native.rsaSignBlinded(
+      rsaPriv2048,
+      blindedMessage2048,
+    );
 
     let time_rsa_2048_unblind = 0;
     for (let i = 0; i < repetitions; i++) {
@@ -577,7 +638,11 @@ namespace RpcFunctions {
       time_rsa_2048_unblind += timer.performanceNow() - start;
     }
 
-    const unblindedSig2048 = native.rsaUnblind(rsaBlindSig2048, 
blindingSecret2048, rsaPub2048);
+    const unblindedSig2048 = native.rsaUnblind(
+      rsaBlindSig2048,
+      blindingSecret2048,
+      rsaPub2048,
+    );
 
     let time_rsa_2048_verify = 0;
     for (let i = 0; i < repetitions; i++) {
@@ -586,7 +651,6 @@ namespace RpcFunctions {
       time_rsa_2048_verify += timer.performanceNow() - start;
     }
 
-
     /* rsa 4096 */
 
     let time_rsa_4096_blind = 0;
@@ -599,11 +663,18 @@ namespace RpcFunctions {
       time_rsa_4096_blind += timer.performanceNow() - start;
     }
 
-    const blindedMessage4096 = native.rsaBlind(h, blindingSecret4096, 
rsaPub4096);
+    const blindedMessage4096 = native.rsaBlind(
+      h,
+      blindingSecret4096,
+      rsaPub4096,
+    );
     if (!blindedMessage4096) {
       throw Error("should not happen");
     }
-    const rsaBlindSig4096 = native.rsaSignBlinded(rsaPriv4096, 
blindedMessage4096);
+    const rsaBlindSig4096 = native.rsaSignBlinded(
+      rsaPriv4096,
+      blindedMessage4096,
+    );
 
     let time_rsa_4096_unblind = 0;
     for (let i = 0; i < repetitions; i++) {
@@ -612,7 +683,11 @@ namespace RpcFunctions {
       time_rsa_4096_unblind += timer.performanceNow() - start;
     }
 
-    const unblindedSig4096 = native.rsaUnblind(rsaBlindSig4096, 
blindingSecret4096, rsaPub4096);
+    const unblindedSig4096 = native.rsaUnblind(
+      rsaBlindSig4096,
+      blindingSecret4096,
+      rsaPub4096,
+    );
 
     let time_rsa_4096_verify = 0;
     for (let i = 0; i < repetitions; i++) {
@@ -621,7 +696,6 @@ namespace RpcFunctions {
       time_rsa_4096_verify += timer.performanceNow() - start;
     }
 
-
     return {
       repetitions,
       time: {
@@ -637,12 +711,11 @@ namespace RpcFunctions {
         rsa_4096_blind: time_rsa_4096_blind,
         rsa_4096_unblind: time_rsa_4096_unblind,
         rsa_4096_verify: time_rsa_4096_verify,
-      }
+      },
     };
   }
 }
 
-
 const worker: Worker = (self as any) as Worker;
 
 worker.onmessage = (msg: MessageEvent) => {
@@ -665,7 +738,7 @@ worker.onmessage = (msg: MessageEvent) => {
   console.log("onmessage with", msg.data.operation);
   console.log("foo");
 
-  emscLoader.getLib().then((p) => {
+  emscLoader.getLib().then(p => {
     const lib = p.lib;
     if (!native.isInitialized()) {
       console.log("initializing emscripten for then first time with lib");
diff --git a/src/crypto/emscInterface.ts b/src/crypto/emscInterface.ts
index dcd16e63..2ddc15a3 100644
--- a/src/crypto/emscInterface.ts
+++ b/src/crypto/emscInterface.ts
@@ -223,6 +223,7 @@ export enum SignaturePurpose {
   MERCHANT_PAYMENT_OK = 1104,
   MASTER_WIRE_FEES = 1028,
   WALLET_COIN_PAYBACK = 1203,
+  WALLET_COIN_LINK = 1204,
 }
 
 
@@ -970,7 +971,7 @@ abstract class SignatureStruct {
       throw Error(`Key ${name} not found`);
     }
     if (!(value instanceof typemap[name])) {
-      throw Error("Wrong type for ${name}");
+      throw Error(`Wrong type for ${name}`);
     }
     this.members[name] = value;
   }
@@ -1293,6 +1294,35 @@ export class DepositRequestPS extends SignatureStruct {
   }
 }
 
+
+interface CoinLinkSignaturePS_args {
+  h_denom_pub: HashCode;
+  old_coin_pub: EddsaPublicKey;
+  transfer_pub: EcdhePublicKey;
+  coin_envelope_hash: HashCode;
+}
+
+
+export class CoinLinkSignaturePS extends SignatureStruct {
+  constructor(w: CoinLinkSignaturePS_args) {
+    super(w);
+  }
+
+  purpose() {
+    return SignaturePurpose.WALLET_COIN_LINK;
+  }
+
+  fieldTypes() {
+    return [
+      ["h_denom_pub", HashCode],
+      ["old_coin_pub", EddsaPublicKey],
+      ["transfer_pub", EcdhePublicKey],
+      ["coin_envelope_hash", HashCode],
+    ];
+  }
+}
+
+
 /**
  * Arguments for constuctor of [[DenominationKeyValidityPS]].
  */
diff --git a/src/wallet.ts b/src/wallet.ts
index 4c7e8c18..0dfb7755 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -30,10 +30,7 @@ import {
   getTalerStampSec,
   strcmp,
 } from "./helpers";
-import {
-  HttpRequestLibrary,
-  RequestException,
-} from "./http";
+import { HttpRequestLibrary, RequestException } from "./http";
 import * as LibtoolVersion from "./libtoolVersion";
 import {
   AbortTransaction,
@@ -109,7 +106,6 @@ import {
   WalletBalanceEntry,
 } from "./walletTypes";
 
-
 interface SpeculativePayData {
   payCoinInfo: PayCoinInfo;
   exchangeUrl: string;
@@ -117,7 +113,6 @@ interface SpeculativePayData {
   proposal: ProposalDownloadRecord;
 }
 
-
 /**
  * Wallet protocol version spoken with the exchange
  * and merchant.
@@ -132,7 +127,7 @@ const builtinCurrencies: CurrencyRecord[] = [
       {
         auditorPub: "BW9DC48PHQY4NH011SHHX36DZZ3Q22Y6X7FZ1VD1CMZ2PTFZ6PN0",
         baseUrl: "https://auditor.demo.taler.net/";,
-        expirationStamp: (new Date(2027, 1)).getTime(),
+        expirationStamp: new Date(2027, 1).getTime(),
       },
     ],
     exchanges: [],
@@ -141,9 +136,8 @@ const builtinCurrencies: CurrencyRecord[] = [
   },
 ];
 
-
 function isWithdrawableDenom(d: DenominationRecord) {
-  const nowSec = (new Date()).getTime() / 1000;
+  const nowSec = new Date().getTime() / 1000;
   const stampWithdrawSec = getTalerStampSec(d.stampExpireWithdraw);
   if (stampWithdrawSec === null) {
     return false;
@@ -153,19 +147,17 @@ function isWithdrawableDenom(d: DenominationRecord) {
     return false;
   }
   // Withdraw if still possible to withdraw within a minute
-  if ((stampWithdrawSec + 60 > nowSec) && (nowSec >= stampStartSec)) {
+  if (stampWithdrawSec + 60 > nowSec && nowSec >= stampStartSec) {
     return true;
   }
   return false;
 }
 
-
 interface SelectPayCoinsResult {
   cds: CoinWithDenom[];
   totalFees: AmountJson;
 }
 
-
 /**
  * Get the amount that we lose when refreshing a coin of the given denomination
  * with a certain amount left.
@@ -177,38 +169,54 @@ interface SelectPayCoinsResult {
  * Considers refresh fees, withdrawal fees after refresh and amounts too small
  * to refresh.
  */
-export function getTotalRefreshCost(denoms: DenominationRecord[],
-                                    refreshedDenom: DenominationRecord,
-                                    amountLeft: AmountJson): AmountJson {
-  const withdrawAmount = Amounts.sub(amountLeft, 
refreshedDenom.feeRefresh).amount;
+export function getTotalRefreshCost(
+  denoms: DenominationRecord[],
+  refreshedDenom: DenominationRecord,
+  amountLeft: AmountJson,
+): AmountJson {
+  const withdrawAmount = Amounts.sub(amountLeft, refreshedDenom.feeRefresh)
+    .amount;
   const withdrawDenoms = getWithdrawDenomList(withdrawAmount, denoms);
-  const resultingAmount = Amounts.add(Amounts.getZero(withdrawAmount.currency),
-                                      ...withdrawDenoms.map((d) => 
d.value)).amount;
+  const resultingAmount = Amounts.add(
+    Amounts.getZero(withdrawAmount.currency),
+    ...withdrawDenoms.map(d => d.value),
+  ).amount;
   const totalCost = Amounts.sub(amountLeft, resultingAmount).amount;
-  console.log("total refresh cost for", amountToPretty(amountLeft), "is", 
amountToPretty(totalCost));
+  console.log(
+    "total refresh cost for",
+    amountToPretty(amountLeft),
+    "is",
+    amountToPretty(totalCost),
+  );
   return totalCost;
 }
 
-
 /**
  * Select coins for a payment under the merchant's constraints.
  *
  * @param denoms all available denoms, used to compute refresh fees
  */
-export function selectPayCoins(denoms: DenominationRecord[], cds: 
CoinWithDenom[], paymentAmount: AmountJson,
-                               depositFeeLimit: AmountJson): 
SelectPayCoinsResult|undefined {
+export function selectPayCoins(
+  denoms: DenominationRecord[],
+  cds: CoinWithDenom[],
+  paymentAmount: AmountJson,
+  depositFeeLimit: AmountJson,
+): SelectPayCoinsResult | undefined {
   if (cds.length === 0) {
     return undefined;
   }
   // Sort by ascending deposit fee and denomPub if deposit fee is the same
   // (to guarantee deterministic results)
-  cds.sort((o1, o2) => Amounts.cmp(o1.denom.feeDeposit, o2.denom.feeDeposit) ||
-                                     strcmp(o1.denom.denomPub, 
o2.denom.denomPub));
+  cds.sort(
+    (o1, o2) =>
+      Amounts.cmp(o1.denom.feeDeposit, o2.denom.feeDeposit) ||
+      strcmp(o1.denom.denomPub, o2.denom.denomPub),
+  );
   const currency = cds[0].denom.value.currency;
   const cdsResult: CoinWithDenom[] = [];
   let accDepositFee: AmountJson = Amounts.getZero(currency);
   let accAmount: AmountJson = Amounts.getZero(currency);
-  for (const {coin, denom} of cds) {
+  for (const { coin, denom } of cds) {
     if (coin.suspended) {
       continue;
     }
@@ -218,20 +226,32 @@ export function selectPayCoins(denoms: 
DenominationRecord[], cds: CoinWithDenom[
     if (Amounts.cmp(denom.feeDeposit, coin.currentAmount) >= 0) {
       continue;
     }
-    cdsResult.push({coin, denom});
+    cdsResult.push({ coin, denom });
     accDepositFee = Amounts.add(denom.feeDeposit, accDepositFee).amount;
-    let leftAmount = Amounts.sub(coin.currentAmount, 
Amounts.sub(paymentAmount, accAmount).amount).amount;
+    let leftAmount = Amounts.sub(
+      coin.currentAmount,
+      Amounts.sub(paymentAmount, accAmount).amount,
+    ).amount;
     accAmount = Amounts.add(coin.currentAmount, accAmount).amount;
     const coversAmount = Amounts.cmp(accAmount, paymentAmount) >= 0;
-    const coversAmountWithFee = Amounts.cmp(accAmount,
-                                      Amounts.add(paymentAmount,
-                                                  denom.feeDeposit).amount) >= 
0;
+    const coversAmountWithFee =
+      Amounts.cmp(
+        accAmount,
+        Amounts.add(paymentAmount, denom.feeDeposit).amount,
+      ) >= 0;
     const isBelowFee = Amounts.cmp(accDepositFee, depositFeeLimit) <= 0;
 
-    console.log("coin selection", { coversAmount, isBelowFee, accDepositFee, 
accAmount, paymentAmount });
+    console.log("coin selection", {
+      coversAmount,
+      isBelowFee,
+      accDepositFee,
+      accAmount,
+      paymentAmount,
+    });
 
     if ((coversAmount && isBelowFee) || coversAmountWithFee) {
-      const depositFeeToCover = Amounts.sub(accDepositFee, 
depositFeeLimit).amount;
+      const depositFeeToCover = Amounts.sub(accDepositFee, depositFeeLimit)
+        .amount;
       leftAmount = Amounts.sub(leftAmount, depositFeeToCover).amount;
       console.log("deposit fee to cover", amountToPretty(depositFeeToCover));
 
@@ -241,21 +261,25 @@ export function selectPayCoins(denoms: 
DenominationRecord[], cds: CoinWithDenom[
         // because the merchant doesn't cover them
         totalFees = Amounts.sub(depositFeeLimit, accDepositFee).amount;
       }
-      totalFees = Amounts.add(totalFees, getTotalRefreshCost(denoms, denom, 
leftAmount)).amount;
+      totalFees = Amounts.add(
+        totalFees,
+        getTotalRefreshCost(denoms, denom, leftAmount),
+      ).amount;
       return { cds: cdsResult, totalFees };
     }
   }
   return undefined;
 }
 
-
 /**
  * Get a list of denominations (with repetitions possible)
  * whose total value is as close as possible to the available
  * amount, but never larger.
  */
-function getWithdrawDenomList(amountAvailable: AmountJson,
-                              denoms: DenominationRecord[]): 
DenominationRecord[] {
+function getWithdrawDenomList(
+  amountAvailable: AmountJson,
+  denoms: DenominationRecord[],
+): DenominationRecord[] {
   let remaining = Amounts.copy(amountAvailable);
   const ds: DenominationRecord[] = [];
 
@@ -284,7 +308,6 @@ function getWithdrawDenomList(amountAvailable: AmountJson,
   return ds;
 }
 
-
 interface CoinsForPaymentArgs {
   allowedAuditors: Auditor[];
   allowedExchanges: ExchangeHandle[];
@@ -296,7 +319,6 @@ interface CoinsForPaymentArgs {
   wireMethod: string;
 }
 
-
 /**
  * The platform-independent wallet implementation.
  */
@@ -310,7 +332,7 @@ export class Wallet {
   private notifier: Notifier;
   private cryptoApi: CryptoApi;
   private processPreCoinConcurrent = 0;
-  private processPreCoinThrottle: {[url: string]: number} = {};
+  private processPreCoinThrottle: { [url: string]: number } = {};
   private timerGroup: TimerGroup;
   private speculativePayData: SpeculativePayData | undefined;
   private cachedNextUrl: { [fulfillmentUrl: string]: NextUrlResult } = {};
@@ -325,10 +347,12 @@ export class Wallet {
     return new QueryRoot(this.db);
   }
 
-  constructor(db: IDBDatabase,
-              http: HttpRequestLibrary,
-              badge: Badge,
-              notifier: Notifier) {
+  constructor(
+    db: IDBDatabase,
+    http: HttpRequestLibrary,
+    badge: Badge,
+    notifier: Notifier,
+  ) {
     this.db = db;
     this.http = http;
     this.badge = badge;
@@ -337,8 +361,8 @@ export class Wallet {
     this.timerGroup = new TimerGroup();
 
     const init = async () => {
-      await this.fillDefaults().catch((e) => console.log(e));
-      await this.collectGarbage().catch((e) => console.log(e));
+      await this.fillDefaults().catch(e => console.log(e));
+      await this.collectGarbage().catch(e => console.log(e));
       this.updateExchanges();
       this.resumePendingFromDb();
       this.timerGroup.every(1000 * 60 * 15, () => this.updateExchanges());
@@ -353,20 +377,17 @@ export class Wallet {
     };
     const onFalse = (r: QueryRoot) => {
       console.log("applying defaults");
-      r.put(Stores.config, {key: "currencyDefaultsApplied", value: true})
+      r.put(Stores.config, { key: "currencyDefaultsApplied", value: true })
         .putAll(Stores.currencies, builtinCurrencies)
         .finish();
     };
-    await (
-      this.q()
-          .iter(Stores.config)
-          .filter((x) => x.key === "currencyDefaultsApplied")
-          .first()
-          .cond((x) => x && x.value, onTrue, onFalse)
-    );
+    await this.q()
+      .iter(Stores.config)
+      .filter(x => x.key === "currencyDefaultsApplied")
+      .first()
+      .cond(x => x && x.value, onTrue, onFalse);
   }
 
-
   private startOperation(operationId: string) {
     this.runningOperations.add(operationId);
     this.badge.startBusy();
@@ -383,15 +404,14 @@ export class Wallet {
     console.log("updating exchanges");
 
     const exchangesUrls = await this.q()
-                                   .iter(Stores.exchanges)
-                                   .map((e) => e.baseUrl)
-                                   .toArray();
+      .iter(Stores.exchanges)
+      .map(e => e.baseUrl)
+      .toArray();
 
     for (const url of exchangesUrls) {
-      this.updateExchangeFromUrl(url)
-          .catch((e) => {
-            console.error("updating exchange failed", e);
-          });
+      this.updateExchangeFromUrl(url).catch(e => {
+        console.error("updating exchange failed", e);
+      });
     }
   }
 
@@ -403,68 +423,69 @@ export class Wallet {
     console.log("resuming pending operations from db");
 
     this.q()
-        .iter(Stores.reserves)
-        .forEach((reserve) => {
-          console.log("resuming reserve", reserve.reserve_pub);
-          this.processReserve(reserve);
-        });
+      .iter(Stores.reserves)
+      .forEach(reserve => {
+        console.log("resuming reserve", reserve.reserve_pub);
+        this.processReserve(reserve);
+      });
 
     this.q()
-        .iter(Stores.precoins)
-        .forEach((preCoin) => {
-          console.log("resuming precoin");
-          this.processPreCoin(preCoin);
-        });
+      .iter(Stores.precoins)
+      .forEach(preCoin => {
+        console.log("resuming precoin");
+        this.processPreCoin(preCoin);
+      });
 
     this.q()
-        .iter(Stores.refresh)
-        .forEach((r: RefreshSessionRecord) => {
-          this.continueRefreshSession(r);
-        });
+      .iter(Stores.refresh)
+      .forEach((r: RefreshSessionRecord) => {
+        this.continueRefreshSession(r);
+      });
 
     this.q()
-        .iter(Stores.coinsReturns)
-        .forEach((r: CoinsReturnRecord) => {
-          this.depositReturnedCoins(r);
-        });
+      .iter(Stores.coinsReturns)
+      .forEach((r: CoinsReturnRecord) => {
+        this.depositReturnedCoins(r);
+      });
 
     // FIXME: optimize via index
     this.q()
-        .iter(Stores.coins)
-        .forEach((c: CoinRecord) => {
-          if (c.status === CoinStatus.Dirty) {
-            console.log("resuming pending refresh for coin", c);
-            this.refresh(c.coinPub);
-          }
-        });
+      .iter(Stores.coins)
+      .forEach((c: CoinRecord) => {
+        if (c.status === CoinStatus.Dirty) {
+          console.log("resuming pending refresh for coin", c);
+          this.refresh(c.coinPub);
+        }
+      });
   }
 
-
-  private async getCoinsForReturn(exchangeBaseUrl: string, amount: 
AmountJson): Promise<CoinWithDenom[] | undefined> {
+  private async getCoinsForReturn(
+    exchangeBaseUrl: string,
+    amount: AmountJson,
+  ): Promise<CoinWithDenom[] | undefined> {
     const exchange = await this.q().get(Stores.exchanges, exchangeBaseUrl);
     if (!exchange) {
       throw Error(`Exchange ${exchangeBaseUrl} not known to the wallet`);
     }
 
-    const coins: CoinRecord[] = await (
-      this.q()
-          .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
-          .toArray()
-    );
+    const coins: CoinRecord[] = await this.q()
+      .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
+      .toArray();
 
     if (!coins || !coins.length) {
       return [];
     }
 
-    const denoms = await 
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, 
exchange.baseUrl).toArray();
+    const denoms = await this.q()
+      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
+      .toArray();
 
     // Denomination of the first coin, we assume that all other
     // coins have the same currency
-    const firstDenom = await this.q().get(Stores.denominations,
-                                          [
-                                            exchange.baseUrl,
-                                            coins[0].denomPub,
-                                          ]);
+    const firstDenom = await this.q().get(Stores.denominations, [
+      exchange.baseUrl,
+      coins[0].denomPub,
+    ]);
     if (!firstDenom) {
       throw Error("db inconsistent");
     }
@@ -472,13 +493,19 @@ export class Wallet {
 
     const cds: CoinWithDenom[] = [];
     for (const coin of coins) {
-      const denom = await this.q().get(Stores.denominations,
-                                     [exchange.baseUrl, coin.denomPub]);
+      const denom = await this.q().get(Stores.denominations, [
+        exchange.baseUrl,
+        coin.denomPub,
+      ]);
       if (!denom) {
         throw Error("db inconsistent");
       }
       if (denom.value.currency !== currency) {
-        console.warn(`same pubkey for different currencies at exchange 
${exchange.baseUrl}`);
+        console.warn(
+          `same pubkey for different currencies at exchange ${
+            exchange.baseUrl
+          }`,
+        );
         continue;
       }
       if (coin.suspended) {
@@ -487,10 +514,10 @@ export class Wallet {
       if (coin.status !== CoinStatus.Fresh) {
         continue;
       }
-      cds.push({coin, denom});
+      cds.push({ coin, denom });
     }
 
-    console.log("coin return:  selecting from possible coins", { cds, amount } 
);
+    console.log("coin return:  selecting from possible coins", { cds, amount 
});
 
     const res = selectPayCoins(denoms, cds, amount, amount);
     if (res) {
@@ -499,12 +526,13 @@ export class Wallet {
     return undefined;
   }
 
-
   /**
    * Get exchanges and associated coins that are still spendable,
    * but only if the sum the coins' remaining value exceeds the payment amount.
    */
-  private async getCoinsForPayment(args: CoinsForPaymentArgs): 
Promise<CoinSelectionResult|undefined> {
+  private async getCoinsForPayment(
+    args: CoinsForPaymentArgs,
+  ): Promise<CoinSelectionResult | undefined> {
     const {
       allowedAuditors,
       allowedExchanges,
@@ -518,7 +546,9 @@ export class Wallet {
 
     let remainingAmount = paymentAmount;
 
-    const exchanges = await this.q().iter(Stores.exchanges).toArray();
+    const exchanges = await this.q()
+      .iter(Stores.exchanges)
+      .toArray();
 
     for (const exchange of exchanges) {
       let isOkay: boolean = false;
@@ -551,34 +581,40 @@ export class Wallet {
       }
 
       const coins: CoinRecord[] = await this.q()
-                                          
.iterIndex(Stores.coins.exchangeBaseUrlIndex,
-                                                     exchange.baseUrl)
-                                          .toArray();
-      const denoms = await 
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, 
exchange.baseUrl).toArray();
+        .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
+        .toArray();
+      const denoms = await this.q()
+        .iterIndex(Stores.denominations.exchangeBaseUrlIndex, 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 this.q().get(Stores.denominations,
-                                            [
-                                              exchange.baseUrl,
-                                              coins[0].denomPub,
-                                            ]);
+      const firstDenom = await this.q().get(Stores.denominations, [
+        exchange.baseUrl,
+        coins[0].denomPub,
+      ]);
       if (!firstDenom) {
         throw Error("db inconsistent");
       }
       const currency = firstDenom.value.currency;
       const cds: CoinWithDenom[] = [];
       for (const coin of coins) {
-        const denom = await this.q().get(Stores.denominations,
-                                       [exchange.baseUrl, coin.denomPub]);
+        const denom = await this.q().get(Stores.denominations, [
+          exchange.baseUrl,
+          coin.denomPub,
+        ]);
         if (!denom) {
           throw Error("db inconsistent");
         }
         if (denom.value.currency !== currency) {
-          console.warn(`same pubkey for different currencies at exchange 
${exchange.baseUrl}`);
+          console.warn(
+            `same pubkey for different currencies at exchange ${
+              exchange.baseUrl
+            }`,
+          );
           continue;
         }
         if (coin.suspended) {
@@ -587,10 +623,13 @@ export class Wallet {
         if (coin.status !== CoinStatus.Fresh) {
           continue;
         }
-        cds.push({coin, denom});
+        cds.push({ coin, denom });
       }
 
-      const fees = await this.q().get(Stores.exchangeWireFees, 
exchange.baseUrl);
+      const fees = await this.q().get(
+        Stores.exchangeWireFees,
+        exchange.baseUrl,
+      );
       if (!fees) {
         console.error("no fees found for exchange", exchange);
         continue;
@@ -603,8 +642,8 @@ export class Wallet {
       console.log("payment coins: wireFeeTime", wireFeeTime);
 
       let totalFees = Amounts.getZero(currency);
-      let wireFee: AmountJson|undefined;
-      for (const fee of (fees.feesForType[wireMethod] || [])) {
+      let wireFee: AmountJson | undefined;
+      for (const fee of fees.feesForType[wireMethod] || []) {
         if (fee.startStamp <= wireFeeTime && fee.endStamp >= wireFeeTime) {
           wireFee = fee.wireFee;
           break;
@@ -617,7 +656,8 @@ export class Wallet {
         const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization);
         if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) {
           totalFees = Amounts.add(amortizedWireFee, totalFees).amount;
-          remainingAmount = Amounts.add(amortizedWireFee, 
remainingAmount).amount;
+          remainingAmount = Amounts.add(amortizedWireFee, remainingAmount)
+            .amount;
         }
       }
 
@@ -635,14 +675,15 @@ export class Wallet {
     return undefined;
   }
 
-
   /**
    * Record all information that is necessary to
    * pay for a proposal in the wallet's database.
    */
-  private async recordConfirmPay(proposal: ProposalDownloadRecord,
-                                 payCoinInfo: PayCoinInfo,
-                                 chosenExchange: string): 
Promise<PurchaseRecord> {
+  private async recordConfirmPay(
+    proposal: ProposalDownloadRecord,
+    payCoinInfo: PayCoinInfo,
+    chosenExchange: string,
+  ): Promise<PurchaseRecord> {
     const payReq: PayReq = {
       coins: payCoinInfo.sigs,
       merchant_pub: proposal.contractTerms.merchant_pub,
@@ -661,26 +702,28 @@ export class Wallet {
       payReq,
       refundsDone: {},
       refundsPending: {},
-      timestamp: (new Date()).getTime(),
+      timestamp: new Date().getTime(),
       timestamp_refund: 0,
     };
 
     await this.q()
-              .put(Stores.purchases, t)
-              .putAll(Stores.coins, payCoinInfo.updatedCoins)
-              .finish();
+      .put(Stores.purchases, t)
+      .putAll(Stores.coins, payCoinInfo.updatedCoins)
+      .finish();
     this.badge.showNotification();
     this.notifier.notify();
     return t;
   }
 
-
   /**
    * Download a proposal and store it in the database.
    * Returns an id for it to retrieve it later.
    */
   async downloadProposal(url: string): Promise<number> {
-    const oldProposal = await this.q().getIndexed(Stores.proposals.urlIndex, 
url);
+    const oldProposal = await this.q().getIndexed(
+      Stores.proposals.urlIndex,
+      url,
+    );
     if (oldProposal) {
       return oldProposal.id!;
     }
@@ -691,7 +734,7 @@ export class Wallet {
     console.log("downloading contract from '" + urlWithNonce + "'");
     let resp;
     try {
-      resp = await axios.get(urlWithNonce, { validateStatus: (s) => s === 200 
});
+      resp = await axios.get(urlWithNonce, { validateStatus: s => s === 200 });
     } catch (e) {
       console.log("contract download failed", e);
       throw e;
@@ -707,7 +750,7 @@ export class Wallet {
       contractTermsHash,
       merchantSig: proposal.sig,
       noncePriv: priv,
-      timestamp: (new Date()).getTime(),
+      timestamp: new Date().getTime(),
       url,
     };
 
@@ -719,16 +762,21 @@ export class Wallet {
     return id;
   }
 
-
   async refundFailedPay(proposalId: number) {
     console.log(`refunding failed payment with proposal id ${proposalId}`);
-    const proposal: ProposalDownloadRecord|undefined = await 
this.q().get(Stores.proposals, proposalId);
+    const proposal: ProposalDownloadRecord | undefined = await this.q().get(
+      Stores.proposals,
+      proposalId,
+    );
 
     if (!proposal) {
       throw Error(`proposal with id ${proposalId} not found`);
     }
 
-    const purchase = await this.q().get(Stores.purchases, 
proposal.contractTermsHash);
+    const purchase = await this.q().get(
+      Stores.purchases,
+      proposal.contractTermsHash,
+    );
     if (!purchase) {
       throw Error("purchase not found for proposal");
     }
@@ -738,8 +786,10 @@ export class Wallet {
     }
   }
 
-
-  async submitPay(contractTermsHash: string, sessionId: string | undefined): 
Promise<ConfirmPayResult> {
+  async submitPay(
+    contractTermsHash: string,
+    sessionId: string | undefined,
+  ): Promise<ConfirmPayResult> {
     const purchase = await this.q().get(Stores.purchases, contractTermsHash);
     if (!purchase) {
       throw Error("Purchase not found: " + contractTermsHash);
@@ -753,7 +803,7 @@ export class Wallet {
     try {
       const config = {
         headers: { "Content-Type": "application/json;charset=UTF-8" },
-        timeout: 5000, /* 5 seconds */
+        timeout: 5000 /* 5 seconds */,
         validateStatus: (s: number) => s === 200,
       };
       resp = await axios.post(purchase.contractTerms.pay_url, payReq, config);
@@ -766,8 +816,10 @@ export class Wallet {
     console.log("got success from pay_url");
 
     const merchantPub = purchase.contractTerms.merchant_pub;
-    const valid: boolean = await (
-      this.cryptoApi.isValidPaymentSignature(merchantResp.sig, 
contractTermsHash, merchantPub)
+    const valid: boolean = await this.cryptoApi.isValidPaymentSignature(
+      merchantResp.sig,
+      contractTermsHash,
+      merchantPub,
     );
     if (!valid) {
       console.error("merchant payment signature invalid");
@@ -795,31 +847,44 @@ export class Wallet {
     }
 
     await this.q()
-              .putAll(Stores.coins, modifiedCoins)
-              .put(Stores.purchases, purchase)
-              .finish();
+      .putAll(Stores.coins, modifiedCoins)
+      .put(Stores.purchases, purchase)
+      .finish();
     for (const c of purchase.payReq.coins) {
       this.refresh(c.coin_pub);
     }
 
     const nextUrl = fu.href();
-    this.cachedNextUrl[purchase.contractTerms.fulfillment_url] = { nextUrl, 
lastSessionId: sessionId };
+    this.cachedNextUrl[purchase.contractTerms.fulfillment_url] = {
+      nextUrl,
+      lastSessionId: sessionId,
+    };
     return { nextUrl };
   }
 
-
   /**
    * Add a contract to the wallet and sign coins, and send them.
    */
-  async confirmPay(proposalId: number, sessionId: string | undefined): 
Promise<ConfirmPayResult> {
-    console.log(`executing confirmPay with proposalId ${proposalId} and 
sessionId ${sessionId}`);
-    const proposal: ProposalDownloadRecord|undefined = await 
this.q().get(Stores.proposals, proposalId);
+  async confirmPay(
+    proposalId: number,
+    sessionId: string | undefined,
+  ): Promise<ConfirmPayResult> {
+    console.log(
+      `executing confirmPay with proposalId ${proposalId} and sessionId 
${sessionId}`,
+    );
+    const proposal: ProposalDownloadRecord | undefined = await this.q().get(
+      Stores.proposals,
+      proposalId,
+    );
 
     if (!proposal) {
       throw Error(`proposal with id ${proposalId} not found`);
     }
 
-    let purchase = await this.q().get(Stores.purchases, 
proposal.contractTermsHash);
+    let purchase = await this.q().get(
+      Stores.purchases,
+      proposal.contractTermsHash,
+    );
 
     if (purchase) {
       return this.submitPay(purchase.contractTermsHash, sessionId);
@@ -857,20 +922,33 @@ export class Wallet {
     const sd = await this.getSpeculativePayData(proposalId);
     if (!sd) {
       const { exchangeUrl, cds, totalAmount } = res;
-      const payCoinInfo = await 
this.cryptoApi.signDeposit(proposal.contractTerms, cds, totalAmount);
-      purchase = await this.recordConfirmPay(proposal, payCoinInfo, 
exchangeUrl);
+      const payCoinInfo = await this.cryptoApi.signDeposit(
+        proposal.contractTerms,
+        cds,
+        totalAmount,
+      );
+      purchase = await this.recordConfirmPay(
+        proposal,
+        payCoinInfo,
+        exchangeUrl,
+      );
     } else {
-      purchase = await this.recordConfirmPay(sd.proposal, sd.payCoinInfo, 
sd.exchangeUrl);
+      purchase = await this.recordConfirmPay(
+        sd.proposal,
+        sd.payCoinInfo,
+        sd.exchangeUrl,
+      );
     }
 
     return this.submitPay(purchase.contractTermsHash, sessionId);
   }
 
-
   /**
    * Get the speculative pay data, but only if coins have not changed in 
between.
    */
-  async getSpeculativePayData(proposalId: number): Promise<SpeculativePayData 
| undefined> {
+  async getSpeculativePayData(
+    proposalId: number,
+  ): Promise<SpeculativePayData | undefined> {
     const sp = this.speculativePayData;
     if (!sp) {
       return;
@@ -888,14 +966,15 @@ export class Wallet {
       if (!currentCoin) {
         return;
       }
-      if (Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) !== 
0) {
+      if (
+        Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) !== 0
+      ) {
         return;
       }
     }
     return sp;
   }
 
-
   /**
    * Check if payment for an offer is possible, or if the offer has already
    * been payed for.
@@ -911,7 +990,10 @@ export class Wallet {
     }
 
     // First check if we already payed for it.
-    const purchase = await this.q().get(Stores.purchases, 
proposal.contractTermsHash);
+    const purchase = await this.q().get(
+      Stores.purchases,
+      proposal.contractTermsHash,
+    );
     if (purchase) {
       return { status: "paid" };
     }
@@ -943,9 +1025,17 @@ export class Wallet {
     }
 
     // Only create speculative signature if we don't already have one for this 
proposal
-    if ((!this.speculativePayData) || (this.speculativePayData && 
this.speculativePayData.proposalId !== proposalId)) {
+    if (
+      !this.speculativePayData ||
+      (this.speculativePayData &&
+        this.speculativePayData.proposalId !== proposalId)
+    ) {
       const { exchangeUrl, cds, totalAmount } = res;
-      const payCoinInfo = await 
this.cryptoApi.signDeposit(proposal.contractTerms, cds, totalAmount);
+      const payCoinInfo = await this.cryptoApi.signDeposit(
+        proposal.contractTerms,
+        cds,
+        totalAmount,
+      );
       this.speculativePayData = {
         exchangeUrl,
         payCoinInfo,
@@ -957,15 +1047,19 @@ export class Wallet {
     return { status: "payment-possible", coinSelection: res };
   }
 
-
   /**
    * Retrieve information required to pay for a contract, where the
    * contract is identified via the fulfillment url.
    */
-  async queryPaymentByFulfillmentUrl(url: string): Promise<PurchaseRecord | 
undefined> {
+  async queryPaymentByFulfillmentUrl(
+    url: string,
+  ): Promise<PurchaseRecord | undefined> {
     console.log("query for payment", url);
 
-    const t = await this.q().getIndexed(Stores.purchases.fulfillmentUrlIndex, 
url);
+    const t = await this.q().getIndexed(
+      Stores.purchases.fulfillmentUrlIndex,
+      url,
+    );
 
     if (!t) {
       console.log("query for payment failed");
@@ -975,13 +1069,14 @@ export class Wallet {
     return t;
   }
 
-
   /**
    * First fetch information requred to withdraw from the reserve,
    * then deplete the reserve, withdrawing coins until it is empty.
    */
-  private async processReserve(reserveRecord: ReserveRecord,
-                               retryDelayMs: number = 250): Promise<void> {
+  private async processReserve(
+    reserveRecord: ReserveRecord,
+    retryDelayMs: number = 250,
+  ): Promise<void> {
     const opId = "reserve-" + reserveRecord.reserve_pub;
     this.startOperation(opId);
 
@@ -990,38 +1085,54 @@ export class Wallet {
       await this.depleteReserve(reserve);
     } catch (e) {
       // random, exponential backoff truncated at 3 minutes
-      const nextDelay = Math.min(2 * retryDelayMs + retryDelayMs * 
Math.random(), 3000 * 60);
-      console.warn(`Failed to deplete reserve, trying again in ${retryDelayMs} 
ms`);
-      this.timerGroup.after(retryDelayMs, () => 
this.processReserve(reserveRecord, nextDelay));
+      const nextDelay = Math.min(
+        2 * retryDelayMs + retryDelayMs * Math.random(),
+        3000 * 60,
+      );
+      console.warn(
+        `Failed to deplete reserve, trying again in ${retryDelayMs} ms`,
+      );
+      this.timerGroup.after(retryDelayMs, () =>
+        this.processReserve(reserveRecord, nextDelay),
+      );
     } finally {
       this.stopOperation(opId);
     }
   }
 
-
   /**
    * Given a planchet, withdraw a coin from the exchange.
    */
-  private async processPreCoin(preCoin: PreCoinRecord,
-                               retryDelayMs = 200): Promise<void> {
+  private async processPreCoin(
+    preCoin: PreCoinRecord,
+    retryDelayMs = 200,
+  ): Promise<void> {
     // Throttle concurrent executions of this function, so we don't withdraw 
too many coins at once.
-    if (this.processPreCoinConcurrent >= 4 || 
this.processPreCoinThrottle[preCoin.exchangeBaseUrl]) {
+    if (
+      this.processPreCoinConcurrent >= 4 ||
+      this.processPreCoinThrottle[preCoin.exchangeBaseUrl]
+    ) {
       console.log("delaying processPreCoin");
-      this.timerGroup.after(retryDelayMs,
-                            () => this.processPreCoin(preCoin, 
Math.min(retryDelayMs * 2, 5 * 60 * 1000)));
+      this.timerGroup.after(retryDelayMs, () =>
+        this.processPreCoin(preCoin, Math.min(retryDelayMs * 2, 5 * 60 * 
1000)),
+      );
       return;
     }
     console.log("executing processPreCoin", preCoin);
     this.processPreCoinConcurrent++;
     try {
-      const exchange = await this.q().get(Stores.exchanges,
-                                          preCoin.exchangeBaseUrl);
+      const exchange = await this.q().get(
+        Stores.exchanges,
+        preCoin.exchangeBaseUrl,
+      );
       if (!exchange) {
         console.error("db inconsistent: exchange for precoin not found");
         return;
       }
-      const denom = await this.q().get(Stores.denominations,
-                                       [preCoin.exchangeBaseUrl, 
preCoin.denomPub]);
+      const denom = await this.q().get(Stores.denominations, [
+        preCoin.exchangeBaseUrl,
+        preCoin.denomPub,
+      ]);
       if (!denom) {
         console.error("db inconsistent: denom for precoin not found");
         return;
@@ -1031,11 +1142,17 @@ export class Wallet {
       console.log("processPreCoin: got coin", coin);
 
       const mutateReserve = (r: ReserveRecord) => {
+        console.log(
+          `before committing coin: current ${amountToPretty(
+            r.current_amount!,
+          )}, precoin: ${amountToPretty(r.precoin_amount)})}`,
+        );
 
-        console.log(`before committing coin: current 
${amountToPretty(r.current_amount!)}, precoin: ${amountToPretty(
-          r.precoin_amount)})}`);
-
-        const x = Amounts.sub(r.precoin_amount, preCoin.coinValue, 
denom.feeWithdraw);
+        const x = Amounts.sub(
+          r.precoin_amount,
+          preCoin.coinValue,
+          denom.feeWithdraw,
+        );
         if (x.saturated) {
           console.error("database inconsistent");
           throw AbortTransaction;
@@ -1045,15 +1162,20 @@ export class Wallet {
       };
 
       await this.q()
-                .mutate(Stores.reserves, preCoin.reservePub, mutateReserve)
-                .delete(Stores.precoins, coin.coinPub)
-                .add(Stores.coins, coin)
-                .finish();
+        .mutate(Stores.reserves, preCoin.reservePub, mutateReserve)
+        .delete(Stores.precoins, coin.coinPub)
+        .add(Stores.coins, coin)
+        .finish();
 
       if (coin.status === CoinStatus.TainedByTip) {
-        const tip = await this.q().getIndexed(Stores.tips.coinPubIndex, 
coin.coinPub);
+        const tip = await this.q().getIndexed(
+          Stores.tips.coinPubIndex,
+          coin.coinPub,
+        );
         if (!tip) {
-          throw Error(`inconsistent DB: tip for coin pub ${coin.coinPub} not 
found.`);
+          throw Error(
+            `inconsistent DB: tip for coin pub ${coin.coinPub} not found.`,
+          );
         }
 
         if (tip.accepted) {
@@ -1075,44 +1197,55 @@ export class Wallet {
 
       this.notifier.notify();
     } catch (e) {
-      console.error("Failed to withdraw coin from precoin, retrying in",
-                    retryDelayMs,
-                    "ms", e);
+      console.error(
+        "Failed to withdraw coin from precoin, retrying in",
+        retryDelayMs,
+        "ms",
+        e,
+      );
       // exponential backoff truncated at one minute
       const nextRetryDelayMs = Math.min(retryDelayMs * 2, 5 * 60 * 1000);
-      this.timerGroup.after(retryDelayMs, () => this.processPreCoin(preCoin, 
nextRetryDelayMs));
-
-      const currentThrottle = 
this.processPreCoinThrottle[preCoin.exchangeBaseUrl] || 0;
-      this.processPreCoinThrottle[preCoin.exchangeBaseUrl] = currentThrottle + 
1;
-      this.timerGroup.after(retryDelayMs, () => 
{this.processPreCoinThrottle[preCoin.exchangeBaseUrl]--; });
+      this.timerGroup.after(retryDelayMs, () =>
+        this.processPreCoin(preCoin, nextRetryDelayMs),
+      );
+
+      const currentThrottle =
+        this.processPreCoinThrottle[preCoin.exchangeBaseUrl] || 0;
+      this.processPreCoinThrottle[preCoin.exchangeBaseUrl] =
+        currentThrottle + 1;
+      this.timerGroup.after(retryDelayMs, () => {
+        this.processPreCoinThrottle[preCoin.exchangeBaseUrl]--;
+      });
     } finally {
       this.processPreCoinConcurrent--;
     }
   }
 
-
   /**
    * Update the timestamp of when an exchange was used.
    */
   async updateExchangeUsedTime(exchangeBaseUrl: string): Promise<void> {
-    const now = (new Date()).getTime();
+    const now = new Date().getTime();
     const update = (r: ExchangeRecord) => {
       r.lastUsedTime = now;
       return r;
     };
-    await this.q().mutate(Stores.exchanges, exchangeBaseUrl, update).finish();
+    await this.q()
+      .mutate(Stores.exchanges, exchangeBaseUrl, update)
+      .finish();
   }
 
-
   /**
    * 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 createReserve(req: CreateReserveRequest): 
Promise<CreateReserveResponse> {
+  async createReserve(
+    req: CreateReserveRequest,
+  ): Promise<CreateReserveResponse> {
     const keypair = await this.cryptoApi.createEddsaKeypair();
-    const now = (new Date()).getTime();
+    const now = new Date().getTime();
     const canonExchange = canonicalizeBaseUrl(req.exchange);
 
     const reserveRecord: ReserveRecord = {
@@ -1134,13 +1267,18 @@ export class Wallet {
       const rec = {
         paytoUri: senderWire,
       };
-      await this.q().put(Stores.senderWires, rec).finish();
+      await this.q()
+        .put(Stores.senderWires, rec)
+        .finish();
     }
 
     await this.updateExchangeUsedTime(req.exchange);
     const exchangeInfo = await this.updateExchangeFromUrl(req.exchange);
-    const {isAudited, isTrusted} = await this.getExchangeTrust(exchangeInfo);
-    let currencyRecord = await this.q().get(Stores.currencies, 
exchangeInfo.currency);
+    const { isAudited, isTrusted } = await this.getExchangeTrust(exchangeInfo);
+    let currencyRecord = await this.q().get(
+      Stores.currencies,
+      exchangeInfo.currency,
+    );
     if (!currencyRecord) {
       currencyRecord = {
         auditors: [],
@@ -1151,13 +1289,16 @@ export class Wallet {
     }
 
     if (!isAudited && !isTrusted) {
-      currencyRecord.exchanges.push({baseUrl: req.exchange, exchangePub: 
exchangeInfo.masterPublicKey});
+      currencyRecord.exchanges.push({
+        baseUrl: req.exchange,
+        exchangePub: exchangeInfo.masterPublicKey,
+      });
     }
 
     await this.q()
-              .put(Stores.currencies, currencyRecord)
-              .put(Stores.reserves, reserveRecord)
-              .finish();
+      .put(Stores.currencies, currencyRecord)
+      .put(Stores.reserves, reserveRecord)
+      .finish();
 
     const r: CreateReserveResponse = {
       exchange: canonExchange,
@@ -1166,7 +1307,6 @@ export class Wallet {
     return r;
   }
 
-
   /**
    * Mark an existing reserve as confirmed.  The wallet will start trying
    * to withdraw from that reserve.  This may not immediately succeed,
@@ -1177,10 +1317,10 @@ export class Wallet {
    * an unconfirmed reserve should be hidden.
    */
   async confirmReserve(req: ConfirmReserveRequest): Promise<void> {
-    const now = (new Date()).getTime();
-    const reserve: ReserveRecord|undefined = await (
-      this.q().get<ReserveRecord>(Stores.reserves,
-                                  req.reservePub));
+    const now = new Date().getTime();
+    const reserve: ReserveRecord | undefined = await this.q().get<
+      ReserveRecord
+    >(Stores.reserves, req.reservePub);
     if (!reserve) {
       console.error("Unable to confirm reserve, not found in DB");
       return;
@@ -1188,21 +1328,20 @@ export class Wallet {
     console.log("reserve confirmed");
     reserve.timestamp_confirmed = now;
     await this.q()
-              .put(Stores.reserves, reserve)
-              .finish();
+      .put(Stores.reserves, reserve)
+      .finish();
     this.notifier.notify();
 
     this.processReserve(reserve);
   }
 
-
   private async withdrawExecute(pc: PreCoinRecord): Promise<CoinRecord> {
     const wd: any = {};
     wd.denom_pub_hash = pc.denomPubHash;
     wd.reserve_pub = pc.reservePub;
     wd.reserve_sig = pc.withdrawSig;
     wd.coin_ev = pc.coinEv;
-    const reqUrl = (new 
URI("reserve/withdraw")).absoluteTo(pc.exchangeBaseUrl);
+    const reqUrl = new URI("reserve/withdraw").absoluteTo(pc.exchangeBaseUrl);
     const resp = await this.http.postJson(reqUrl.href(), wd);
 
     if (resp.status !== 200) {
@@ -1212,9 +1351,11 @@ export class Wallet {
       });
     }
     const r = JSON.parse(resp.responseText);
-    const denomSig = await this.cryptoApi.rsaUnblind(r.ev_sig,
-                                                     pc.blindingKey,
-                                                     pc.denomPub);
+    const denomSig = await this.cryptoApi.rsaUnblind(
+      r.ev_sig,
+      pc.blindingKey,
+      pc.denomPub,
+    );
     const coin: CoinRecord = {
       blindingKey: pc.blindingKey,
       coinPriv: pc.coinPriv,
@@ -1230,7 +1371,6 @@ export class Wallet {
     return coin;
   }
 
-
   /**
    * Withdraw coins from a reserve until it is empty.
    *
@@ -1246,23 +1386,32 @@ export class Wallet {
     if (!withdrawAmount) {
       throw Error("can't withdraw when amount is unknown");
     }
-    const denomsForWithdraw = await 
this.getVerifiedWithdrawDenomList(reserve.exchange_base_url, withdrawAmount);
-    const smallestAmount = await 
this.getVerifiedSmallestWithdrawAmount(reserve.exchange_base_url);
+    const denomsForWithdraw = await this.getVerifiedWithdrawDenomList(
+      reserve.exchange_base_url,
+      withdrawAmount,
+    );
+    const smallestAmount = await this.getVerifiedSmallestWithdrawAmount(
+      reserve.exchange_base_url,
+    );
 
     console.log(`withdrawing ${denomsForWithdraw.length} coins`);
 
-    const ps = denomsForWithdraw.map(async(denom) => {
+    const ps = denomsForWithdraw.map(async denom => {
       function mutateReserve(r: ReserveRecord): ReserveRecord {
         const currentAmount = r.current_amount;
         if (!currentAmount) {
           throw Error("can't withdraw when amount is unknown");
         }
-        r.precoin_amount = Amounts.add(r.precoin_amount,
-                                       denom.value,
-                                       denom.feeWithdraw).amount;
-        const result = Amounts.sub(currentAmount,
-                                   denom.value,
-                                   denom.feeWithdraw);
+        r.precoin_amount = Amounts.add(
+          r.precoin_amount,
+          denom.value,
+          denom.feeWithdraw,
+        ).amount;
+        const result = Amounts.sub(
+          currentAmount,
+          denom.value,
+          denom.feeWithdraw,
+        );
         if (result.saturated) {
           console.error("can't create precoin, saturated");
           throw AbortTransaction;
@@ -1271,39 +1420,44 @@ export class Wallet {
 
         // Reserve is depleted if the amount left is too small to withdraw
         if (Amounts.cmp(r.current_amount, smallestAmount) < 0) {
-          r.timestamp_depleted = (new Date()).getTime();
+          r.timestamp_depleted = new Date().getTime();
         }
 
-        console.log(`after creating precoin: current 
${amountToPretty(r.current_amount)}, precoin: ${amountToPretty(
-          r.precoin_amount)})}`);
+        console.log(
+          `after creating precoin: current ${amountToPretty(
+            r.current_amount,
+          )}, precoin: ${amountToPretty(r.precoin_amount)})}`,
+        );
 
         return r;
       }
 
-      const preCoin = await this.cryptoApi
-                              .createPreCoin(denom, reserve);
+      const preCoin = await this.cryptoApi.createPreCoin(denom, reserve);
       await this.q()
-                .put(Stores.precoins, preCoin)
-                .mutate(Stores.reserves, reserve.reserve_pub, mutateReserve);
+        .put(Stores.precoins, preCoin)
+        .mutate(Stores.reserves, reserve.reserve_pub, mutateReserve);
       await this.processPreCoin(preCoin);
     });
 
     await Promise.all(ps);
   }
 
-
   /**
    * Update the information about a reserve that is stored in the wallet
    * by quering the reserve's exchange.
    */
   private async updateReserve(reservePub: string): Promise<ReserveRecord> {
-    const reserve = await this.q()
-                            .get<ReserveRecord>(Stores.reserves, reservePub);
+    const reserve = await this.q().get<ReserveRecord>(
+      Stores.reserves,
+      reservePub,
+    );
     if (!reserve) {
       throw Error("reserve not in db");
     }
-    const reqUrl = new 
URI("reserve/status").absoluteTo(reserve.exchange_base_url);
-    reqUrl.query({reserve_pub: reservePub});
+    const reqUrl = new URI("reserve/status").absoluteTo(
+      reserve.exchange_base_url,
+    );
+    reqUrl.query({ reserve_pub: reservePub });
     const resp = await this.http.get(reqUrl.href());
     if (resp.status !== 200) {
       throw Error();
@@ -1314,13 +1468,12 @@ export class Wallet {
     }
     reserve.current_amount = Amounts.parseOrThrow(reserveInfo.balance);
     await this.q()
-              .put(Stores.reserves, reserve)
-              .finish();
+      .put(Stores.reserves, reserve)
+      .finish();
     this.notifier.notify();
     return reserve;
   }
 
-
   /**
    * Get the wire information for the exchange with the given base URL.
    */
@@ -1341,35 +1494,39 @@ export class Wallet {
     return ExchangeWireJson.checked(wiJson);
   }
 
-
   async getPossibleDenoms(exchangeBaseUrl: string) {
-    return (
-      this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex,
-                         exchangeBaseUrl)
-          .filter((d) => d.status === DenominationStatus.Unverified || 
d.status === DenominationStatus.VerifiedGood)
-          .toArray()
-    );
+    return this.q()
+      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeBaseUrl)
+      .filter(
+        d =>
+          d.status === DenominationStatus.Unverified ||
+          d.status === DenominationStatus.VerifiedGood,
+      )
+      .toArray();
   }
 
-
   /**
    * Compute the smallest withdrawable amount possible, based on verified 
denominations.
    *
    * Writes to the DB in order to record the result from verifying
    * denominations.
    */
-  async getVerifiedSmallestWithdrawAmount(exchangeBaseUrl: string): 
Promise<AmountJson> {
+  async getVerifiedSmallestWithdrawAmount(
+    exchangeBaseUrl: string,
+  ): Promise<AmountJson> {
     const exchange = await this.q().get(Stores.exchanges, exchangeBaseUrl);
     if (!exchange) {
       throw Error(`exchange ${exchangeBaseUrl} not found`);
     }
 
-    const possibleDenoms = await (
-      this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex,
-                         exchange.baseUrl)
-          .filter((d) => d.status === DenominationStatus.Unverified || 
d.status === DenominationStatus.VerifiedGood)
-          .toArray()
-    );
+    const possibleDenoms = await this.q()
+      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
+      .filter(
+        d =>
+          d.status === DenominationStatus.Unverified ||
+          d.status === DenominationStatus.VerifiedGood,
+      )
+      .toArray();
     possibleDenoms.sort((d1, d2) => {
       const a1 = Amounts.add(d1.feeWithdraw, d1.value).amount;
       const a2 = Amounts.add(d2.feeWithdraw, d2.value).amount;
@@ -1381,14 +1538,18 @@ export class Wallet {
         return Amounts.add(denom.feeWithdraw, denom.value).amount;
       }
       console.log(`verifying denom ${denom.denomPub.substr(0, 15)}`);
-      const valid = await this.cryptoApi.isValidDenom(denom,
-                                                      
exchange.masterPublicKey);
+      const valid = await this.cryptoApi.isValidDenom(
+        denom,
+        exchange.masterPublicKey,
+      );
       if (!valid) {
         denom.status = DenominationStatus.VerifiedBad;
       } else {
         denom.status = DenominationStatus.VerifiedGood;
       }
-      await this.q().put(Stores.denominations, denom).finish();
+      await this.q()
+        .put(Stores.denominations, denom)
+        .finish();
       if (valid) {
         return Amounts.add(denom.feeWithdraw, denom.value).amount;
       }
@@ -1396,7 +1557,6 @@ export class Wallet {
     return Amounts.getZero(exchange.currency);
   }
 
-
   /**
    * Get a list of denominations to withdraw from the given exchange for the
    * given amount, making sure that all denominations' signatures are verified.
@@ -1404,19 +1564,23 @@ export class Wallet {
    * Writes to the DB in order to record the result from verifying
    * denominations.
    */
-  async getVerifiedWithdrawDenomList(exchangeBaseUrl: string,
-                                     amount: AmountJson): 
Promise<DenominationRecord[]> {
+  async getVerifiedWithdrawDenomList(
+    exchangeBaseUrl: string,
+    amount: AmountJson,
+  ): Promise<DenominationRecord[]> {
     const exchange = await this.q().get(Stores.exchanges, exchangeBaseUrl);
     if (!exchange) {
       throw Error(`exchange ${exchangeBaseUrl} not found`);
     }
 
-    const possibleDenoms = await (
-      this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex,
-                         exchange.baseUrl)
-          .filter((d) => d.status === DenominationStatus.Unverified || 
d.status === DenominationStatus.VerifiedGood)
-          .toArray()
-    );
+    const possibleDenoms = await this.q()
+      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
+      .filter(
+        d =>
+          d.status === DenominationStatus.Unverified ||
+          d.status === DenominationStatus.VerifiedGood,
+      )
+      .toArray();
 
     let allValid = false;
 
@@ -1429,8 +1593,10 @@ export class Wallet {
       for (const denom of selectedDenoms || []) {
         if (denom.status === DenominationStatus.Unverified) {
           console.log(`verifying denom ${denom.denomPub.substr(0, 15)}`);
-          const valid = await this.cryptoApi.isValidDenom(denom,
-                                                        
exchange.masterPublicKey);
+          const valid = await this.cryptoApi.isValidDenom(
+            denom,
+            exchange.masterPublicKey,
+          );
           if (!valid) {
             denom.status = DenominationStatus.VerifiedBad;
             allValid = false;
@@ -1438,7 +1604,9 @@ export class Wallet {
             denom.status = DenominationStatus.VerifiedGood;
             nextPossibleDenoms.push(denom);
           }
-          await this.q().put(Stores.denominations, denom).finish();
+          await this.q()
+            .put(Stores.denominations, denom)
+            .finish();
         } else {
           nextPossibleDenoms.push(denom);
         }
@@ -1448,14 +1616,18 @@ export class Wallet {
     return selectedDenoms;
   }
 
-
   /**
    * Check if and how an exchange is trusted and/or audited.
    */
-  async getExchangeTrust(exchangeInfo: ExchangeRecord): Promise<{isTrusted: 
boolean, isAudited: boolean}> {
+  async getExchangeTrust(
+    exchangeInfo: ExchangeRecord,
+  ): Promise<{ isTrusted: boolean; isAudited: boolean }> {
     let isTrusted = false;
     let isAudited = false;
-    const currencyRecord = await this.q().get(Stores.currencies, 
exchangeInfo.currency);
+    const currencyRecord = await this.q().get(
+      Stores.currencies,
+      exchangeInfo.currency,
+    );
     if (currencyRecord) {
       for (const trustedExchange of currencyRecord.exchanges) {
         if (trustedExchange.exchangePub === exchangeInfo.masterPublicKey) {
@@ -1472,22 +1644,27 @@ export class Wallet {
         }
       }
     }
-    return {isTrusted, isAudited};
+    return { isTrusted, isAudited };
   }
 
-  async getReserveCreationInfo(baseUrl: string,
-                               amount: AmountJson): 
Promise<ReserveCreationInfo> {
+  async getReserveCreationInfo(
+    baseUrl: string,
+    amount: AmountJson,
+  ): Promise<ReserveCreationInfo> {
     const exchangeInfo = await this.updateExchangeFromUrl(baseUrl);
 
-    const selectedDenoms = await this.getVerifiedWithdrawDenomList(baseUrl,
-                                                                 amount);
+    const selectedDenoms = await this.getVerifiedWithdrawDenomList(
+      baseUrl,
+      amount,
+    );
     let acc = Amounts.getZero(amount.currency);
     for (const d of selectedDenoms) {
       acc = Amounts.add(acc, d.feeWithdraw).amount;
     }
     const actualCoinCost = selectedDenoms
-      .map((d: DenominationRecord) => Amounts.add(d.value,
-                                                  d.feeWithdraw).amount)
+      .map(
+        (d: DenominationRecord) => Amounts.add(d.value, d.feeWithdraw).amount,
+      )
       .reduce((a, b) => Amounts.add(a, b).amount);
 
     const wireInfo = await this.getWireInfo(baseUrl);
@@ -1503,7 +1680,7 @@ export class Wallet {
       exchangeWireAccounts.push(account.url);
     }
 
-    const {isTrusted, isAudited} = await this.getExchangeTrust(exchangeInfo);
+    const { isTrusted, isAudited } = await this.getExchangeTrust(exchangeInfo);
 
     let earliestDepositExpiration = Infinity;
     for (const denom of selectedDenoms) {
@@ -1513,23 +1690,35 @@ export class Wallet {
       }
     }
 
-    const possibleDenoms = await (
-      this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, baseUrl)
-          .filter((d) => d.isOffered)
-          .toArray()
-    ) || [];
+    const possibleDenoms =
+      (await this.q()
+        .iterIndex(Stores.denominations.exchangeBaseUrlIndex, baseUrl)
+        .filter(d => d.isOffered)
+        .toArray()) || [];
 
     const trustedAuditorPubs = [];
-    const currencyRecord = await 
this.q().get<CurrencyRecord>(Stores.currencies, amount.currency);
+    const currencyRecord = await this.q().get<CurrencyRecord>(
+      Stores.currencies,
+      amount.currency,
+    );
     if (currencyRecord) {
-      trustedAuditorPubs.push(...currencyRecord.auditors.map((a) => 
a.auditorPub));
+      trustedAuditorPubs.push(
+        ...currencyRecord.auditors.map(a => a.auditorPub),
+      );
     }
 
     let versionMatch;
     if (exchangeInfo.protocolVersion) {
-      versionMatch = LibtoolVersion.compare(WALLET_PROTOCOL_VERSION, 
exchangeInfo.protocolVersion);
-
-      if (versionMatch && !versionMatch.compatible && versionMatch.currentCmp 
=== -1) {
+      versionMatch = LibtoolVersion.compare(
+        WALLET_PROTOCOL_VERSION,
+        exchangeInfo.protocolVersion,
+      );
+
+      if (
+        versionMatch &&
+        !versionMatch.compatible &&
+        versionMatch.currentCmp === -1
+      ) {
         console.log("wallet version might be outdated, checking for updates");
         chrome.runtime.requestUpdateCheck((status, details) => {
           console.log("update check status:", status);
@@ -1556,7 +1745,6 @@ export class Wallet {
     return ret;
   }
 
-
   /**
    * Update or add exchange DB entry by fetching the /keys information.
    * Optionally link the reserve entry to the new or existing
@@ -1569,25 +1757,32 @@ export class Wallet {
     if (keysResp.status !== 200) {
       throw Error("/keys request failed");
     }
-    const exchangeKeysJson = 
KeysJson.checked(JSON.parse(keysResp.responseText));
+    const exchangeKeysJson = KeysJson.checked(
+      JSON.parse(keysResp.responseText),
+    );
     const exchangeWire = await this.getWireInfo(baseUrl);
     return this.updateExchangeFromJson(baseUrl, exchangeKeysJson, 
exchangeWire);
   }
 
-
   private async suspendCoins(exchangeInfo: ExchangeRecord): Promise<void> {
-    const resultSuspendedCoins = await (
-      this.q()
-          .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchangeInfo.baseUrl)
-          .indexJoinLeft(Stores.denominations.exchangeBaseUrlIndex,
-                         (e) => e.exchangeBaseUrl)
-          .fold((cd: JoinLeftResult<CoinRecord, DenominationRecord>,
-                 suspendedCoins: CoinRecord[]) => {
-            if ((!cd.right) || (!cd.right.isOffered)) {
-              return Array.prototype.concat(suspendedCoins, [cd.left]);
-            }
-            return Array.prototype.concat(suspendedCoins);
-          }, []));
+    const resultSuspendedCoins = await this.q()
+      .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchangeInfo.baseUrl)
+      .indexJoinLeft(
+        Stores.denominations.exchangeBaseUrlIndex,
+        e => e.exchangeBaseUrl,
+      )
+      .fold(
+        (
+          cd: JoinLeftResult<CoinRecord, DenominationRecord>,
+          suspendedCoins: CoinRecord[],
+        ) => {
+          if (!cd.right || !cd.right.isOffered) {
+            return Array.prototype.concat(suspendedCoins, [cd.left]);
+          }
+          return Array.prototype.concat(suspendedCoins);
+        },
+        [],
+      );
 
     const q = this.q();
     resultSuspendedCoins.map((c: CoinRecord) => {
@@ -1600,11 +1795,11 @@ export class Wallet {
     await q.finish();
   }
 
-
-  private async updateExchangeFromJson(baseUrl: string,
-                                       exchangeKeysJson: KeysJson,
-                                       wireMethodDetails: ExchangeWireJson): 
Promise<ExchangeRecord> {
-
+  private async updateExchangeFromJson(
+    baseUrl: string,
+    exchangeKeysJson: KeysJson,
+    wireMethodDetails: ExchangeWireJson,
+  ): Promise<ExchangeRecord> {
     // FIXME: all this should probably be commited atomically
     const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
     if (updateTimeSec === null) {
@@ -1625,7 +1820,8 @@ export class Wallet {
       exchangeInfo = {
         auditors: exchangeKeysJson.auditors,
         baseUrl,
-        currency: 
Amounts.parseOrThrow(exchangeKeysJson.denoms[0].value).currency,
+        currency: Amounts.parseOrThrow(exchangeKeysJson.denoms[0].value)
+          .currency,
         lastUpdateTime: updateTimeSec,
         lastUsedTime: 0,
         masterPublicKey: exchangeKeysJson.master_public_key,
@@ -1641,14 +1837,16 @@ export class Wallet {
       console.log("updating old exchange");
     }
 
-    const updatedExchangeInfo = await this.updateExchangeInfo(exchangeInfo,
-                                                              
exchangeKeysJson);
+    const updatedExchangeInfo = await this.updateExchangeInfo(
+      exchangeInfo,
+      exchangeKeysJson,
+    );
     await this.suspendCoins(updatedExchangeInfo);
     updatedExchangeInfo.protocolVersion = exchangeKeysJson.version;
 
     await this.q()
-              .put(Stores.exchanges, updatedExchangeInfo)
-              .finish();
+      .put(Stores.exchanges, updatedExchangeInfo)
+      .finish();
 
     let oldWireFees = await this.q().get(Stores.exchangeWireFees, baseUrl);
     if (!oldWireFees) {
@@ -1689,7 +1887,11 @@ export class Wallet {
           startStamp: start,
           wireFee: Amounts.parseOrThrow(fee.wire_fee),
         };
-        const valid: boolean = await 
this.cryptoApi.isValidWireFee(paytoTargetType, wf, 
exchangeInfo.masterPublicKey);
+        const valid: boolean = await this.cryptoApi.isValidWireFee(
+          paytoTargetType,
+          wf,
+          exchangeInfo.masterPublicKey,
+        );
         if (!valid) {
           console.error("fee signature invalid", fee);
           throw Error("fee signature invalid");
@@ -1702,12 +1904,17 @@ export class Wallet {
 
     if (exchangeKeysJson.payback) {
       for (const payback of exchangeKeysJson.payback) {
-        const denom = await 
this.q().getIndexed(Stores.denominations.denomPubHashIndex, 
payback.h_denom_pub);
+        const denom = await this.q().getIndexed(
+          Stores.denominations.denomPubHashIndex,
+          payback.h_denom_pub,
+        );
         if (!denom) {
           continue;
         }
         console.log(`cashing back denom`, denom);
-        const coins = await this.q().iterIndex(Stores.coins.denomPubIndex, 
denom.denomPub).toArray();
+        const coins = await this.q()
+          .iterIndex(Stores.coins.denomPubIndex, denom.denomPub)
+          .toArray();
         for (const coin of coins) {
           this.payback(coin.coinPub);
         }
@@ -1717,19 +1924,27 @@ export class Wallet {
     return updatedExchangeInfo;
   }
 
-
-  private async updateExchangeInfo(exchangeInfo: ExchangeRecord,
-                                   newKeys: KeysJson): Promise<ExchangeRecord> 
{
+  private async updateExchangeInfo(
+    exchangeInfo: ExchangeRecord,
+    newKeys: KeysJson,
+  ): Promise<ExchangeRecord> {
     if (exchangeInfo.masterPublicKey !== newKeys.master_public_key) {
       throw Error("public keys do not match");
     }
 
-    const existingDenoms: {[denomPub: string]: DenominationRecord} = await (
-      this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex,
-                         exchangeInfo.baseUrl)
-          .fold((x: DenominationRecord,
-                 acc: typeof existingDenoms) => (acc[x.denomPub] = x, acc), {})
-    );
+    const existingDenoms: {
+      [denomPub: string]: DenominationRecord;
+    } = await this.q()
+      .iterIndex(
+        Stores.denominations.exchangeBaseUrlIndex,
+        exchangeInfo.baseUrl,
+      )
+      .fold(
+        (x: DenominationRecord, acc: typeof existingDenoms) => (
+          (acc[x.denomPub] = x), acc
+        ),
+        {},
+      );
 
     const newDenoms: typeof existingDenoms = {};
     const newAndUnseenDenoms: typeof existingDenoms = {};
@@ -1750,15 +1965,18 @@ export class Wallet {
     }
 
     await this.q()
-              .putAll(Stores.denominations,
-                      Object.keys(newAndUnseenDenoms).map((d) => 
newAndUnseenDenoms[d]))
-              .putAll(Stores.denominations,
-                      Object.keys(existingDenoms).map((d) => 
existingDenoms[d]))
-              .finish();
+      .putAll(
+        Stores.denominations,
+        Object.keys(newAndUnseenDenoms).map(d => newAndUnseenDenoms[d]),
+      )
+      .putAll(
+        Stores.denominations,
+        Object.keys(existingDenoms).map(d => existingDenoms[d]),
+      )
+      .finish();
     return exchangeInfo;
   }
 
-
   /**
    * Get detailed balance information, sliced by exchange and by currency.
    */
@@ -1767,15 +1985,24 @@ export class Wallet {
      * Add amount to a balance field, both for
      * the slicing by exchange and currency.
      */
-    function addTo(balance: WalletBalance,
-                   field: keyof WalletBalanceEntry,
-                   amount: AmountJson,
-                   exchange: string): void {
+    function addTo(
+      balance: WalletBalance,
+      field: keyof WalletBalanceEntry,
+      amount: AmountJson,
+      exchange: string,
+    ): void {
       const z = Amounts.getZero(amount.currency);
-      const balanceIdentity = {available: z, paybackAmount: z, 
pendingIncoming: z, pendingPayment: z};
+      const balanceIdentity = {
+        available: z,
+        paybackAmount: z,
+        pendingIncoming: z,
+        pendingPayment: z,
+      };
       let entryCurr = balance.byCurrency[amount.currency];
       if (!entryCurr) {
-        balance.byCurrency[amount.currency] = entryCurr = { ...balanceIdentity 
};
+        balance.byCurrency[amount.currency] = entryCurr = {
+          ...balanceIdentity,
+        };
       }
       let entryEx = balance.byExchange[exchange];
       if (!entryEx) {
@@ -1819,14 +2046,19 @@ export class Wallet {
       if (!r.hasPayback) {
         return balance;
       }
-      if (Amounts.cmp(smallestWithdraw[r.exchange_base_url], 
r.current_amount!) < 0) {
+      if (
+        Amounts.cmp(smallestWithdraw[r.exchange_base_url], r.current_amount!) <
+        0
+      ) {
         addTo(balance, "paybackAmount", r.current_amount!, 
r.exchange_base_url);
       }
       return balance;
     }
 
-    function collectPendingRefresh(r: RefreshSessionRecord,
-                                   balance: WalletBalance) {
+    function collectPendingRefresh(
+      r: RefreshSessionRecord,
+      balance: WalletBalance,
+    ) {
       // Don't count finished refreshes, since the refresh already resulted
       // in coins being added to the wallet.
       if (r.finished) {
@@ -1842,13 +2074,20 @@ export class Wallet {
         return balance;
       }
       for (const c of t.payReq.coins) {
-        addTo(balance, "pendingPayment", Amounts.parseOrThrow(c.contribution), 
c.exchange_url);
+        addTo(
+          balance,
+          "pendingPayment",
+          Amounts.parseOrThrow(c.contribution),
+          c.exchange_url,
+        );
       }
       return balance;
     }
 
-    function collectSmallestWithdraw(e: JoinResult<ExchangeRecord, 
DenominationRecord>,
-                                     sw: any) {
+    function collectSmallestWithdraw(
+      e: JoinResult<ExchangeRecord, DenominationRecord>,
+      sw: any,
+    ) {
       let min = sw[e.left.baseUrl];
       const v = Amounts.add(e.right.value, e.right.feeWithdraw).amount;
       if (!min) {
@@ -1866,31 +2105,26 @@ export class Wallet {
     };
     // Mapping from exchange pub to smallest
     // possible amount we can withdraw
-    let smallestWithdraw: {[baseUrl: string]: AmountJson} = {};
+    let smallestWithdraw: { [baseUrl: string]: AmountJson } = {};
 
-    smallestWithdraw = await (this.q()
-                                  .iter(Stores.exchanges)
-                                  
.indexJoin(Stores.denominations.exchangeBaseUrlIndex,
-                                             (x) => x.baseUrl)
-                                  .fold(collectSmallestWithdraw, {}));
+    smallestWithdraw = await this.q()
+      .iter(Stores.exchanges)
+      .indexJoin(Stores.denominations.exchangeBaseUrlIndex, x => x.baseUrl)
+      .fold(collectSmallestWithdraw, {});
 
     const tx = this.q();
-    tx.iter(Stores.coins)
-      .fold(collectBalances, balanceStore);
-    tx.iter(Stores.refresh)
-      .fold(collectPendingRefresh, balanceStore);
-    tx.iter(Stores.reserves)
-      .fold(collectPendingWithdraw, balanceStore);
-    tx.iter(Stores.reserves)
-      .fold(collectPaybacks, balanceStore);
-    tx.iter(Stores.purchases)
-      .fold(collectPayments, balanceStore);
+    tx.iter(Stores.coins).fold(collectBalances, balanceStore);
+    tx.iter(Stores.refresh).fold(collectPendingRefresh, balanceStore);
+    tx.iter(Stores.reserves).fold(collectPendingWithdraw, balanceStore);
+    tx.iter(Stores.reserves).fold(collectPaybacks, balanceStore);
+    tx.iter(Stores.purchases).fold(collectPayments, balanceStore);
     await tx.finish();
     return balanceStore;
   }
 
-
-  async createRefreshSession(oldCoinPub: string): 
Promise<RefreshSessionRecord|undefined> {
+  async createRefreshSession(
+    oldCoinPub: string,
+  ): Promise<RefreshSessionRecord | undefined> {
     const coin = await this.q().get<CoinRecord>(Stores.coins, oldCoinPub);
 
     if (!coin) {
@@ -1907,48 +2141,52 @@ export class Wallet {
       throw Error("db inconsistent");
     }
 
-    const oldDenom = await this.q().get(Stores.denominations,
-                                      [exchange.baseUrl, coin.denomPub]);
+    const oldDenom = await this.q().get(Stores.denominations, [
+      exchange.baseUrl,
+      coin.denomPub,
+    ]);
 
     if (!oldDenom) {
       throw Error("db inconsistent");
     }
 
-    const availableDenoms: DenominationRecord[] = await (
-      this.q()
-          .iterIndex(Stores.denominations.exchangeBaseUrlIndex,
-                     exchange.baseUrl)
-          .toArray()
-    );
+    const availableDenoms: DenominationRecord[] = await this.q()
+      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
+      .toArray();
 
-    const availableAmount = Amounts.sub(coin.currentAmount,
-                                      oldDenom.feeRefresh).amount;
+    const availableAmount = Amounts.sub(coin.currentAmount, 
oldDenom.feeRefresh)
+      .amount;
 
-    const newCoinDenoms = getWithdrawDenomList(availableAmount,
-                                             availableDenoms);
+    const newCoinDenoms = getWithdrawDenomList(
+      availableAmount,
+      availableDenoms,
+    );
 
     console.log("refreshing coin", coin);
     console.log("refreshing into", newCoinDenoms);
 
     if (newCoinDenoms.length === 0) {
-      console.log(`not refreshing, available amount 
${amountToPretty(availableAmount)} too small`);
+      console.log(
+        `not refreshing, available amount ${amountToPretty(
+          availableAmount,
+        )} too small`,
+      );
       coin.status = CoinStatus.Useless;
       await this.q().put(Stores.coins, coin);
       this.notifier.notify();
       return undefined;
     }
 
-
-    const refreshSession: RefreshSessionRecord = await (
-      this.cryptoApi.createRefreshSession(exchange.baseUrl,
-                                          3,
-                                          coin,
-                                          newCoinDenoms,
-                                          oldDenom.feeRefresh));
+    const refreshSession: RefreshSessionRecord = await 
this.cryptoApi.createRefreshSession(
+      exchange.baseUrl,
+      3,
+      coin,
+      newCoinDenoms,
+      oldDenom.feeRefresh,
+    );
 
     function mutateCoin(c: CoinRecord): CoinRecord {
-      const r = Amounts.sub(c.currentAmount,
-                          refreshSession.valueWithFee);
+      const r = Amounts.sub(c.currentAmount, refreshSession.valueWithFee);
       if (r.saturated) {
         // Something else must have written the coin value
         throw AbortTransaction;
@@ -1961,8 +2199,9 @@ export class Wallet {
     // Store refresh session and subtract refreshed amount from
     // coin in the same transaction.
     const query = this.q();
-    query.put(Stores.refresh, refreshSession, "refreshKey")
-         .mutate(Stores.coins, coin.coinPub, mutateCoin);
+    query
+      .put(Stores.refresh, refreshSession, "refreshKey")
+      .mutate(Stores.coins, coin.coinPub, mutateCoin);
     await query.finish();
     this.notifier.notify();
 
@@ -1976,10 +2215,10 @@ export class Wallet {
     return refreshSession;
   }
 
-
   async refresh(oldCoinPub: string): Promise<void> {
-
-    const oldRefreshSessions = await this.q().iter(Stores.refresh).toArray();
+    const oldRefreshSessions = await this.q()
+      .iter(Stores.refresh)
+      .toArray();
     for (const session of oldRefreshSessions) {
       console.log("got old session for", oldCoinPub, session);
       this.continueRefreshSession(session);
@@ -1989,7 +2228,10 @@ export class Wallet {
       console.warn("can't refresh, coin not in database");
       return;
     }
-    if (coin.status === CoinStatus.Useless || coin.status === 
CoinStatus.Fresh) {
+    if (
+      coin.status === CoinStatus.Useless ||
+      coin.status === CoinStatus.Fresh
+    ) {
       return;
     }
     const refreshSession = await this.createRefreshSession(oldCoinPub);
@@ -2007,7 +2249,10 @@ export class Wallet {
     }
     if (typeof refreshSession.norevealIndex !== "number") {
       await this.refreshMelt(refreshSession);
-      const r = await this.q().get<RefreshSessionRecord>(Stores.refresh, 
refreshSession.id);
+      const r = await this.q().get<RefreshSessionRecord>(
+        Stores.refresh,
+        refreshSession.id,
+      );
       if (!r) {
         throw Error("refresh session does not exist anymore");
       }
@@ -2017,21 +2262,24 @@ export class Wallet {
     await this.refreshReveal(refreshSession);
   }
 
-
   async refreshMelt(refreshSession: RefreshSessionRecord): Promise<void> {
     if (refreshSession.norevealIndex !== undefined) {
       console.error("won't melt again");
       return;
     }
 
-    const coin = await this.q().get<CoinRecord>(Stores.coins,
-                                              refreshSession.meltCoinPub);
+    const coin = await this.q().get<CoinRecord>(
+      Stores.coins,
+      refreshSession.meltCoinPub,
+    );
     if (!coin) {
       console.error("can't melt coin, it does not exist");
       return;
     }
 
-    const reqUrl = new 
URI("refresh/melt").absoluteTo(refreshSession.exchangeBaseUrl);
+    const reqUrl = new URI("refresh/melt").absoluteTo(
+      refreshSession.exchangeBaseUrl,
+    );
     const meltReq = {
       coin_pub: coin.coinPub,
       confirm_sig: refreshSession.confirmSig,
@@ -2064,11 +2312,12 @@ export class Wallet {
 
     refreshSession.norevealIndex = norevealIndex;
 
-    await this.q().put(Stores.refresh, refreshSession).finish();
+    await this.q()
+      .put(Stores.refresh, refreshSession)
+      .finish();
     this.notifier.notify();
   }
 
-
   async refreshReveal(refreshSession: RefreshSessionRecord): Promise<void> {
     const norevealIndex = refreshSession.norevealIndex;
     if (norevealIndex === undefined) {
@@ -2082,17 +2331,40 @@ export class Wallet {
       throw Error("refresh index error");
     }
 
+    const meltCoinRecord = await this.q().get(
+      Stores.coins,
+      refreshSession.meltCoinPub,
+    );
+    if (!meltCoinRecord) {
+      throw Error("inconsistent database");
+    }
+
     const evs = preCoins.map((x: RefreshPreCoinRecord) => x.coinEv);
 
+    const linkSigs: string[] = [];
+    for (let i = 0; i < refreshSession.newDenoms.length; i++) {
+      const linkSig = await this.cryptoApi.signCoinLink(
+        meltCoinRecord.coinPriv,
+        refreshSession.newDenomHashes[i],
+        refreshSession.meltCoinPub,
+        refreshSession.transferPubs[norevealIndex],
+        preCoins[i].coinEv,
+      );
+      linkSigs.push(linkSig);
+    }
+
     const req = {
       coin_evs: evs,
       new_denoms_h: refreshSession.newDenomHashes,
       rc: refreshSession.hash,
       transfer_privs: privs,
       transfer_pub: refreshSession.transferPubs[norevealIndex],
+      link_sigs: linkSigs,
     };
 
-    const reqUrl = new URI("refresh/reveal") 
.absoluteTo(refreshSession.exchangeBaseUrl);
+    const reqUrl = new URI("refresh/reveal").absoluteTo(
+      refreshSession.exchangeBaseUrl,
+    );
     console.log("reveal request:", req);
     const resp = await this.http.postJson(reqUrl.href(), req);
 
@@ -2110,8 +2382,10 @@ export class Wallet {
       console.log("/refresh/reveal did not contain ev_sigs");
     }
 
-    const exchange = await this.q().get<ExchangeRecord>(Stores.exchanges,
-                                                      
refreshSession.exchangeBaseUrl);
+    const exchange = await this.q().get<ExchangeRecord>(
+      Stores.exchanges,
+      refreshSession.exchangeBaseUrl,
+    );
     if (!exchange) {
       console.error(`exchange ${refreshSession.exchangeBaseUrl} not found`);
       return;
@@ -2120,21 +2394,21 @@ export class Wallet {
     const coins: CoinRecord[] = [];
 
     for (let i = 0; i < respJson.ev_sigs.length; i++) {
-      const denom = await (
-        this.q()
-            .get(Stores.denominations,
-                 [
-                   refreshSession.exchangeBaseUrl,
-                   refreshSession.newDenoms[i],
-                 ]));
+      const denom = await this.q().get(Stores.denominations, [
+        refreshSession.exchangeBaseUrl,
+        refreshSession.newDenoms[i],
+      ]);
       if (!denom) {
         console.error("denom not found");
         continue;
       }
-      const pc = 
refreshSession.preCoinsForGammas[refreshSession.norevealIndex!][i];
-      const denomSig = await 
this.cryptoApi.rsaUnblind(respJson.ev_sigs[i].ev_sig,
-                                                     pc.blindingKey,
-                                                     denom.denomPub);
+      const pc =
+        refreshSession.preCoinsForGammas[refreshSession.norevealIndex!][i];
+      const denomSig = await this.cryptoApi.rsaUnblind(
+        respJson.ev_sigs[i].ev_sig,
+        pc.blindingKey,
+        denom.denomPub,
+      );
       const coin: CoinRecord = {
         blindingKey: pc.blindingKey,
         coinPriv: pc.privateKey,
@@ -2154,22 +2428,23 @@ export class Wallet {
     refreshSession.finished = true;
 
     await this.q()
-              .putAll(Stores.coins, coins)
-              .put(Stores.refresh, refreshSession)
-              .finish();
+      .putAll(Stores.coins, coins)
+      .put(Stores.refresh, refreshSession)
+      .finish();
     this.notifier.notify();
   }
 
-
   /**
    * Retrive the full event history for this wallet.
    */
-  async getHistory(): Promise<{history: HistoryRecord[]}> {
+  async getHistory(): Promise<{ history: HistoryRecord[] }> {
     const history: HistoryRecord[] = [];
 
     // FIXME: do pagination instead of generating the full history
 
-    const proposals = await 
this.q().iter<ProposalDownloadRecord>(Stores.proposals).toArray();
+    const proposals = await this.q()
+      .iter<ProposalDownloadRecord>(Stores.proposals)
+      .toArray();
     for (const p of proposals) {
       history.push({
         detail: {
@@ -2181,7 +2456,9 @@ export class Wallet {
       });
     }
 
-    const purchases = await 
this.q().iter<PurchaseRecord>(Stores.purchases).toArray();
+    const purchases = await this.q()
+      .iter<PurchaseRecord>(Stores.purchases)
+      .toArray();
     for (const p of purchases) {
       history.push({
         detail: {
@@ -2195,16 +2472,17 @@ export class Wallet {
       });
       if (p.timestamp_refund) {
         const contractAmount = Amounts.parseOrThrow(p.contractTerms.amount);
-        const amountsPending = (
-          Object.keys(p.refundsPending)
-                .map((x) => 
Amounts.parseOrThrow(p.refundsPending[x].refund_amount))
+        const amountsPending = Object.keys(p.refundsPending).map(x =>
+          Amounts.parseOrThrow(p.refundsPending[x].refund_amount),
         );
-        const amountsDone = (
-          Object.keys(p.refundsDone)
-                .map((x) => 
Amounts.parseOrThrow(p.refundsDone[x].refund_amount))
+        const amountsDone = Object.keys(p.refundsDone).map(x =>
+          Amounts.parseOrThrow(p.refundsDone[x].refund_amount),
         );
         const amounts: AmountJson[] = amountsPending.concat(amountsDone);
-        const amount = Amounts.add(Amounts.getZero(contractAmount.currency), 
...amounts).amount;
+        const amount = Amounts.add(
+          Amounts.getZero(contractAmount.currency),
+          ...amounts,
+        ).amount;
 
         history.push({
           detail: {
@@ -2219,7 +2497,9 @@ export class Wallet {
       }
     }
 
-    const reserves: ReserveRecord[] = await 
this.q().iter<ReserveRecord>(Stores.reserves).toArray();
+    const reserves: ReserveRecord[] = await this.q()
+      .iter<ReserveRecord>(Stores.reserves)
+      .toArray();
     for (const r of reserves) {
       history.push({
         detail: {
@@ -2243,7 +2523,9 @@ export class Wallet {
       }
     }
 
-    const tips: TipRecord[] = await 
this.q().iter<TipRecord>(Stores.tips).toArray();
+    const tips: TipRecord[] = await this.q()
+      .iter<TipRecord>(Stores.tips)
+      .toArray();
     for (const tip of tips) {
       history.push({
         detail: {
@@ -2259,70 +2541,74 @@ export class Wallet {
 
     history.sort((h1, h2) => Math.sign(h1.timestamp - h2.timestamp));
 
-    return {history};
+    return { history };
   }
 
   async getDenoms(exchangeUrl: string): Promise<DenominationRecord[]> {
-    const denoms = await 
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, 
exchangeUrl).toArray();
+    const denoms = await this.q()
+      .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchangeUrl)
+      .toArray();
     return denoms;
   }
 
-  async getProposal(proposalId: number): 
Promise<ProposalDownloadRecord|undefined> {
+  async getProposal(
+    proposalId: number,
+  ): Promise<ProposalDownloadRecord | undefined> {
     const proposal = await this.q().get(Stores.proposals, proposalId);
     return proposal;
   }
 
   async getExchanges(): Promise<ExchangeRecord[]> {
     return this.q()
-               .iter<ExchangeRecord>(Stores.exchanges)
-               .toArray();
+      .iter<ExchangeRecord>(Stores.exchanges)
+      .toArray();
   }
 
   async getCurrencies(): Promise<CurrencyRecord[]> {
     return this.q()
-               .iter<CurrencyRecord>(Stores.currencies)
-               .toArray();
+      .iter<CurrencyRecord>(Stores.currencies)
+      .toArray();
   }
 
   async updateCurrency(currencyRecord: CurrencyRecord): Promise<void> {
     console.log("updating currency to", currencyRecord);
     await this.q()
-               .put(Stores.currencies, currencyRecord)
-               .finish();
+      .put(Stores.currencies, currencyRecord)
+      .finish();
     this.notifier.notify();
   }
 
   async getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> {
     return this.q()
-               .iter<ReserveRecord>(Stores.reserves)
-               .filter((r: ReserveRecord) => r.exchange_base_url === 
exchangeBaseUrl)
-               .toArray();
+      .iter<ReserveRecord>(Stores.reserves)
+      .filter((r: ReserveRecord) => r.exchange_base_url === exchangeBaseUrl)
+      .toArray();
   }
 
   async getCoins(exchangeBaseUrl: string): Promise<CoinRecord[]> {
     return this.q()
-               .iter<CoinRecord>(Stores.coins)
-               .filter((c: CoinRecord) => c.exchangeBaseUrl === 
exchangeBaseUrl)
-               .toArray();
+      .iter<CoinRecord>(Stores.coins)
+      .filter((c: CoinRecord) => c.exchangeBaseUrl === exchangeBaseUrl)
+      .toArray();
   }
 
   async getPreCoins(exchangeBaseUrl: string): Promise<PreCoinRecord[]> {
     return this.q()
-               .iter<PreCoinRecord>(Stores.precoins)
-               .filter((c: PreCoinRecord) => c.exchangeBaseUrl === 
exchangeBaseUrl)
-               .toArray();
+      .iter<PreCoinRecord>(Stores.precoins)
+      .filter((c: PreCoinRecord) => c.exchangeBaseUrl === exchangeBaseUrl)
+      .toArray();
   }
 
   async hashContract(contract: ContractTerms): Promise<string> {
     return this.cryptoApi.hashString(canonicalJson(contract));
   }
 
-
-  async getCurrencyRecord(currency: string): Promise<CurrencyRecord|undefined> 
{
+  async getCurrencyRecord(
+    currency: string,
+  ): Promise<CurrencyRecord | undefined> {
     return this.q().get(Stores.currencies, currency);
   }
 
-
   async payback(coinPub: string): Promise<void> {
     let coin = await this.q().get(Stores.coins, coinPub);
     if (!coin) {
@@ -2338,7 +2624,9 @@ export class Wallet {
     }
     switch (coin.status) {
       case CoinStatus.Refreshed:
-        throw Error(`Can't do payback for coin ${coinPub} since it's 
refreshed`);
+        throw Error(
+          `Can't do payback for coin ${coinPub} since it's refreshed`,
+        );
       case CoinStatus.PaybackDone:
         console.log(`Coin ${coinPub} already payed back`);
         return;
@@ -2348,7 +2636,9 @@ export class Wallet {
     // technically we might update reserve status before we get the response
     // from the reserve for the payback request.
     reserve.hasPayback = true;
-    await this.q().put(Stores.coins, coin).put(Stores.reserves, reserve);
+    await this.q()
+      .put(Stores.coins, coin)
+      .put(Stores.reserves, reserve);
     this.notifier.notify();
 
     const paybackRequest = await this.cryptoApi.createPaybackRequest(coin);
@@ -2357,7 +2647,9 @@ export class Wallet {
     if (resp.status !== 200) {
       throw Error();
     }
-    const paybackConfirmation = 
PaybackConfirmation.checked(JSON.parse(resp.responseText));
+    const paybackConfirmation = PaybackConfirmation.checked(
+      JSON.parse(resp.responseText),
+    );
     if (paybackConfirmation.reserve_pub !== coin.reservePub) {
       throw Error(`Coin's reserve doesn't match reserve on payback`);
     }
@@ -2371,8 +2663,10 @@ export class Wallet {
     await this.updateReserve(reservePub!);
   }
 
-
-  async denominationRecordFromKeys(exchangeBaseUrl: string, denomIn: 
Denomination): Promise<DenominationRecord> {
+  async denominationRecordFromKeys(
+    exchangeBaseUrl: string,
+    denomIn: Denomination,
+  ): Promise<DenominationRecord> {
     const denomPubHash = await this.cryptoApi.hashDenomPub(denomIn.denom_pub);
     const d: DenominationRecord = {
       denomPub: denomIn.denom_pub,
@@ -2405,7 +2699,10 @@ export class Wallet {
   }
 
   async getPaybackReserves(): Promise<ReserveRecord[]> {
-    return await this.q().iter(Stores.reserves).filter((r) => 
r.hasPayback).toArray();
+    return await this.q()
+      .iter(Stores.reserves)
+      .filter(r => r.hasPayback)
+      .toArray();
   }
 
   /**
@@ -2417,18 +2714,26 @@ export class Wallet {
 
   async getSenderWireInfos(): Promise<SenderWireInfos> {
     const m: { [url: string]: Set<string> } = {};
-    await this.q().iter(Stores.exchangeWireFees).map((x) => {
-      const s = m[x.exchangeBaseUrl] = m[x.exchangeBaseUrl] || new Set();
-      Object.keys(x.feesForType).map((k) => s.add(k));
-    }).run();
+    await this.q()
+      .iter(Stores.exchangeWireFees)
+      .map(x => {
+        const s = (m[x.exchangeBaseUrl] = m[x.exchangeBaseUrl] || new Set());
+        Object.keys(x.feesForType).map(k => s.add(k));
+      })
+      .run();
     console.log(m);
     const exchangeWireTypes: { [url: string]: string[] } = {};
-    Object.keys(m).map((e) => { exchangeWireTypes[e] = Array.from(m[e]); });
+    Object.keys(m).map(e => {
+      exchangeWireTypes[e] = Array.from(m[e]);
+    });
 
     const senderWiresSet = new Set();
-    await this.q().iter(Stores.senderWires).map((x) => {
+    await this.q()
+      .iter(Stores.senderWires)
+      .map(x => {
         senderWiresSet.add(x.paytoUri);
-    }).run();
+      })
+      .run();
     const senderWires = Array.from(senderWiresSet);
 
     return {
@@ -2448,7 +2753,7 @@ export class Wallet {
       console.error(`wire type must be a non-empty string, not ${wireType}`);
       return;
     }
-    const stampSecNow = Math.floor((new Date()).getTime() / 1000);
+    const stampSecNow = Math.floor(new Date().getTime() / 1000);
     const exchange = await this.q().get(Stores.exchanges, req.exchange);
     if (!exchange) {
       console.error(`Exchange ${req.exchange} not known to the wallet`);
@@ -2464,13 +2769,17 @@ export class Wallet {
 
     const { priv, pub } = await this.cryptoApi.createEddsaKeypair();
 
-    const wireHash = await 
this.cryptoApi.hashString(canonicalJson(req.senderWire));
+    const wireHash = await this.cryptoApi.hashString(
+      canonicalJson(req.senderWire),
+    );
 
     const contractTerms: ContractTerms = {
       H_wire: wireHash,
       amount: Amounts.toString(req.amount),
       auditors: [],
-      exchanges: [ { master_pub: exchange.masterPublicKey, url: 
exchange.baseUrl } ],
+      exchanges: [
+        { master_pub: exchange.masterPublicKey, url: exchange.baseUrl },
+      ],
       extra: {},
       fulfillment_url: "",
       locations: [],
@@ -2486,15 +2795,19 @@ export class Wallet {
       wire_method: wireType,
     };
 
-    const contractTermsHash = await 
this.cryptoApi.hashString(canonicalJson(contractTerms));
+    const contractTermsHash = await this.cryptoApi.hashString(
+      canonicalJson(contractTerms),
+    );
 
-    const payCoinInfo = await (
-      this.cryptoApi.signDeposit(contractTerms, cds, 
Amounts.parseOrThrow(contractTerms.amount))
+    const payCoinInfo = await this.cryptoApi.signDeposit(
+      contractTerms,
+      cds,
+      Amounts.parseOrThrow(contractTerms.amount),
     );
 
     console.log("pci", payCoinInfo);
 
-    const coins = payCoinInfo.sigs.map((s) => ({ coinPaySig: s }));
+    const coins = payCoinInfo.sigs.map(s => ({ coinPaySig: s }));
 
     const coinsReturnRecord: CoinsReturnRecord = {
       coins,
@@ -2506,16 +2819,18 @@ export class Wallet {
     };
 
     await this.q()
-              .put(Stores.coinsReturns, coinsReturnRecord)
-              .putAll(Stores.coins, payCoinInfo.updatedCoins)
-              .finish();
+      .put(Stores.coinsReturns, coinsReturnRecord)
+      .putAll(Stores.coins, payCoinInfo.updatedCoins)
+      .finish();
     this.badge.showNotification();
     this.notifier.notify();
 
     this.depositReturnedCoins(coinsReturnRecord);
   }
 
-  async depositReturnedCoins(coinsReturnRecord: CoinsReturnRecord): 
Promise<void> {
+  async depositReturnedCoins(
+    coinsReturnRecord: CoinsReturnRecord,
+  ): Promise<void> {
     for (const c of coinsReturnRecord.coins) {
       if (c.depositedSig) {
         continue;
@@ -2536,7 +2851,7 @@ export class Wallet {
         wire_transfer_deadline: coinsReturnRecord.contractTerms.pay_deadline,
       };
       console.log("req", req);
-      const reqUrl = (new 
URI("deposit")).absoluteTo(coinsReturnRecord.exchange);
+      const reqUrl = new URI("deposit").absoluteTo(coinsReturnRecord.exchange);
       const resp = await this.http.postJson(reqUrl.href(), req);
       if (resp.status !== 200) {
         console.error("deposit failed due to status code", resp);
@@ -2556,7 +2871,10 @@ export class Wallet {
       // FIXME: verify signature
 
       // For every successful deposit, we replace the old record with an 
updated one
-      const currentCrr = await this.q().get(Stores.coinsReturns, 
coinsReturnRecord.contractTermsHash);
+      const currentCrr = await this.q().get(
+        Stores.coinsReturns,
+        coinsReturnRecord.contractTermsHash,
+      );
       if (!currentCrr) {
         console.error("database inconsistent");
         continue;
@@ -2571,7 +2889,9 @@ export class Wallet {
     }
   }
 
-  async acceptRefundResponse(refundResponse: MerchantRefundResponse): 
Promise<string> {
+  async acceptRefundResponse(
+    refundResponse: MerchantRefundResponse,
+  ): Promise<string> {
     const refundPermissions = refundResponse.refund_permissions;
 
     if (!refundPermissions.length) {
@@ -2582,16 +2902,19 @@ export class Wallet {
     /**
      * Add refund to purchase if not already added.
      */
-    function f(t: PurchaseRecord|undefined): PurchaseRecord|undefined {
+    function f(t: PurchaseRecord | undefined): PurchaseRecord | undefined {
       if (!t) {
         console.error("purchase not found, not adding refunds");
         return;
       }
 
-      t.timestamp_refund = (new Date()).getTime();
+      t.timestamp_refund = new Date().getTime();
 
       for (const perm of refundPermissions) {
-        if (!t.refundsPending[perm.merchant_sig] && 
!t.refundsDone[perm.merchant_sig]) {
+        if (
+          !t.refundsPending[perm.merchant_sig] &&
+          !t.refundsDone[perm.merchant_sig]
+        ) {
           t.refundsPending[perm.merchant_sig] = perm;
         }
       }
@@ -2601,7 +2924,9 @@ export class Wallet {
     const hc = refundResponse.h_contract_terms;
 
     // Add the refund permissions to the purchase within a DB transaction
-    await this.q().mutate(Stores.purchases, hc, f).finish();
+    await this.q()
+      .mutate(Stores.purchases, hc, f)
+      .finish();
     this.notifier.notify();
 
     // Start submitting it but don't wait for it here.
@@ -2610,7 +2935,6 @@ export class Wallet {
     return hc;
   }
 
-
   /**
    * Accept a refund, return the contract hash for the contract
    * that was involved in the refund.
@@ -2632,11 +2956,13 @@ export class Wallet {
     return this.acceptRefundResponse(refundResponse);
   }
 
-
   private async submitRefunds(contractTermsHash: string): Promise<void> {
     const purchase = await this.q().get(Stores.purchases, contractTermsHash);
     if (!purchase) {
-      console.error("not submitting refunds, contract terms not found:", 
contractTermsHash);
+      console.error(
+        "not submitting refunds, contract terms not found:",
+        contractTermsHash,
+      );
       return;
     }
     const pendingKeys = Object.keys(purchase.refundsPending);
@@ -2657,7 +2983,7 @@ export class Wallet {
       console.log("sending refund permission", perm);
       // FIXME: not correct once we support multiple exchanges per payment
       const exchangeUrl = purchase.payReq.coins[0].exchange_url;
-      const reqUrl = (new URI("refund")).absoluteTo(exchangeUrl);
+      const reqUrl = new URI("refund").absoluteTo(exchangeUrl);
       const resp = await this.http.postJson(reqUrl.href(), req);
       if (resp.status !== 200) {
         console.error("refund failed", resp);
@@ -2665,7 +2991,9 @@ export class Wallet {
       }
 
       // Transactionally mark successful refunds as done
-      const transformPurchase = (t: PurchaseRecord|undefined): 
PurchaseRecord|undefined => {
+      const transformPurchase = (
+        t: PurchaseRecord | undefined,
+      ): PurchaseRecord | undefined => {
         if (!t) {
           console.warn("purchase not found, not updating refund");
           return;
@@ -2676,7 +3004,9 @@ export class Wallet {
         }
         return t;
       };
-      const transformCoin = (c: CoinRecord|undefined): CoinRecord|undefined => 
{
+      const transformCoin = (
+        c: CoinRecord | undefined,
+      ): CoinRecord | undefined => {
         if (!c) {
           console.warn("coin not found, can't apply refund");
           return;
@@ -2691,9 +3021,9 @@ export class Wallet {
       };
 
       await this.q()
-                .mutate(Stores.purchases, contractTermsHash, transformPurchase)
-                .mutate(Stores.coins, perm.coin_pub, transformCoin)
-                .finish();
+        .mutate(Stores.purchases, contractTermsHash, transformPurchase)
+        .mutate(Stores.coins, perm.coin_pub, transformCoin)
+        .finish();
       this.refresh(perm.coin_pub);
     }
 
@@ -2701,27 +3031,44 @@ export class Wallet {
     this.notifier.notify();
   }
 
-  async getPurchase(contractTermsHash: string): 
Promise<PurchaseRecord|undefined> {
+  async getPurchase(
+    contractTermsHash: string,
+  ): Promise<PurchaseRecord | undefined> {
     return this.q().get(Stores.purchases, contractTermsHash);
   }
 
-  async getFullRefundFees(refundPermissions: MerchantRefundPermission[]): 
Promise<AmountJson> {
+  async getFullRefundFees(
+    refundPermissions: MerchantRefundPermission[],
+  ): Promise<AmountJson> {
     if (refundPermissions.length === 0) {
       throw Error("no refunds given");
     }
-    const coin0 = await this.q().get(Stores.coins, 
refundPermissions[0].coin_pub);
+    const coin0 = await this.q().get(
+      Stores.coins,
+      refundPermissions[0].coin_pub,
+    );
     if (!coin0) {
       throw Error("coin not found");
     }
-    let feeAcc = 
Amounts.getZero(Amounts.parseOrThrow(refundPermissions[0].refund_amount).currency);
+    let feeAcc = Amounts.getZero(
+      Amounts.parseOrThrow(refundPermissions[0].refund_amount).currency,
+    );
 
-    const denoms = await 
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, 
coin0.exchangeBaseUrl).toArray();
+    const denoms = await this.q()
+      .iterIndex(
+        Stores.denominations.exchangeBaseUrlIndex,
+        coin0.exchangeBaseUrl,
+      )
+      .toArray();
     for (const rp of refundPermissions) {
       const coin = await this.q().get(Stores.coins, rp.coin_pub);
       if (!coin) {
         throw Error("coin not found");
       }
-      const denom = await this.q().get(Stores.denominations, 
[coin0.exchangeBaseUrl, coin.denomPub]);
+      const denom = await this.q().get(Stores.denominations, [
+        coin0.exchangeBaseUrl,
+        coin.denomPub,
+      ]);
       if (!denom) {
         throw Error(`denom not found (${coin.denomPub})`);
       }
@@ -2731,13 +3078,16 @@ export class Wallet {
       // refreshed normally (and what about incremental refunds?)
       const refundAmount = Amounts.parseOrThrow(rp.refund_amount);
       const refundFee = Amounts.parseOrThrow(rp.refund_fee);
-      const refreshCost = getTotalRefreshCost(denoms, denom, 
Amounts.sub(refundAmount, refundFee).amount);
+      const refreshCost = getTotalRefreshCost(
+        denoms,
+        denom,
+        Amounts.sub(refundAmount, refundFee).amount,
+      );
       feeAcc = Amounts.add(feeAcc, refreshCost, refundFee).amount;
     }
     return feeAcc;
   }
 
-
   async processTip(tipToken: TipToken): Promise<TipRecord> {
     const merchantDomain = new URI(tipToken.pickup_url).origin();
     const key = tipToken.tip_id + merchantDomain;
@@ -2754,7 +3104,6 @@ export class Wallet {
     }
   }
 
-
   private async processTipImpl(tipToken: TipToken): Promise<TipRecord> {
     console.log("got tip token", tipToken);
 
@@ -2765,17 +3114,25 @@ export class Wallet {
       throw Error("tipping failed (invalid expiration)");
     }
 
-    let tipRecord = await this.q().get(Stores.tips, [tipToken.tip_id, 
merchantDomain]);
+    let tipRecord = await this.q().get(Stores.tips, [
+      tipToken.tip_id,
+      merchantDomain,
+    ]);
 
     if (tipRecord && tipRecord.pickedUp) {
       return tipRecord;
     }
     const tipAmount = Amounts.parseOrThrow(tipToken.amount);
     await this.updateExchangeFromUrl(tipToken.exchange_url);
-    const denomsForWithdraw = await 
this.getVerifiedWithdrawDenomList(tipToken.exchange_url, tipAmount);
-    const planchets = await Promise.all(denomsForWithdraw.map(d => 
this.cryptoApi.createTipPlanchet(d)));
+    const denomsForWithdraw = await this.getVerifiedWithdrawDenomList(
+      tipToken.exchange_url,
+      tipAmount,
+    );
+    const planchets = await Promise.all(
+      denomsForWithdraw.map(d => this.cryptoApi.createTipPlanchet(d)),
+    );
     const coinPubs: string[] = planchets.map(x => x.coinPub);
-    const now = (new Date()).getTime();
+    const now = new Date().getTime();
     tipRecord = {
       accepted: false,
       amount: Amounts.parseOrThrow(tipToken.amount),
@@ -2792,11 +3149,14 @@ export class Wallet {
 
     let merchantResp;
 
-    tipRecord = await this.q().putOrGetExisting(Stores.tips, tipRecord, 
[tipRecord.tipId, merchantDomain]);
+    tipRecord = await this.q().putOrGetExisting(Stores.tips, tipRecord, [
+      tipRecord.tipId,
+      merchantDomain,
+    ]);
     this.notifier.notify();
 
     // Planchets in the form that the merchant expects
-    const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) 
=> ({
+    const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map(p => 
({
       coin_ev: p.coinEv,
       denom_pub_hash: p.denomPubHash,
     }));
@@ -2839,13 +3199,14 @@ export class Wallet {
 
     tipRecord.pickedUp = true;
 
-    await this.q().put(Stores.tips, tipRecord).finish();
+    await this.q()
+      .put(Stores.tips, tipRecord)
+      .finish();
     this.notifier.notify();
 
     return tipRecord;
   }
 
-
   /**
    * Start using the coins from a tip.
    */
@@ -2881,7 +3242,6 @@ export class Wallet {
     this.notifier.notify();
   }
 
-
   async getTipStatus(tipToken: TipToken): Promise<TipStatus> {
     const tipId = tipToken.tip_id;
     const merchantDomain = new URI(tipToken.pickup_url).origin();
@@ -2901,7 +3261,6 @@ export class Wallet {
     return tipStatus;
   }
 
-
   async abortFailedPayment(contractTermsHash: string): Promise<void> {
     const purchase = await this.q().get(Stores.purchases, contractTermsHash);
     if (!purchase) {
@@ -2929,7 +3288,7 @@ export class Wallet {
     try {
       const config = {
         headers: { "Content-Type": "application/json;charset=UTF-8" },
-        timeout: 5000, /* 5 seconds */
+        timeout: 5000 /* 5 seconds */,
         validateStatus: (s: number) => s === 200,
       };
       resp = await axios.post(purchase.contractTerms.pay_url, abortReq, 
config);
@@ -2946,10 +3305,13 @@ export class Wallet {
       p.abortDone = true;
       return p;
     };
-    await this.q().mutate(Stores.purchases, purchase.contractTermsHash, 
markAbortDone);
+    await this.q().mutate(
+      Stores.purchases,
+      purchase.contractTermsHash,
+      markAbortDone,
+    );
   }
 
-
   /**
    * Synchronously get the paid URL for a resource from the plain fulfillment
    * URL.  Returns undefined if the fulfillment URL is not a resource that was
@@ -2965,7 +3327,7 @@ export class Wallet {
    * based on the current system time.
    */
   async collectGarbage() {
-    const nowMilli = (new Date()).getTime();
+    const nowMilli = new Date().getTime();
     const nowSec = Math.floor(nowMilli / 1000);
 
     const gcReserve = (r: ReserveRecord, n: number) => {
@@ -2978,28 +3340,35 @@ export class Wallet {
       }
       return false;
     };
-    await this.q().deleteIf(Stores.reserves, gcReserve).finish();
+    await this.q()
+      .deleteIf(Stores.reserves, gcReserve)
+      .finish();
 
     const gcProposal = (d: ProposalDownloadRecord, n: number) => {
       // Delete proposal after 60 minutes or 5 minutes before pay deadline,
       // whatever comes first.
-      const deadlinePayMilli = getTalerStampSec(d.contractTerms.pay_deadline)! 
* 1000;
-      const deadlineExpireMilli = nowMilli + (1000 * 60 * 60);
+      const deadlinePayMilli =
+        getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
+      const deadlineExpireMilli = nowMilli + 1000 * 60 * 60;
       return d.timestamp < Math.min(deadlinePayMilli, deadlineExpireMilli);
     };
-    await this.q().deleteIf(Stores.proposals, gcProposal).finish();
+    await this.q()
+      .deleteIf(Stores.proposals, gcProposal)
+      .finish();
 
     const activeExchanges: string[] = [];
     const gcExchange = (d: ExchangeRecord, n: number) => {
       // Delete if if unused and last update more than 20 minutes ago
-      if (!d.lastUsedTime && nowMilli > d.lastUpdateTime + (1000 * 60 * 20)) {
+      if (!d.lastUsedTime && nowMilli > d.lastUpdateTime + 1000 * 60 * 20) {
         return true;
       }
       activeExchanges.push(d.baseUrl);
       return false;
     };
 
-    await this.q().deleteIf(Stores.exchanges, gcExchange).finish();
+    await this.q()
+      .deleteIf(Stores.exchanges, gcExchange)
+      .finish();
 
     const gcDenominations = (d: DenominationRecord, n: number) => {
       if (nowSec > getTalerStampSec(d.stampExpireDeposit)!) {
@@ -3010,7 +3379,9 @@ export class Wallet {
       }
       return false;
     };
-    await this.q().deleteIf(Stores.denominations, gcDenominations).finish();
+    await this.q()
+      .deleteIf(Stores.denominations, gcDenominations)
+      .finish();
 
     const gcWireFees = (r: ExchangeWireFeesRecord, n: number) => {
       if (activeExchanges.indexOf(r.exchangeBaseUrl) < 0) {
@@ -3018,8 +3389,9 @@ export class Wallet {
       }
       return false;
     };
-    await this.q().deleteIf(Stores.exchangeWireFees, gcWireFees).finish();
-
+    await this.q()
+      .deleteIf(Stores.exchangeWireFees, gcWireFees)
+      .finish();
 
     // FIXME(#5210) also GC coins
   }

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



reply via email to

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