gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 05/05: more lint fixes


From: gnunet
Subject: [taler-wallet-core] 05/05: more lint fixes
Date: Tue, 07 Apr 2020 10:12:24 +0200

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

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

commit fb2e2f89935240666de66e4b2c11125cb3b2943d
Author: Florian Dold <address@hidden>
AuthorDate: Tue Apr 7 13:37:32 2020 +0530

    more lint fixes
---
 .eslintrc.js                               |   2 +
 .vscode/settings.json                      |   1 -
 Makefile                                   |   2 +-
 package.json                               |   2 +
 src/android/index.ts                       |   9 +-
 src/crypto/primitives/kdf.ts               |   4 +-
 src/crypto/primitives/sha256.ts            |  10 +--
 src/crypto/talerCrypto.ts                  |   6 +-
 src/crypto/workers/cryptoApi.ts            |  50 +++++------
 src/crypto/workers/cryptoImplementation.ts |  31 +------
 src/crypto/workers/nodeThreadWorker.ts     |  20 +++--
 src/crypto/workers/synchronousWorker.ts    |  12 ++-
 src/db.ts                                  |   4 +-
 src/headless/bank.ts                       |   2 +-
 src/headless/clk.ts                        |   8 +-
 src/headless/helpers.ts                    |   4 +-
 src/headless/merchant.ts                   |   2 +-
 src/headless/taler-wallet-cli.ts           |  13 +--
 src/operations/balance.ts                  |   4 +-
 src/operations/exchanges.ts                |   3 +-
 src/operations/pending.ts                  |  26 ++++--
 src/operations/recoup.ts                   |   4 +-
 src/operations/refresh.ts                  |  13 ++-
 src/operations/refund.ts                   |   2 +-
 src/operations/reserves.ts                 |  38 ++++-----
 src/operations/state.ts                    |   2 +-
 src/operations/tip.ts                      |   7 +-
 src/operations/withdraw.ts                 |  20 +----
 src/types/ReserveStatus.ts                 |   8 +-
 src/types/ReserveTransaction.ts            | 129 +++++++++++++++--------------
 src/types/dbTypes.ts                       |   2 +-
 src/types/history.ts                       |   1 -
 src/types/notifications.ts                 |   9 +-
 src/types/pending.ts                       |   1 -
 src/types/talerTypes.ts                    |  30 +++----
 src/types/walletTypes.ts                   |  20 ++---
 src/util/RequestThrottler.ts               |   7 +-
 src/util/amounts.ts                        |  10 +--
 src/util/codec.ts                          |   2 +-
 src/util/helpers.ts                        |  12 +--
 src/util/http.ts                           |  14 +++-
 src/util/query.ts                          |   6 +-
 src/util/talerconfig.ts                    |   2 -
 src/util/taleruri-test.ts                  |   2 +-
 src/util/taleruri.ts                       |   2 +-
 src/util/timer.ts                          |   1 -
 src/wallet.ts                              |   5 +-
 src/webex/compat.ts                        |   2 +-
 src/webex/i18n.tsx                         |  12 +--
 src/webex/messages.ts                      |  20 -----
 src/webex/pageEntryPoint.ts                |   2 +-
 src/webex/pages/add-auditor.tsx            |   4 +-
 src/webex/pages/auditors.tsx               |  21 +++--
 src/webex/pages/pay.tsx                    |   2 +-
 src/webex/pages/payback.tsx                |   2 +-
 src/webex/pages/popup.tsx                  |  26 ++----
 src/webex/pages/refund.tsx                 |   6 +-
 src/webex/pages/reset-required.tsx         |   8 +-
 src/webex/pages/return-coins.tsx           |   5 +-
 src/webex/pages/tip.tsx                    |  19 ++---
 src/webex/pages/welcome.tsx                |   4 +-
 src/webex/pages/withdraw.tsx               |  23 +++--
 src/webex/renderHtml.tsx                   |  68 +++++++++------
 yarn.lock                                  | 116 +++++++++++++++++++++++++-
 64 files changed, 489 insertions(+), 415 deletions(-)

diff --git a/.eslintrc.js b/.eslintrc.js
index 1cd05edd..0d84f0f5 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -7,6 +7,7 @@ module.exports = {
     "plugin:@typescript-eslint/eslint-recommended",
     "plugin:@typescript-eslint/recommended",
     "plugin:react/recommended",
+    "plugin:react-hooks/recommended",
   ],
   settings: {
     "react": {
@@ -18,6 +19,7 @@ module.exports = {
     "no-constant-condition": ["error", { "checkLoops": false }],
     "prefer-const": ["warn", { destructuring: "all" }],
     "@typescript-eslint/camelcase": "off",
+    "@typescript-eslint/ban-ts-ignore": "off",
     "@typescript-eslint/no-explicit-any": "off",
     "@typescript-eslint/no-unused-vars": ["warn", { args: "none" }],
     "@typescript-eslint/explicit-function-return-type": [
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 6482c5da..7f8edcba 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -34,7 +34,6 @@
         },
         "**/*.js.map": true
     },
-    "tslint.enable": true,
     "editor.wrappingIndent": "same",
     "editor.tabSize": 2
 }
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 46e7e0b2..6d0bacbb 100644
--- a/Makefile
+++ b/Makefile
@@ -82,4 +82,4 @@ rollup: tsc
 
 .PHONY: lint
 lint:
-       ./node_modules/.bin/eslint 'src/**/*' --ext '.js,.ts,.tsx'
+       ./node_modules/.bin/eslint --ext '.js,.ts,.tsx' 'src'
diff --git a/package.json b/package.json
index 91e263af..eac8d69c 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,9 @@
     "@typescript-eslint/parser": "^2.26.0",
     "ava": "^3.6.0",
     "eslint": "^6.8.0",
+    "eslint-config-airbnb-typescript": "^7.2.0",
     "eslint-plugin-import": "^2.20.2",
+    "eslint-plugin-jsx-a11y": "^6.2.3",
     "eslint-plugin-react": "^7.19.0",
     "eslint-plugin-react-hooks": "^3.0.0",
     "jed": "^1.1.1",
diff --git a/src/android/index.ts b/src/android/index.ts
index 9c7efc0e..019fe1fb 100644
--- a/src/android/index.ts
+++ b/src/android/index.ts
@@ -32,7 +32,6 @@ import {
   Headers,
 } from "../util/http";
 import { NodeHttpLib } from "../headless/NodeHttpLib";
-import { OperationFailedAndReportedError } from "../operations/errors";
 import { WalletNotification } from "../types/notifications";
 
 // @ts-ignore: special built-in module
@@ -99,7 +98,7 @@ export class AndroidHttpLib implements HttpRequestLibrary {
     }
   }
 
-  handleTunnelResponse(msg: any) {
+  handleTunnelResponse(msg: any): void {
     const myId = msg.id;
     const p = this.requestMap[myId];
     if (!p) {
@@ -123,7 +122,7 @@ export class AndroidHttpLib implements HttpRequestLibrary {
   }
 }
 
-function sendAkonoMessage(m: string) {
+function sendAkonoMessage(m: string): void {
   // @ts-ignore
   globalThis.__akono_sendMessage(m);
 }
@@ -266,7 +265,7 @@ class AndroidWalletMessageHandler {
   }
 }
 
-export function installAndroidWalletListener() {
+export function installAndroidWalletListener(): void {
   // @ts-ignore
   const sendMessage: (m: string) => void = globalThis.__akono_sendMessage;
   if (typeof sendMessage !== "function") {
@@ -276,7 +275,7 @@ export function installAndroidWalletListener() {
     throw new Error(errMsg);
   }
   const handler = new AndroidWalletMessageHandler();
-  const onMessage = async (msgStr: any) => {
+  const onMessage = async (msgStr: any): Promise<void> => {
     if (typeof msgStr !== "string") {
       console.error("expected string as message");
       return;
diff --git a/src/crypto/primitives/kdf.ts b/src/crypto/primitives/kdf.ts
index 03deb372..edc681bc 100644
--- a/src/crypto/primitives/kdf.ts
+++ b/src/crypto/primitives/kdf.ts
@@ -51,11 +51,11 @@ export function hmac(
   return digest(b2);
 }
 
-export function hmacSha512(key: Uint8Array, message: Uint8Array) {
+export function hmacSha512(key: Uint8Array, message: Uint8Array): Uint8Array {
   return hmac(sha512, 128, key, message);
 }
 
-export function hmacSha256(key: Uint8Array, message: Uint8Array) {
+export function hmacSha256(key: Uint8Array, message: Uint8Array): Uint8Array {
   return hmac(sha256, 64, key, message);
 }
 
diff --git a/src/crypto/primitives/sha256.ts b/src/crypto/primitives/sha256.ts
index ed88b5ff..97723dbf 100644
--- a/src/crypto/primitives/sha256.ts
+++ b/src/crypto/primitives/sha256.ts
@@ -215,7 +215,7 @@ export class HashSha256 {
   }
 
   // Cleans internal buffers and re-initializes hash state.
-  clean() {
+  clean(): void {
     for (let i = 0; i < this.buffer.length; i++) {
       this.buffer[i] = 0;
     }
@@ -306,14 +306,14 @@ export class HashSha256 {
   }
 
   // Internal function for use in HMAC for optimization.
-  _saveState(out: Uint32Array) {
+  _saveState(out: Uint32Array): void {
     for (let i = 0; i < this.state.length; i++) {
       out[i] = this.state[i];
     }
   }
 
   // Internal function for use in HMAC for optimization.
-  _restoreState(from: Uint32Array, bytesHashed: number) {
+  _restoreState(from: Uint32Array, bytesHashed: number): void {
     for (let i = 0; i < this.state.length; i++) {
       this.state[i] = from[i];
     }
@@ -376,7 +376,7 @@ export class HMAC {
   }
 
   // Cleans HMAC state.
-  clean() {
+  clean(): void {
     for (let i = 0; i < this.istate.length; i++) {
       this.ostate[i] = this.istate[i] = 0;
     }
@@ -418,7 +418,7 @@ export function sha256(data: Uint8Array): Uint8Array {
 }
 
 // Returns HMAC-SHA256 of data under the key.
-export function hmacSha256(key: Uint8Array, data: Uint8Array) {
+export function hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array {
   const h = new HMAC(key).update(data);
   const digest = h.digest();
   h.clean();
diff --git a/src/crypto/talerCrypto.ts b/src/crypto/talerCrypto.ts
index 3da4f4a4..457009a0 100644
--- a/src/crypto/talerCrypto.ts
+++ b/src/crypto/talerCrypto.ts
@@ -191,12 +191,12 @@ function kdfMod(
   }
 }
 
-export function stringToBytes(s: string) {
+export function stringToBytes(s: string): Uint8Array {
   const te = new TextEncoder();
   return te.encode(s);
 }
 
-function loadBigInt(arr: Uint8Array) {
+function loadBigInt(arr: Uint8Array): bigint.BigInteger {
   return bigint.fromArray(Array.from(arr), 256, false);
 }
 
@@ -219,7 +219,7 @@ function rsaBlindingKeyDerive(
  * @param r KDF result
  * @param n RSA modulus of the public key
  */
-function rsaGcdValidate(r: bigint.BigInteger, n: bigint.BigInteger) {
+function rsaGcdValidate(r: bigint.BigInteger, n: bigint.BigInteger): void {
   const t = bigint.gcd(r, n);
   if (!t.equals(bigint.one)) {
     throw Error("malicious RSA public key");
diff --git a/src/crypto/workers/cryptoApi.ts b/src/crypto/workers/cryptoApi.ts
index 24a43ff4..456bf309 100644
--- a/src/crypto/workers/cryptoApi.ts
+++ b/src/crypto/workers/cryptoApi.ts
@@ -34,13 +34,7 @@ import {
 
 import { CryptoWorker } from "./cryptoWorker";
 
-import {
-  RecoupRequest,
-  CoinDepositPermission,
-  RecoupConfirmation,
-  ExchangeSignKeyJson,
-  EddsaPublicKeyString,
-} from "../../types/talerTypes";
+import { RecoupRequest, CoinDepositPermission } from "../../types/talerTypes";
 
 import {
   BenchmarkResult,
@@ -154,7 +148,7 @@ export class CryptoApi {
   /**
    * Terminate all worker threads.
    */
-  terminateWorkers() {
+  terminateWorkers(): void {
     for (const worker of this.workers) {
       if (worker.w) {
         CryptoApi.enableTracing && console.log("terminating worker");
@@ -172,7 +166,7 @@ export class CryptoApi {
     }
   }
 
-  stop() {
+  stop(): void {
     this.terminateWorkers();
     this.stopped = true;
   }
@@ -192,11 +186,14 @@ export class CryptoApi {
     }
     ws.currentWorkItem = work;
     this.numBusy++;
+    let worker: CryptoWorker;
     if (!ws.w) {
-      const w = this.workerFactory.startWorker();
-      w.onmessage = (m: MessageEvent) => this.handleWorkerMessage(ws, m);
-      w.onerror = (e: ErrorEvent) => this.handleWorkerError(ws, e);
-      ws.w = w;
+      worker = this.workerFactory.startWorker();
+      worker.onmessage = (m: MessageEvent) => this.handleWorkerMessage(ws, m);
+      worker.onerror = (e: ErrorEvent) => this.handleWorkerError(ws, e);
+      ws.w = worker;
+    } else {
+      worker = ws.w;
     }
 
     const msg: any = {
@@ -206,28 +203,28 @@ export class CryptoApi {
     };
     this.resetWorkerTimeout(ws);
     work.startTime = timer.performanceNow();
-    setTimeout(() => ws.w!.postMessage(msg), 0);
+    setTimeout(() => worker.postMessage(msg), 0);
   }
 
-  resetWorkerTimeout(ws: WorkerState) {
+  resetWorkerTimeout(ws: WorkerState): void {
     if (ws.terminationTimerHandle !== null) {
       ws.terminationTimerHandle.clear();
       ws.terminationTimerHandle = null;
     }
-    const destroy = () => {
+    const destroy = (): void => {
       // terminate worker if it's idle
       if (ws.w && ws.currentWorkItem === null) {
-        ws.w!.terminate();
+        ws.w.terminate();
         ws.w = null;
       }
     };
     ws.terminationTimerHandle = timer.after(15 * 1000, destroy);
   }
 
-  handleWorkerError(ws: WorkerState, e: ErrorEvent) {
+  handleWorkerError(ws: WorkerState, e: ErrorEvent): void {
     if (ws.currentWorkItem) {
       console.error(
-        `error in worker during ${ws.currentWorkItem!.operation}`,
+        `error in worker during ${ws.currentWorkItem.operation}`,
         e,
       );
     } else {
@@ -235,8 +232,10 @@ export class CryptoApi {
     }
     console.error(e.message);
     try {
-      ws.w!.terminate();
-      ws.w = null;
+      if (ws.w) {
+        ws.w.terminate();
+        ws.w = null;
+      }
     } catch (e) {
       console.error(e);
     }
@@ -248,19 +247,22 @@ export class CryptoApi {
     this.findWork(ws);
   }
 
-  private findWork(ws: WorkerState) {
+  private findWork(ws: WorkerState): void {
     // try to find more work for this worker
     for (let i = 0; i < NUM_PRIO; i++) {
       const q = this.workQueues[NUM_PRIO - i - 1];
       if (q.length !== 0) {
-        const work: WorkItem = q.shift()!;
+        const work: WorkItem | undefined = q.shift();
+        if (!work) {
+          continue;
+        }
         this.wake(ws, work);
         return;
       }
     }
   }
 
-  handleWorkerMessage(ws: WorkerState, msg: MessageEvent) {
+  handleWorkerMessage(ws: WorkerState, msg: MessageEvent): void {
     const id = msg.data.id;
     if (typeof id !== "number") {
       console.error("rpc id must be number");
diff --git a/src/crypto/workers/cryptoImplementation.ts 
b/src/crypto/workers/cryptoImplementation.ts
index 0a3c217a..96ad29bc 100644
--- a/src/crypto/workers/cryptoImplementation.ts
+++ b/src/crypto/workers/cryptoImplementation.ts
@@ -36,13 +36,7 @@ import {
   CoinSourceType,
 } from "../../types/dbTypes";
 
-import {
-  CoinDepositPermission,
-  RecoupRequest,
-  RecoupConfirmation,
-  ExchangeSignKeyJson,
-  EddsaPublicKeyString,
-} from "../../types/talerTypes";
+import { CoinDepositPermission, RecoupRequest } from "../../types/talerTypes";
 import {
   BenchmarkResult,
   PlanchetCreationResult,
@@ -70,11 +64,7 @@ import {
 } from "../talerCrypto";
 import { randomBytes } from "../primitives/nacl-fast";
 import { kdf } from "../primitives/kdf";
-import {
-  Timestamp,
-  getTimestampNow,
-  timestampIsBetween,
-} from "../../util/time";
+import { Timestamp, getTimestampNow } from "../../util/time";
 
 enum SignaturePurpose {
   RESERVE_WITHDRAW = 1200,
@@ -144,24 +134,9 @@ function buildSigPS(purposeNum: number): 
SignaturePurposeBuilder {
   return new SignaturePurposeBuilder(purposeNum);
 }
 
-function checkSignKeyOkay(
-  key: string,
-  exchangeKeys: ExchangeSignKeyJson[],
-): boolean {
-  const now = getTimestampNow();
-  for (const k of exchangeKeys) {
-    if (k.key == key) {
-      return timestampIsBetween(now, k.stamp_start, k.stamp_end);
-    }
-  }
-  return false;
-}
-
 export class CryptoImplementation {
   static enableTracing = false;
 
-  constructor() {}
-
   /**
    * Create a pre-coin of the given denomination to be withdrawn from then 
given
    * reserve.
@@ -535,7 +510,7 @@ export class CryptoImplementation {
     let time_eddsa_create = 0;
     for (let i = 0; i < repetitions; i++) {
       const start = timer.performanceNow();
-      const pair = createEddsaKeyPair();
+      createEddsaKeyPair();
       time_eddsa_create += timer.performanceNow() - start;
     }
 
diff --git a/src/crypto/workers/nodeThreadWorker.ts 
b/src/crypto/workers/nodeThreadWorker.ts
index d289c14b..6c9dfc56 100644
--- a/src/crypto/workers/nodeThreadWorker.ts
+++ b/src/crypto/workers/nodeThreadWorker.ts
@@ -1,5 +1,3 @@
-import { CryptoWorkerFactory } from "./cryptoApi";
-
 /*
  This file is part of TALER
  (C) 2016 GNUnet e.V.
@@ -16,8 +14,10 @@ import { CryptoWorkerFactory } from "./cryptoApi";
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-// tslint:disable:no-var-requires
-
+/**
+ * Imports
+ */
+import { CryptoWorkerFactory } from "./cryptoApi";
 import { CryptoWorker } from "./cryptoWorker";
 import os from "os";
 import { CryptoImplementation } from "./cryptoImplementation";
@@ -55,7 +55,7 @@ const workerCode = `
  * This function is executed in the worker thread to handle
  * a message.
  */
-export function handleWorkerMessage(msg: any) {
+export function handleWorkerMessage(msg: any): void {
   const args = msg.args;
   if (!Array.isArray(args)) {
     console.error("args must be array");
@@ -72,7 +72,7 @@ export function handleWorkerMessage(msg: any) {
     return;
   }
 
-  const handleRequest = async () => {
+  const handleRequest = async (): Promise<void> => {
     const impl = new CryptoImplementation();
 
     if (!(operation in impl)) {
@@ -82,6 +82,7 @@ export function handleWorkerMessage(msg: any) {
 
     try {
       const result = (impl as any)[operation](...args);
+      // eslint-disable-next-line @typescript-eslint/no-var-requires
       const worker_threads = require("worker_threads");
       const p = worker_threads.parentPort;
       worker_threads.parentPort?.postMessage;
@@ -101,7 +102,7 @@ export function handleWorkerMessage(msg: any) {
   });
 }
 
-export function handleWorkerError(e: Error) {
+export function handleWorkerError(e: Error): void {
   console.log("got error from worker", e);
 }
 
@@ -135,6 +136,7 @@ class NodeThreadCryptoWorker implements CryptoWorker {
   private nodeWorker: import("worker_threads").Worker;
 
   constructor() {
+    // eslint-disable-next-line @typescript-eslint/no-var-requires
     const worker_threads = require("worker_threads");
     this.nodeWorker = new worker_threads.Worker(workerCode, { eval: true });
     this.nodeWorker.on("error", (err: Error) => {
@@ -168,14 +170,14 @@ class NodeThreadCryptoWorker implements CryptoWorker {
   /**
    * Send a message to the worker thread.
    */
-  postMessage(msg: any) {
+  postMessage(msg: any): void {
     this.nodeWorker.postMessage(msg);
   }
 
   /**
    * Forcibly terminate the worker thread.
    */
-  terminate() {
+  terminate(): void {
     this.nodeWorker.terminate();
   }
 }
diff --git a/src/crypto/workers/synchronousWorker.ts 
b/src/crypto/workers/synchronousWorker.ts
index f4bcf396..2cc74097 100644
--- a/src/crypto/workers/synchronousWorker.ts
+++ b/src/crypto/workers/synchronousWorker.ts
@@ -70,13 +70,17 @@ export class SynchronousCryptoWorker {
     }
   }
 
-  private dispatchMessage(msg: any) {
+  private dispatchMessage(msg: any): void {
     if (this.onmessage) {
       this.onmessage({ data: msg });
     }
   }
 
-  private async handleRequest(operation: string, id: number, args: string[]) {
+  private async handleRequest(
+    operation: string,
+    id: number,
+    args: string[],
+  ): Promise<void> {
     const impl = new CryptoImplementation();
 
     if (!(operation in impl)) {
@@ -102,7 +106,7 @@ export class SynchronousCryptoWorker {
   /**
    * Send a message to the worker thread.
    */
-  postMessage(msg: any) {
+  postMessage(msg: any): void {
     const args = msg.args;
     if (!Array.isArray(args)) {
       console.error("args must be array");
@@ -127,7 +131,7 @@ export class SynchronousCryptoWorker {
   /**
    * Forcibly terminate the worker thread.
    */
-  terminate() {
+  terminate(): void {
     // This is a no-op.
   }
 }
diff --git a/src/db.ts b/src/db.ts
index da43cf82..18efbf2b 100644
--- a/src/db.ts
+++ b/src/db.ts
@@ -23,7 +23,7 @@ export function openTalerDatabase(
     db: IDBDatabase,
     oldVersion: number,
     newVersion: number,
-  ) => {
+  ): void => {
     switch (oldVersion) {
       case 0: // DB does not exist yet
         for (const n in Stores) {
@@ -53,6 +53,6 @@ export function openTalerDatabase(
   );
 }
 
-export function deleteTalerDatabase(idbFactory: IDBFactory) {
+export function deleteTalerDatabase(idbFactory: IDBFactory): void {
   Database.deleteDatabase(idbFactory, TALER_DB_NAME);
 }
diff --git a/src/headless/bank.ts b/src/headless/bank.ts
index 81b5293b..2177908a 100644
--- a/src/headless/bank.ts
+++ b/src/headless/bank.ts
@@ -95,7 +95,7 @@ export class Bank {
     amount: string,
     reservePub: string,
     exchangePaytoUri: string,
-  ) {
+  ): Promise<void> {
     const reqUrl = new URL("testing/withdraw", this.bankBaseUrl).href;
 
     const body = {
diff --git a/src/headless/clk.ts b/src/headless/clk.ts
index 09f758ad..a905464b 100644
--- a/src/headless/clk.ts
+++ b/src/headless/clk.ts
@@ -117,7 +117,7 @@ export class CommandGroup<GN extends keyof any, TG> {
     private scArgs: SubcommandArgs,
   ) {}
 
-  action(f: ActionFn<TG>) {
+  action(f: ActionFn<TG>): void {
     if (this.myAction) {
       throw Error("only one action supported per command");
     }
@@ -269,7 +269,7 @@ export class CommandGroup<GN extends keyof any, TG> {
     return cg as any;
   }
 
-  printHelp(progName: string, parents: CommandGroup<any, any>[]) {
+  printHelp(progName: string, parents: CommandGroup<any, any>[]): void {
     let usageSpec = "";
     for (const p of parents) {
       usageSpec += (p.name ?? progName) + " ";
@@ -320,7 +320,7 @@ export class CommandGroup<GN extends keyof any, TG> {
     parents: CommandGroup<any, any>[],
     unparsedArgs: string[],
     parsedArgs: any,
-  ) {
+  ): void {
     let posArgIndex = 0;
     let argsTerminated = false;
     let i;
@@ -507,7 +507,7 @@ export class Program<PN extends keyof any, T> {
     });
   }
 
-  run() {
+  run(): void {
     const args = process.argv;
     if (args.length < 2) {
       console.error(
diff --git a/src/headless/helpers.ts b/src/headless/helpers.ts
index 11e2c90d..47a0844b 100644
--- a/src/headless/helpers.ts
+++ b/src/headless/helpers.ts
@@ -106,7 +106,7 @@ export async function getDefaultNodeWallet(
     myHttpLib = new NodeHttpLib();
   }
 
-  const myVersionChange = () => {
+  const myVersionChange = (): Promise<void> => {
     console.error("version change requested, should not happen");
     throw Error();
   };
@@ -141,7 +141,7 @@ export async function withdrawTestBalance(
   amount = "TESTKUDOS:10",
   bankBaseUrl = "https://bank.test.taler.net/";,
   exchangeBaseUrl = "https://exchange.test.taler.net/";,
-) {
+): Promise<void> {
   const reserveResponse = await myWallet.createReserve({
     amount: amounts.parseOrThrow(amount),
     exchange: exchangeBaseUrl,
diff --git a/src/headless/merchant.ts b/src/headless/merchant.ts
index 27e7e3f2..b479c5a8 100644
--- a/src/headless/merchant.ts
+++ b/src/headless/merchant.ts
@@ -65,7 +65,7 @@ export class MerchantBackendConnection {
 
   constructor(public merchantBaseUrl: string, public apiKey: string) {}
 
-  async authorizeTip(amount: string, justification: string) {
+  async authorizeTip(amount: string, justification: string): Promise<string> {
     const reqUrl = new URL("tip-authorize", this.merchantBaseUrl).href;
     const tipReq = {
       amount,
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index a7dcce9a..3da01a2d 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -22,7 +22,7 @@ import { runIntegrationTest, runIntegrationTestBasic } from 
"./integrationtest";
 import { Wallet } from "../wallet";
 import qrcodeGenerator from "qrcode-generator";
 import * as clk from "./clk";
-import { BridgeIDBFactory, MemoryBackend } from "idb-bridge";
+import { BridgeIDBFactory } from "idb-bridge";
 import { Logger } from "../util/logging";
 import { Amounts } from "../util/amounts";
 import { decodeCrock } from "../crypto/talerCrypto";
@@ -46,7 +46,7 @@ async function doPay(
   wallet: Wallet,
   payUrl: string,
   options: { alwaysYes: boolean } = { alwaysYes: true },
-) {
+): Promise<void> {
   const result = await wallet.preparePayForUri(payUrl);
   if (result.status === "error") {
     console.error("Could not pay:", result.error);
@@ -89,21 +89,22 @@ async function doPay(
   }
 
   if (pay) {
-    const payRes = await wallet.confirmPay(result.proposalId, undefined);
+    await wallet.confirmPay(result.proposalId, undefined);
     console.log("paid!");
   } else {
     console.log("not paying");
   }
 }
 
-function applyVerbose(verbose: boolean) {
+function applyVerbose(verbose: boolean): void {
   if (verbose) {
     console.log("enabled verbose logging");
     BridgeIDBFactory.enableTracing = true;
   }
 }
 
-function printVersion() {
+function printVersion(): void {
+  // eslint-disable-next-line @typescript-eslint/no-var-requires
   const info = require("../../../package.json");
   console.log(`${info.version}`);
   process.exit(0);
@@ -329,7 +330,7 @@ exchangesCli
   .flag("force", ["-f", "--force"])
   .action(async (args) => {
     await withWallet(args, async (wallet) => {
-      const res = await wallet.updateExchangeFromUrl(
+      await wallet.updateExchangeFromUrl(
         args.exchangesUpdateCmd.url,
         args.exchangesUpdateCmd.force,
       );
diff --git a/src/operations/balance.ts b/src/operations/balance.ts
index 7c2d0e3f..c369af19 100644
--- a/src/operations/balance.ts
+++ b/src/operations/balance.ts
@@ -18,9 +18,9 @@
  * Imports.
  */
 import { WalletBalance, WalletBalanceEntry } from "../types/walletTypes";
-import { Database, TransactionHandle } from "../util/query";
+import { TransactionHandle } from "../util/query";
 import { InternalWalletState } from "./state";
-import { Stores, TipRecord, CoinStatus } from "../types/dbTypes";
+import { Stores, CoinStatus } from "../types/dbTypes";
 import * as Amounts from "../util/amounts";
 import { AmountJson } from "../util/amounts";
 import { Logger } from "../util/logging";
diff --git a/src/operations/exchanges.ts b/src/operations/exchanges.ts
index 27fed0b6..a9e5158e 100644
--- a/src/operations/exchanges.ts
+++ b/src/operations/exchanges.ts
@@ -460,7 +460,8 @@ export async function updateExchangeFromUrl(
   baseUrl: string,
   forceNow = false,
 ): Promise<ExchangeRecord> {
-  const onOpErr = (e: OperationError): Promise<void> => setExchangeError(ws, 
baseUrl, e);
+  const onOpErr = (e: OperationError): Promise<void> =>
+    setExchangeError(ws, baseUrl, e);
   return await guardOperationException(
     () => updateExchangeFromUrlImpl(ws, baseUrl, forceNow),
     onOpErr,
diff --git a/src/operations/pending.ts b/src/operations/pending.ts
index cee929aa..3e548a27 100644
--- a/src/operations/pending.ts
+++ b/src/operations/pending.ts
@@ -37,7 +37,7 @@ import {
 } from "../util/time";
 import { TransactionHandle } from "../util/query";
 import { InternalWalletState } from "./state";
-import { getBalances, getBalancesInsideTransaction } from "./balance";
+import { getBalancesInsideTransaction } from "./balance";
 import { ReserveType } from "../types/history";
 
 function updateRetryDelay(
@@ -286,13 +286,23 @@ async function gatherProposalPending(
       if (onlyDue) {
         return;
       }
-      resp.pendingOperations.push({
-        type: PendingOperationType.ProposalChoice,
-        givesLifeness: false,
-        merchantBaseUrl: proposal.download!!.contractData.merchantBaseUrl,
-        proposalId: proposal.proposalId,
-        proposalTimestamp: proposal.timestamp,
-      });
+      const dl = proposal.download;
+      if (!dl) {
+        resp.pendingOperations.push({
+          type: PendingOperationType.Bug,
+          message: "proposal is in invalid state",
+          details: {},
+          givesLifeness: false,
+        });
+      } else {
+        resp.pendingOperations.push({
+          type: PendingOperationType.ProposalChoice,
+          givesLifeness: false,
+          merchantBaseUrl: dl.contractData.merchantBaseUrl,
+          proposalId: proposal.proposalId,
+          proposalTimestamp: proposal.timestamp,
+        });
+      }
     } else if (proposal.proposalStatus == ProposalStatus.DOWNLOADING) {
       resp.nextRetryDelay = updateRetryDelay(
         resp.nextRetryDelay,
diff --git a/src/operations/recoup.ts b/src/operations/recoup.ts
index 97bc3956..e1c2325d 100644
--- a/src/operations/recoup.ts
+++ b/src/operations/recoup.ts
@@ -279,7 +279,7 @@ async function recoupRefreshCoin(
 async function resetRecoupGroupRetry(
   ws: InternalWalletState,
   recoupGroupId: string,
-) {
+): Promise<void> {
   await ws.db.mutate(Stores.recoupGroups, recoupGroupId, (x) => {
     if (x.retryInfo.active) {
       x.retryInfo = initRetryInfo();
@@ -294,7 +294,7 @@ export async function processRecoupGroup(
   forceNow = false,
 ): Promise<void> {
   await ws.memoProcessRecoup.memo(recoupGroupId, async () => {
-    const onOpErr = (e: OperationError) =>
+    const onOpErr = (e: OperationError): Promise<void> =>
       incrementRecoupRetry(ws, recoupGroupId, e);
     return await guardOperationException(
       async () => await processRecoupGroupImpl(ws, recoupGroupId, forceNow),
diff --git a/src/operations/refresh.ts b/src/operations/refresh.ts
index 23d192e0..92476933 100644
--- a/src/operations/refresh.ts
+++ b/src/operations/refresh.ts
@@ -28,7 +28,7 @@ import {
   CoinSourceType,
 } from "../types/dbTypes";
 import { amountToPretty } from "../util/helpers";
-import { Database, TransactionHandle } from "../util/query";
+import { TransactionHandle } from "../util/query";
 import { InternalWalletState } from "./state";
 import { Logger } from "../util/logging";
 import { getWithdrawDenomList } from "./withdraw";
@@ -390,8 +390,7 @@ async function refreshReveal(
       console.error("denom not found");
       continue;
     }
-    const pc =
-      refreshSession.planchetsForGammas[refreshSession.norevealIndex!][i];
+    const pc = refreshSession.planchetsForGammas[norevealIndex][i];
     const denomSig = await ws.cryptoApi.rsaUnblind(
       respJson.ev_sigs[i].ev_sig,
       pc.blindingKey,
@@ -485,7 +484,7 @@ export async function processRefreshGroup(
   forceNow = false,
 ): Promise<void> {
   await ws.memoProcessRefresh.memo(refreshGroupId, async () => {
-    const onOpErr = (e: OperationError) =>
+    const onOpErr = (e: OperationError): Promise<void> =>
       incrementRefreshRetry(ws, refreshGroupId, e);
     return await guardOperationException(
       async () => await processRefreshGroupImpl(ws, refreshGroupId, forceNow),
@@ -497,7 +496,7 @@ export async function processRefreshGroup(
 async function resetRefreshGroupRetry(
   ws: InternalWalletState,
   refreshSessionId: string,
-) {
+): Promise<void> {
   await ws.db.mutate(Stores.refreshGroups, refreshSessionId, (x) => {
     if (x.retryInfo.active) {
       x.retryInfo = initRetryInfo();
@@ -510,7 +509,7 @@ async function processRefreshGroupImpl(
   ws: InternalWalletState,
   refreshGroupId: string,
   forceNow: boolean,
-) {
+): Promise<void> {
   if (forceNow) {
     await resetRefreshGroupRetry(ws, refreshGroupId);
   }
@@ -532,7 +531,7 @@ async function processRefreshSession(
   ws: InternalWalletState,
   refreshGroupId: string,
   coinIndex: number,
-) {
+): Promise<void> {
   logger.trace(
     `processing refresh session for coin ${coinIndex} of group 
${refreshGroupId}`,
   );
diff --git a/src/operations/refund.ts b/src/operations/refund.ts
index af9a4889..8feb2bae 100644
--- a/src/operations/refund.ts
+++ b/src/operations/refund.ts
@@ -465,7 +465,7 @@ async function processPurchaseApplyRefundImpl(
       // Avoid duplicates
       const refreshCoinsMap: { [coinPub: string]: CoinPublicKey } = {};
 
-      const modCoin = async (perm: MerchantRefundPermission) => {
+      const modCoin = async (perm: MerchantRefundPermission): Promise<void> => 
{
         const c = await tx.get(Stores.coins, perm.coin_pub);
         if (!c) {
           console.warn("coin not found, can't apply refund");
diff --git a/src/operations/reserves.ts b/src/operations/reserves.ts
index 2ee1b358..153ad6b8 100644
--- a/src/operations/reserves.ts
+++ b/src/operations/reserves.ts
@@ -44,10 +44,7 @@ import {
   getExchangeTrust,
   getExchangePaytoUri,
 } from "./exchanges";
-import {
-  WithdrawOperationStatusResponse,
-  codecForWithdrawOperationStatusResponse,
-} from "../types/talerTypes";
+import { codecForWithdrawOperationStatusResponse } from "../types/talerTypes";
 import { assertUnreachable } from "../util/assertUnreachable";
 import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
 import { randomBytes } from "../crypto/primitives/nacl-fast";
@@ -71,7 +68,10 @@ import {
 
 const logger = new Logger("reserves.ts");
 
-async function resetReserveRetry(ws: InternalWalletState, reservePub: string) {
+async function resetReserveRetry(
+  ws: InternalWalletState,
+  reservePub: string,
+): Promise<void> {
   await ws.db.mutate(Stores.reserves, reservePub, (x) => {
     if (x.retryInfo.active) {
       x.retryInfo = initRetryInfo();
@@ -101,8 +101,6 @@ export async function createReserve(
     reserveStatus = ReserveRecordStatus.UNCONFIRMED;
   }
 
-  const currency = req.amount.currency;
-
   const reserveRecord: ReserveRecord = {
     timestampCreated: now,
     exchangeBaseUrl: canonExchange,
@@ -251,7 +249,7 @@ export async function processReserve(
   forceNow = false,
 ): Promise<void> {
   return ws.memoProcessReserve.memo(reservePub, async () => {
-    const onOpError = (err: OperationError) =>
+    const onOpError = (err: OperationError): Promise<void> =>
       incrementReserveRetry(ws, reservePub, err);
     await guardOperationException(
       () => processReserveImpl(ws, reservePub, forceNow),
@@ -280,7 +278,8 @@ async function registerReserveWithBank(
   if (reserve.timestampReserveInfoPosted) {
     throw Error("bank claims that reserve info selection is not done");
   }
-  const bankResp = await ws.http.postJson(bankStatusUrl, {
+  // FIXME: parse bank response
+  await ws.http.postJson(bankStatusUrl, {
     reserve_pub: reservePub,
     selected_exchange: reserve.exchangeWire,
   });
@@ -305,7 +304,7 @@ export async function processReserveBankStatus(
   ws: InternalWalletState,
   reservePub: string,
 ): Promise<void> {
-  const onOpError = (err: OperationError) =>
+  const onOpError = (err: OperationError): Promise<void> =>
     incrementReserveRetry(ws, reservePub, err);
   await guardOperationException(
     () => processReserveBankStatusImpl(ws, reservePub),
@@ -330,20 +329,13 @@ async function processReserveBankStatusImpl(
     return;
   }
 
-  let status: WithdrawOperationStatusResponse;
-  try {
-    const statusResp = await ws.http.get(bankStatusUrl);
-    if (statusResp.status !== 200) {
-      throw Error(
-        `unexpected status ${statusResp.status} for bank status query`,
-      );
-    }
-    status = codecForWithdrawOperationStatusResponse().decode(
-      await statusResp.json(),
-    );
-  } catch (e) {
-    throw e;
+  const statusResp = await ws.http.get(bankStatusUrl);
+  if (statusResp.status !== 200) {
+    throw Error(`unexpected status ${statusResp.status} for bank status 
query`);
   }
+  const status = codecForWithdrawOperationStatusResponse().decode(
+    await statusResp.json(),
+  );
 
   ws.notify({ type: NotificationType.Wildcard });
 
diff --git a/src/operations/state.ts b/src/operations/state.ts
index 246a7126..97d59124 100644
--- a/src/operations/state.ts
+++ b/src/operations/state.ts
@@ -49,7 +49,7 @@ export class InternalWalletState {
     this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
   }
 
-  public notify(n: WalletNotification) {
+  public notify(n: WalletNotification): void {
     logger.trace("Notification", n);
     for (const l of this.listeners) {
       const nc = JSON.parse(JSON.stringify(n));
diff --git a/src/operations/tip.ts b/src/operations/tip.ts
index 1a88dbca..6f492ea3 100644
--- a/src/operations/tip.ts
+++ b/src/operations/tip.ts
@@ -18,9 +18,7 @@ import { InternalWalletState } from "./state";
 import { parseTipUri } from "../util/taleruri";
 import { TipStatus, OperationError } from "../types/walletTypes";
 import {
-  TipPickupGetResponse,
   TipPlanchetDetail,
-  TipResponse,
   codecForTipPickupGetResponse,
   codecForTipResponse,
 } from "../types/talerTypes";
@@ -149,7 +147,8 @@ export async function processTip(
   tipId: string,
   forceNow = false,
 ): Promise<void> {
-  const onOpErr = (e: OperationError) => incrementTipRetry(ws, tipId, e);
+  const onOpErr = (e: OperationError): Promise<void> =>
+    incrementTipRetry(ws, tipId, e);
   await guardOperationException(
     () => processTipImpl(ws, tipId, forceNow),
     onOpErr,
@@ -172,7 +171,7 @@ async function processTipImpl(
   ws: InternalWalletState,
   tipId: string,
   forceNow: boolean,
-) {
+): Promise<void> {
   if (forceNow) {
     await resetTipRetry(ws, tipId);
   }
diff --git a/src/operations/withdraw.ts b/src/operations/withdraw.ts
index 70ecf9ae..1f5bfd0b 100644
--- a/src/operations/withdraw.ts
+++ b/src/operations/withdraw.ts
@@ -21,7 +21,6 @@ import {
   DenominationStatus,
   CoinStatus,
   CoinRecord,
-  PlanchetRecord,
   initRetryInfo,
   updateRetryInfoTimeout,
   CoinSourceType,
@@ -52,14 +51,10 @@ import {
   timestampCmp,
   timestampSubtractDuraction,
 } from "../util/time";
-import {
-  summarizeReserveHistory,
-  ReserveHistorySummary,
-} from "../util/reserveHistoryUtil";
 
 const logger = new Logger("withdraw.ts");
 
-function isWithdrawableDenom(d: DenominationRecord) {
+function isWithdrawableDenom(d: DenominationRecord): boolean {
   const now = getTimestampNow();
   const started = timestampCmp(now, d.stampStart) >= 0;
   const lastPossibleWithdraw = timestampSubtractDuraction(
@@ -175,8 +170,6 @@ async function processPlanchet(
   if (withdrawalGroup.withdrawn[coinIdx]) {
     return;
   }
-  if (withdrawalGroup.source.type === "reserve") {
-  }
   const planchet = withdrawalGroup.planchets[coinIdx];
   if (!planchet) {
     console.log("processPlanchet: planchet not found");
@@ -248,7 +241,6 @@ async function processPlanchet(
   };
 
   let withdrawalGroupFinished = false;
-  let summary: ReserveHistorySummary | undefined = undefined;
 
   const success = await ws.db.runWithWriteTransaction(
     [Stores.coins, Stores.withdrawalGroups, Stores.reserves],
@@ -276,12 +268,6 @@ async function processPlanchet(
         withdrawalGroupFinished = true;
       }
       await tx.put(Stores.withdrawalGroups, ws);
-      if (!planchet.isFromTip) {
-        const r = await tx.get(Stores.reserves, planchet.reservePub);
-        if (r) {
-          summary = summarizeReserveHistory(r.reserveTransactions, r.currency);
-        }
-      }
       await tx.add(Stores.coins, coin);
       return true;
     },
@@ -396,7 +382,7 @@ export async function processWithdrawGroup(
   withdrawalGroupId: string,
   forceNow = false,
 ): Promise<void> {
-  const onOpErr = (e: OperationError) =>
+  const onOpErr = (e: OperationError): Promise<void> =>
     incrementWithdrawalRetry(ws, withdrawalGroupId, e);
   await guardOperationException(
     () => processWithdrawGroupImpl(ws, withdrawalGroupId, forceNow),
@@ -407,7 +393,7 @@ export async function processWithdrawGroup(
 async function resetWithdrawalGroupRetry(
   ws: InternalWalletState,
   withdrawalGroupId: string,
-) {
+): Promise<void> {
   await ws.db.mutate(Stores.withdrawalGroups, withdrawalGroupId, (x) => {
     if (x.retryInfo.active) {
       x.retryInfo = initRetryInfo();
diff --git a/src/types/ReserveStatus.ts b/src/types/ReserveStatus.ts
index 5a3011b3..18601b9a 100644
--- a/src/types/ReserveStatus.ts
+++ b/src/types/ReserveStatus.ts
@@ -51,7 +51,7 @@ export interface ReserveStatus {
 }
 
 export const codecForReserveStatus = (): Codec<ReserveStatus> =>
-    makeCodecForObject<ReserveStatus>()
-      .property("balance", codecForString)
-      .property("history", makeCodecForList(codecForReserveTransaction()))
-      .build("ReserveStatus");
+  makeCodecForObject<ReserveStatus>()
+    .property("balance", codecForString)
+    .property("history", makeCodecForList(codecForReserveTransaction()))
+    .build("ReserveStatus");
diff --git a/src/types/ReserveTransaction.ts b/src/types/ReserveTransaction.ts
index acbd502e..bdd9b0f9 100644
--- a/src/types/ReserveTransaction.ts
+++ b/src/types/ReserveTransaction.ts
@@ -179,67 +179,72 @@ export type ReserveTransaction =
   | ReserveClosingTransaction
   | ReserveRecoupTransaction;
 
-export const codecForReserveWithdrawTransaction = (): 
Codec<ReserveWithdrawTransaction> =>
-    makeCodecForObject<ReserveWithdrawTransaction>()
-      .property("amount", codecForString)
-      .property("h_coin_envelope", codecForString)
-      .property("h_denom_pub", codecForString)
-      .property("reserve_sig", codecForString)
-      .property(
-        "type",
-        makeCodecForConstString(ReserveTransactionType.Withdraw),
-      )
-      .property("withdraw_fee", codecForString)
-      .build("ReserveWithdrawTransaction");
-
-export const codecForReserveCreditTransaction = (): 
Codec<ReserveCreditTransaction> =>
-    makeCodecForObject<ReserveCreditTransaction>()
-      .property("amount", codecForString)
-      .property("sender_account_url", codecForString)
-      .property("timestamp", codecForTimestamp)
-      .property("wire_reference", codecForString)
-      .property("type", makeCodecForConstString(ReserveTransactionType.Credit))
-      .build("ReserveCreditTransaction");
-
-export const codecForReserveClosingTransaction = (): 
Codec<ReserveClosingTransaction> =>
-    makeCodecForObject<ReserveClosingTransaction>()
-      .property("amount", codecForString)
-      .property("closing_fee", codecForString)
-      .property("exchange_pub", codecForString)
-      .property("exchange_sig", codecForString)
-      .property("h_wire", codecForString)
-      .property("timestamp", codecForTimestamp)
-      .property("type", 
makeCodecForConstString(ReserveTransactionType.Closing))
-      .property("wtid", codecForString)
-      .build("ReserveClosingTransaction");
-
-export const codecForReserveRecoupTransaction = (): 
Codec<ReserveRecoupTransaction> =>
-    makeCodecForObject<ReserveRecoupTransaction>()
-      .property("amount", codecForString)
-      .property("coin_pub", codecForString)
-      .property("exchange_pub", codecForString)
-      .property("exchange_sig", codecForString)
-      .property("timestamp", codecForTimestamp)
-      .property("type", makeCodecForConstString(ReserveTransactionType.Recoup))
-      .build("ReserveRecoupTransaction");
+export const codecForReserveWithdrawTransaction = (): Codec<
+  ReserveWithdrawTransaction
+> =>
+  makeCodecForObject<ReserveWithdrawTransaction>()
+    .property("amount", codecForString)
+    .property("h_coin_envelope", codecForString)
+    .property("h_denom_pub", codecForString)
+    .property("reserve_sig", codecForString)
+    .property("type", makeCodecForConstString(ReserveTransactionType.Withdraw))
+    .property("withdraw_fee", codecForString)
+    .build("ReserveWithdrawTransaction");
+
+export const codecForReserveCreditTransaction = (): Codec<
+  ReserveCreditTransaction
+> =>
+  makeCodecForObject<ReserveCreditTransaction>()
+    .property("amount", codecForString)
+    .property("sender_account_url", codecForString)
+    .property("timestamp", codecForTimestamp)
+    .property("wire_reference", codecForString)
+    .property("type", makeCodecForConstString(ReserveTransactionType.Credit))
+    .build("ReserveCreditTransaction");
+
+export const codecForReserveClosingTransaction = (): Codec<
+  ReserveClosingTransaction
+> =>
+  makeCodecForObject<ReserveClosingTransaction>()
+    .property("amount", codecForString)
+    .property("closing_fee", codecForString)
+    .property("exchange_pub", codecForString)
+    .property("exchange_sig", codecForString)
+    .property("h_wire", codecForString)
+    .property("timestamp", codecForTimestamp)
+    .property("type", makeCodecForConstString(ReserveTransactionType.Closing))
+    .property("wtid", codecForString)
+    .build("ReserveClosingTransaction");
+
+export const codecForReserveRecoupTransaction = (): Codec<
+  ReserveRecoupTransaction
+> =>
+  makeCodecForObject<ReserveRecoupTransaction>()
+    .property("amount", codecForString)
+    .property("coin_pub", codecForString)
+    .property("exchange_pub", codecForString)
+    .property("exchange_sig", codecForString)
+    .property("timestamp", codecForTimestamp)
+    .property("type", makeCodecForConstString(ReserveTransactionType.Recoup))
+    .build("ReserveRecoupTransaction");
 
 export const codecForReserveTransaction = (): Codec<ReserveTransaction> =>
-    makeCodecForUnion<ReserveTransaction>()
-      .discriminateOn("type")
-      .alternative(
-        ReserveTransactionType.Withdraw,
-        codecForReserveWithdrawTransaction(),
-      )
-      .alternative(
-        ReserveTransactionType.Closing,
-        codecForReserveClosingTransaction(),
-      )
-      .alternative(
-        ReserveTransactionType.Recoup,
-        codecForReserveRecoupTransaction(),
-      )
-      .alternative(
-        ReserveTransactionType.Credit,
-        codecForReserveCreditTransaction(),
-      )
-      .build<ReserveTransaction>("ReserveTransaction");
+  makeCodecForUnion<ReserveTransaction>()
+    .discriminateOn("type")
+    .alternative(
+      ReserveTransactionType.Withdraw,
+      codecForReserveWithdrawTransaction(),
+    )
+    .alternative(
+      ReserveTransactionType.Closing,
+      codecForReserveClosingTransaction(),
+    )
+    .alternative(
+      ReserveTransactionType.Recoup,
+      codecForReserveRecoupTransaction(),
+    )
+    .alternative(
+      ReserveTransactionType.Credit,
+      codecForReserveCreditTransaction(),
+    )
+    .build<ReserveTransaction>("ReserveTransaction");
diff --git a/src/types/dbTypes.ts b/src/types/dbTypes.ts
index 434993cb..009b0531 100644
--- a/src/types/dbTypes.ts
+++ b/src/types/dbTypes.ts
@@ -43,7 +43,6 @@ import {
   ReserveRecoupTransaction,
 } from "./ReserveTransaction";
 import { Timestamp, Duration, getTimestampNow } from "../util/time";
-import { Wallet } from "../wallet";
 
 export enum ReserveRecordStatus {
   /**
@@ -1524,6 +1523,7 @@ export class WalletImportRecord {
 /**
  * The stores and indices for the wallet database.
  */
+// eslint-disable-next-line @typescript-eslint/no-namespace
 export namespace Stores {
   class ExchangesStore extends Store<ExchangeRecord> {
     constructor() {
diff --git a/src/types/history.ts b/src/types/history.ts
index 8179f626..a5f4e9d3 100644
--- a/src/types/history.ts
+++ b/src/types/history.ts
@@ -22,7 +22,6 @@
  * Imports.
  */
 import { RefreshReason } from "./walletTypes";
-import { ReserveTransaction } from "./ReserveTransaction";
 import { WithdrawalSource } from "./dbTypes";
 import { Timestamp } from "../util/time";
 
diff --git a/src/types/notifications.ts b/src/types/notifications.ts
index 05d3c273..3cd5be89 100644
--- a/src/types/notifications.ts
+++ b/src/types/notifications.ts
@@ -1,6 +1,3 @@
-import { OperationError } from "./walletTypes";
-import { WithdrawCoinSource, WithdrawalSource } from "./dbTypes";
-
 /*
  This file is part of GNU Taler
  (C) 2019 GNUnet e.V.
@@ -22,6 +19,12 @@ import { WithdrawCoinSource, WithdrawalSource } from 
"./dbTypes";
  * of the wallet.
  */
 
+/**
+ * Imports.
+ */
+import { OperationError } from "./walletTypes";
+import { WithdrawalSource } from "./dbTypes";
+
 export const enum NotificationType {
   CoinWithdrawn = "coin-withdrawn",
   ProposalAccepted = "proposal-accepted",
diff --git a/src/types/pending.ts b/src/types/pending.ts
index 5bca8c39..4ff82f55 100644
--- a/src/types/pending.ts
+++ b/src/types/pending.ts
@@ -25,7 +25,6 @@ import { OperationError, WalletBalance } from "./walletTypes";
 import { WithdrawalSource, RetryInfo, ReserveRecordStatus } from "./dbTypes";
 import { Timestamp, Duration } from "../util/time";
 import { ReserveType } from "./history";
-import { AmountString } from "./talerTypes";
 
 export const enum PendingOperationType {
   Bug = "bug",
diff --git a/src/types/talerTypes.ts b/src/types/talerTypes.ts
index 74157b18..799c84dc 100644
--- a/src/types/talerTypes.ts
+++ b/src/types/talerTypes.ts
@@ -963,22 +963,22 @@ export const codecForWithdrawOperationStatusResponse = 
(): Codec<
     .build("WithdrawOperationStatusResponse");
 
 export const codecForTipPickupGetResponse = (): Codec<TipPickupGetResponse> =>
-    makeCodecForObject<TipPickupGetResponse>()
-      .property("extra", codecForAny)
-      .property("amount", codecForString)
-      .property("amount_left", codecForString)
-      .property("exchange_url", codecForString)
-      .property("stamp_expire", codecForTimestamp)
-      .property("stamp_created", codecForTimestamp)
-      .build("TipPickupGetResponse");
+  makeCodecForObject<TipPickupGetResponse>()
+    .property("extra", codecForAny)
+    .property("amount", codecForString)
+    .property("amount_left", codecForString)
+    .property("exchange_url", codecForString)
+    .property("stamp_expire", codecForTimestamp)
+    .property("stamp_created", codecForTimestamp)
+    .build("TipPickupGetResponse");
 
 export const codecForRecoupConfirmation = (): Codec<RecoupConfirmation> =>
-    makeCodecForObject<RecoupConfirmation>()
-      .property("reserve_pub", makeCodecOptional(codecForString))
-      .property("old_coin_pub", makeCodecOptional(codecForString))
-      .build("RecoupConfirmation");
+  makeCodecForObject<RecoupConfirmation>()
+    .property("reserve_pub", makeCodecOptional(codecForString))
+    .property("old_coin_pub", makeCodecOptional(codecForString))
+    .build("RecoupConfirmation");
 
 export const codecForWithdrawResponse = (): Codec<WithdrawResponse> =>
-    makeCodecForObject<WithdrawResponse>()
-      .property("ev_sig", codecForString)
-      .build("WithdrawResponse");
+  makeCodecForObject<WithdrawResponse>()
+    .property("ev_sig", codecForString)
+    .build("WithdrawResponse");
diff --git a/src/types/walletTypes.ts b/src/types/walletTypes.ts
index 3d919c6f..113a137c 100644
--- a/src/types/walletTypes.ts
+++ b/src/types/walletTypes.ts
@@ -260,13 +260,13 @@ export interface CreateReserveRequest {
 }
 
 export const codecForCreateReserveRequest = (): Codec<CreateReserveRequest> =>
-    makeCodecForObject<CreateReserveRequest>()
-      .property("amount", codecForAmountJson())
-      .property("exchange", codecForString)
-      .property("exchangeWire", codecForString)
-      .property("senderWire", makeCodecOptional(codecForString))
-      .property("bankWithdrawStatusUrl", makeCodecOptional(codecForString))
-      .build("CreateReserveRequest");
+  makeCodecForObject<CreateReserveRequest>()
+    .property("amount", codecForAmountJson())
+    .property("exchange", codecForString)
+    .property("exchangeWire", codecForString)
+    .property("senderWire", makeCodecOptional(codecForString))
+    .property("bankWithdrawStatusUrl", makeCodecOptional(codecForString))
+    .build("CreateReserveRequest");
 
 /**
  * Request to mark a reserve as confirmed.
@@ -280,9 +280,9 @@ export interface ConfirmReserveRequest {
 }
 
 export const codecForConfirmReserveRequest = (): Codec<ConfirmReserveRequest> 
=>
-    makeCodecForObject<ConfirmReserveRequest>()
-      .property("reservePub", codecForString)
-      .build("ConfirmReserveRequest");
+  makeCodecForObject<ConfirmReserveRequest>()
+    .property("reservePub", codecForString)
+    .build("ConfirmReserveRequest");
 
 /**
  * Wire coins to the user's own bank account.
diff --git a/src/util/RequestThrottler.ts b/src/util/RequestThrottler.ts
index c99c7e94..d979fbfc 100644
--- a/src/util/RequestThrottler.ts
+++ b/src/util/RequestThrottler.ts
@@ -21,12 +21,7 @@
 /**
  * Imports.
  */
-import {
-  getTimestampNow,
-  Timestamp,
-  timestampSubtractDuraction,
-  timestampDifference,
-} from "../util/time";
+import { getTimestampNow, timestampDifference } from "../util/time";
 
 /**
  * Maximum request per second, per origin.
diff --git a/src/util/amounts.ts b/src/util/amounts.ts
index da1c1923..5953f513 100644
--- a/src/util/amounts.ts
+++ b/src/util/amounts.ts
@@ -67,11 +67,11 @@ export interface AmountJson {
 }
 
 export const codecForAmountJson = (): Codec<AmountJson> =>
-    makeCodecForObject<AmountJson>()
-      .property("currency", codecForString)
-      .property("value", codecForNumber)
-      .property("fraction", codecForNumber)
-      .build("AmountJson");
+  makeCodecForObject<AmountJson>()
+    .property("currency", codecForString)
+    .property("value", codecForNumber)
+    .property("fraction", codecForNumber)
+    .build("AmountJson");
 
 /**
  * Result of a possibly overflowing operation.
diff --git a/src/util/codec.ts b/src/util/codec.ts
index 480e1eec..136c5b05 100644
--- a/src/util/codec.ts
+++ b/src/util/codec.ts
@@ -346,4 +346,4 @@ export function makeCodecOptional<V>(
       return innerCodec.decode(x, c);
     },
   };
-}
\ No newline at end of file
+}
diff --git a/src/util/helpers.ts b/src/util/helpers.ts
index 0e19d7ab..7cd9e423 100644
--- a/src/util/helpers.ts
+++ b/src/util/helpers.ts
@@ -39,7 +39,7 @@ export function amountToPretty(amount: AmountJson): string {
  *
  * See http://api.taler.net/wallet.html#general
  */
-export function canonicalizeBaseUrl(url: string) {
+export function canonicalizeBaseUrl(url: string): string {
   if (!url.startsWith("http") && !url.startsWith("https")) {
     url = "https://"; + url;
   }
@@ -145,13 +145,3 @@ export function strcmp(s1: string, s2: string): number {
   }
   return 0;
 }
-
-/**
- * Run a function and return its result.
- *
- * Used as a nicer-looking way to do immediately invoked function
- * expressions (IFFEs).
- */
-export function runBlock<T>(f: () => T) {
-  return f();
-}
diff --git a/src/util/http.ts b/src/util/http.ts
index 3842347d..0ac989a1 100644
--- a/src/util/http.ts
+++ b/src/util/http.ts
@@ -136,9 +136,13 @@ export class BrowserHttpLib implements HttpRequestLibrary {
           const headerMap = new Headers();
           arr.forEach(function (line) {
             const parts = line.split(": ");
-            const header = parts.shift();
+            const headerName = parts.shift();
+            if (!headerName) {
+              console.error("invalid header");
+              return;
+            }
             const value = parts.join(": ");
-            headerMap.set(header!, value);
+            headerMap.set(headerName, value);
           });
           const resp: HttpResponse = {
             status: myRequest.status,
@@ -156,7 +160,11 @@ export class BrowserHttpLib implements HttpRequestLibrary {
     return this.req("get", url, undefined, opt);
   }
 
-  postJson(url: string, body: any, opt?: HttpRequestOptions): 
Promise<HttpResponse> {
+  postJson(
+    url: string,
+    body: any,
+    opt?: HttpRequestOptions,
+  ): Promise<HttpResponse> {
     return this.req("post", url, JSON.stringify(body), opt);
   }
 
diff --git a/src/util/query.ts b/src/util/query.ts
index 256395d4..8dd3ff1e 100644
--- a/src/util/query.ts
+++ b/src/util/query.ts
@@ -292,7 +292,11 @@ export class TransactionHandle {
     return requestToPromise(req);
   }
 
-  mutate<T>(store: Store<T>, key: any, f: (x: T) => T | undefined): 
Promise<void> {
+  mutate<T>(
+    store: Store<T>,
+    key: any,
+    f: (x: T) => T | undefined,
+  ): Promise<void> {
     const req = this.tx.objectStore(store.name).openCursor(key);
     return applyMutation(req, f);
   }
diff --git a/src/util/talerconfig.ts b/src/util/talerconfig.ts
index 61c75574..ec08c352 100644
--- a/src/util/talerconfig.ts
+++ b/src/util/talerconfig.ts
@@ -59,8 +59,6 @@ export class ConfigValue<T> {
 export class Configuration {
   private sectionMap: SectionMap = {};
 
-  constructor() {}
-
   loadFromString(s: string): void {
     const reComment = /^\s*#.*$/;
     const reSection = /^\s*\[\s*([^\]]*)\s*\]\s*$/;
diff --git a/src/util/taleruri-test.ts b/src/util/taleruri-test.ts
index 9efc1846..1510880c 100644
--- a/src/util/taleruri-test.ts
+++ b/src/util/taleruri-test.ts
@@ -28,7 +28,7 @@ test("taler pay url parsing: wrong scheme", (t) => {
   t.is(r1, undefined);
 
   const url2 = "taler://refund/a/b/c/d/e/f";
-  const r2 = parsePayUri(url1);
+  const r2 = parsePayUri(url2);
   t.is(r2, undefined);
 });
 
diff --git a/src/util/taleruri.ts b/src/util/taleruri.ts
index 8ad79669..46cc199f 100644
--- a/src/util/taleruri.ts
+++ b/src/util/taleruri.ts
@@ -97,7 +97,7 @@ export function classifyTalerUri(s: string): TalerUriType {
   return TalerUriType.Unknown;
 }
 
-export function getOrderDownloadUrl(merchantBaseUrl: string, orderId: string) {
+export function getOrderDownloadUrl(merchantBaseUrl: string, orderId: string): 
string {
   const u = new URL("proposal", merchantBaseUrl);
   u.searchParams.set("order_id", orderId);
   return u.href;
diff --git a/src/util/timer.ts b/src/util/timer.ts
index 18132a81..9d743b8e 100644
--- a/src/util/timer.ts
+++ b/src/util/timer.ts
@@ -26,7 +26,6 @@
  */
 import { Duration } from "./time";
 
-
 /**
  * Cancelable timer.
  */
diff --git a/src/wallet.ts b/src/wallet.ts
index 02450523..273a9f87 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -154,7 +154,10 @@ export class Wallet {
     this.ws = new InternalWalletState(db, http, cryptoWorkerFactory);
   }
 
-  getExchangePaytoUri(exchangeBaseUrl: string, supportedTargetTypes: 
string[]): Promise<string> {
+  getExchangePaytoUri(
+    exchangeBaseUrl: string,
+    supportedTargetTypes: string[],
+  ): Promise<string> {
     return getExchangePaytoUri(this.ws, exchangeBaseUrl, supportedTargetTypes);
   }
 
diff --git a/src/webex/compat.ts b/src/webex/compat.ts
index f1a68f33..1cbf34a2 100644
--- a/src/webex/compat.ts
+++ b/src/webex/compat.ts
@@ -30,6 +30,6 @@ export function isFirefox(): boolean {
 /**
  * Check if we are running under nodejs.
  */
-export function isNode() {
+export function isNode(): boolean {
   return typeof process !== "undefined" && process.release.name === "node";
 }
diff --git a/src/webex/i18n.tsx b/src/webex/i18n.tsx
index ed6fe868..3f23267d 100644
--- a/src/webex/i18n.tsx
+++ b/src/webex/i18n.tsx
@@ -57,7 +57,7 @@ function setupJed(): any {
 /**
  * Convert template strings to a msgid
  */
-function toI18nString(stringSeq: ReadonlyArray<string>) {
+function toI18nString(stringSeq: ReadonlyArray<string>): string {
   let s = "";
   for (let i = 0; i < stringSeq.length; i++) {
     s += stringSeq[i];
@@ -71,7 +71,7 @@ function toI18nString(stringSeq: ReadonlyArray<string>) {
 /**
  * Internationalize a string template with arbitrary serialized values.
  */
-export function str(stringSeq: TemplateStringsArray, ...values: any[]) {
+export function str(stringSeq: TemplateStringsArray, ...values: any[]): string 
{
   const s = toI18nString(stringSeq);
   const tr = jed
     .translate(s)
@@ -226,8 +226,8 @@ export class TranslatePlural extends React.Component<
         typeof childArray[i] === "string" &&
         typeof childArray[i + 1] === "string"
       ) {
-        childArray[i + i] =
-          ((childArray[i] as string) + childArray[i + 1]) as string;
+        childArray[i + i] = ((childArray[i] as string) +
+          childArray[i + 1]) as string;
         childArray.splice(i, 1);
       }
     }
@@ -267,8 +267,8 @@ export class TranslateSingular extends React.Component<
         typeof childArray[i] === "string" &&
         typeof childArray[i + 1] === "string"
       ) {
-        childArray[i + i] =
-          ((childArray[i] as string) + childArray[i + 1]) as string;
+        childArray[i + i] = ((childArray[i] as string) +
+          childArray[i + 1]) as string;
         childArray.splice(i, 1);
       }
     }
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index b695b4ab..19d125a8 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -23,7 +23,6 @@
 
 import { AmountJson } from "../util/amounts";
 import * as dbTypes from "../types/dbTypes";
-import * as talerTypes from "../types/talerTypes";
 import * as walletTypes from "../types/walletTypes";
 
 import { UpgradeResponse } from "./wxApi";
@@ -172,22 +171,3 @@ export interface MessageMap {
  */
 export type MessageType = keyof MessageMap;
 
-/**
- * Make a request whose details match the request type.
- */
-export function makeRequest<T extends MessageType>(
-  type: T,
-  details: MessageMap[T]["request"],
-) {
-  return { type, details };
-}
-
-/**
- * Make a response that matches the request type.
- */
-export function makeResponse<T extends MessageType>(
-  type: T,
-  response: MessageMap[T]["response"],
-) {
-  return response;
-}
diff --git a/src/webex/pageEntryPoint.ts b/src/webex/pageEntryPoint.ts
index 04e3dd8d..dd9c1303 100644
--- a/src/webex/pageEntryPoint.ts
+++ b/src/webex/pageEntryPoint.ts
@@ -25,7 +25,7 @@ import { createPopup } from "./pages/popup";
 import { createWithdrawPage } from "./pages/withdraw";
 import { createWelcomePage } from "./pages/welcome";
 
-function main() {
+function main(): void {
   try {
     let mainElement;
     const m = location.pathname.match(/([^/]+)$/);
diff --git a/src/webex/pages/add-auditor.tsx b/src/webex/pages/add-auditor.tsx
index fc7de920..c28d15ca 100644
--- a/src/webex/pages/add-auditor.tsx
+++ b/src/webex/pages/add-auditor.tsx
@@ -91,9 +91,7 @@ function ConfirmAuditor(props: ConfirmAuditorProps): 
JSX.Element {
       {addDone ? (
         <div>
           Auditor was added! You can also{" "}
-          <a href={chrome.extension.getURL("/auditors.html")}>
-            view and edit
-          </a>{" "}
+          <a href={chrome.extension.getURL("/auditors.html")}>view and 
edit</a>{" "}
           auditors.
         </div>
       ) : (
diff --git a/src/webex/pages/auditors.tsx b/src/webex/pages/auditors.tsx
index e933aeac..ac93afd3 100644
--- a/src/webex/pages/auditors.tsx
+++ b/src/webex/pages/auditors.tsx
@@ -29,7 +29,6 @@ import {
 import { getCurrencies, updateCurrency } from "../wxApi";
 
 import * as React from "react";
-import * as ReactDOM from "react-dom";
 
 interface CurrencyListState {
   currencies?: CurrencyRecord[];
@@ -49,13 +48,16 @@ class CurrencyList extends React.Component<{}, 
CurrencyListState> {
     this.state = {} as any;
   }
 
-  async update() {
+  async update(): Promise<void> {
     const currencies = await getCurrencies();
     console.log("currencies: ", currencies);
     this.setState({ currencies });
   }
 
-  async confirmRemoveAuditor(c: CurrencyRecord, a: AuditorRecord) {
+  async confirmRemoveAuditor(
+    c: CurrencyRecord,
+    a: AuditorRecord,
+  ): Promise<void> {
     if (
       window.confirm(
         `Do you really want to remove auditor ${a.baseUrl} for currency 
${c.name}?`,
@@ -66,7 +68,10 @@ class CurrencyList extends React.Component<{}, 
CurrencyListState> {
     }
   }
 
-  async confirmRemoveExchange(c: CurrencyRecord, e: ExchangeForCurrencyRecord) 
{
+  async confirmRemoveExchange(
+    c: CurrencyRecord,
+    e: ExchangeForCurrencyRecord,
+  ): Promise<void> {
     if (
       window.confirm(
         `Do you really want to remove exchange ${e.baseUrl} for currency 
${c.name}?`,
@@ -86,7 +91,7 @@ class CurrencyList extends React.Component<{}, 
CurrencyListState> {
         <p>Trusted Auditors:</p>
         <ul>
           {c.auditors.map((a) => (
-            <li>
+            <li key={a.baseUrl}>
               {a.baseUrl}{" "}
               <button
                 className="pure-button button-destructive"
@@ -114,7 +119,7 @@ class CurrencyList extends React.Component<{}, 
CurrencyListState> {
         <p>Trusted Exchanges:</p>
         <ul>
           {c.exchanges.map((e) => (
-            <li>
+            <li key={e.baseUrl}>
               {e.baseUrl}{" "}
               <button
                 className="pure-button button-destructive"
@@ -137,7 +142,7 @@ class CurrencyList extends React.Component<{}, 
CurrencyListState> {
     return (
       <div id="main">
         {currencies.map((c) => (
-          <div>
+          <div key={c.name}>
             <h1>Currency {c.name}</h1>
             <p>Displayed with {c.fractionalDigits} fractional digits.</p>
             <h2>Auditors</h2>
@@ -151,6 +156,6 @@ class CurrencyList extends React.Component<{}, 
CurrencyListState> {
   }
 }
 
-function makeAuditorsPage() {
+export function makeAuditorsPage(): JSX.Element {
   return <CurrencyList />;
 }
diff --git a/src/webex/pages/pay.tsx b/src/webex/pages/pay.tsx
index e3dd630b..61f28770 100644
--- a/src/webex/pages/pay.tsx
+++ b/src/webex/pages/pay.tsx
@@ -47,7 +47,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: 
string }): JSX.Element {
       setPayStatus(p);
     };
     doFetch();
-  }, [numTries]);
+  }, [numTries, talerPayUri]);
 
   if (!payStatus) {
     return <span>Loading payment information ...</span>;
diff --git a/src/webex/pages/payback.tsx b/src/webex/pages/payback.tsx
index 9c53aac9..5d42f5f4 100644
--- a/src/webex/pages/payback.tsx
+++ b/src/webex/pages/payback.tsx
@@ -25,6 +25,6 @@
  */
 import * as React from "react";
 
-export function makePaybackPage() {
+export function makePaybackPage(): JSX.Element {
   return <div>not implemented</div>;
 }
diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx
index f62f64b7..cdb09d44 100644
--- a/src/webex/pages/popup.tsx
+++ b/src/webex/pages/popup.tsx
@@ -31,11 +31,7 @@ import * as Amounts from "../../util/amounts";
 
 import { WalletBalance, WalletBalanceEntry } from "../../types/walletTypes";
 
-import {
-  abbrev,
-  renderAmount,
-  PageLink,
-} from "../renderHtml";
+import { abbrev, renderAmount, PageLink } from "../renderHtml";
 import * as wxApi from "../wxApi";
 
 import React, { Fragment } from "react";
@@ -671,7 +667,7 @@ class WalletHistory extends React.Component<any, any> {
     console.log("rendering history");
     const history: HistoryEvent[] = this.myHistory;
     if (this.gotError) {
-      return i18n.str`Error: could not retrieve event history`;
+      return <span>i18n.str`Error: could not retrieve event history`</span>;
     }
 
     if (!history) {
@@ -734,18 +730,10 @@ function WalletDebug(props: any): JSX.Element {
   return (
     <div>
       <p>Debug tools:</p>
-      <button onClick={openExtensionPage("/popup.html")}>
-        wallet tab
-      </button>
-      <button onClick={openExtensionPage("/benchmark.html")}>
-        benchmark
-      </button>
-      <button onClick={openExtensionPage("/show-db.html")}>
-        show db
-      </button>
-      <button onClick={openExtensionPage("/tree.html")}>
-        show tree
-      </button>
+      <button onClick={openExtensionPage("/popup.html")}>wallet tab</button>
+      <button onClick={openExtensionPage("/benchmark.html")}>benchmark</button>
+      <button onClick={openExtensionPage("/show-db.html")}>show db</button>
+      <button onClick={openExtensionPage("/tree.html")}>show tree</button>
       <br />
       <button onClick={confirmReset}>reset</button>
       <button onClick={reload}>reload chrome extension</button>
@@ -788,4 +776,4 @@ function WalletPopup(): JSX.Element {
 export function createPopup(): JSX.Element {
   chrome.runtime.connect({ name: "popup" });
   return <WalletPopup />;
-}
\ No newline at end of file
+}
diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx
index 4a13317c..621a286b 100644
--- a/src/webex/pages/refund.tsx
+++ b/src/webex/pages/refund.tsx
@@ -47,7 +47,7 @@ function RefundStatusView(props: { talerRefundUri: string }): 
JSX.Element {
       }
     };
     doFetch();
-  }, []);
+  }, [props.talerRefundUri]);
 
   console.log("rendering");
 
@@ -63,7 +63,7 @@ function RefundStatusView(props: { talerRefundUri: string }): 
JSX.Element {
     <>
       <h2>Refund Status</h2>
       <p>
-        The product <em>{purchaseDetails.contractTerms.summary!}</em> has
+        The product <em>{purchaseDetails.contractTerms.summary}</em> has
         received a total refund of{" "}
         <AmountView amount={purchaseDetails.totalRefundAmount} />.
       </p>
@@ -77,7 +77,7 @@ export function createRefundPage(): JSX.Element {
 
   const container = document.getElementById("container");
   if (!container) {
-    throw Error("fatal: can't mount component, container missing")
+    throw Error("fatal: can't mount component, container missing");
   }
 
   const talerRefundUri = url.searchParams.get("talerRefundUri");
diff --git a/src/webex/pages/reset-required.tsx 
b/src/webex/pages/reset-required.tsx
index e58243b3..9e40e798 100644
--- a/src/webex/pages/reset-required.tsx
+++ b/src/webex/pages/reset-required.tsx
@@ -42,17 +42,17 @@ class ResetNotification extends React.Component<any, State> 
{
     this.state = { checked: false, resetRequired: true };
     setInterval(() => this.update(), 500);
   }
-  async update() {
+  async update(): Promise<void> {
     const res = await wxApi.checkUpgrade();
     this.setState({ resetRequired: res.dbResetRequired });
   }
-  render() {
+  render(): JSX.Element {
     if (this.state.resetRequired) {
       return (
         <div>
           <h1>Manual Reset Reqired</h1>
           <p>
-            The wallet's database in your browser is incompatible with the{" "}
+            The wallet&apos;s database in your browser is incompatible with 
the{" "}
             currently installed wallet. Please reset manually.
           </p>
           <p>
@@ -88,6 +88,6 @@ class ResetNotification extends React.Component<any, State> {
   }
 }
 
-export function createResetRequiredPage() {
+export function createResetRequiredPage(): JSX.Element {
   return <ResetNotification />;
 }
diff --git a/src/webex/pages/return-coins.tsx b/src/webex/pages/return-coins.tsx
index 7d759705..ccdb6db5 100644
--- a/src/webex/pages/return-coins.tsx
+++ b/src/webex/pages/return-coins.tsx
@@ -290,8 +290,9 @@ class ReturnCoins extends React.Component<{}, 
ReturnCoinsState> {
       <div id="main">
         <h1>Wire electronic cash back to own bank account</h1>
         <p>
-          You can send coins back into your own bank account. Note that you're
-          acting as a merchant when doing this, and thus the same fees apply.
+          You can send coins back into your own bank account. Note that
+          you&apos;re acting as a merchant when doing this, and thus the same
+          fees apply.
         </p>
         {this.state.lastConfirmedDetail ? (
           <p className="okaybox">
diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx
index 9c797f50..4a1d3743 100644
--- a/src/webex/pages/tip.tsx
+++ b/src/webex/pages/tip.tsx
@@ -25,10 +25,7 @@ import * as React from "react";
 
 import { acceptTip, getTipStatus } from "../wxApi";
 
-import {
-  renderAmount,
-  ProgressButton,
-} from "../renderHtml";
+import { renderAmount, ProgressButton } from "../renderHtml";
 
 import { useState, useEffect } from "react";
 import { TipStatus } from "../../types/walletTypes";
@@ -45,7 +42,7 @@ function TipDisplay(props: { talerTipUri: string }): 
JSX.Element {
       setTipStatus(ts);
     };
     doFetch();
-  }, []);
+  }, [props.talerTipUri]);
 
   if (discarded) {
     return <span>You&apos;ve discarded the tip.</span>;
@@ -96,11 +93,11 @@ function TipDisplay(props: { talerTipUri: string }): 
JSX.Element {
 }
 
 export function createTipPage(): JSX.Element {
-    const url = new URL(document.location.href);
-    const talerTipUri = url.searchParams.get("talerTipUri");
-    if (typeof talerTipUri !== "string") {
-      throw Error("talerTipUri must be a string");
-    }
+  const url = new URL(document.location.href);
+  const talerTipUri = url.searchParams.get("talerTipUri");
+  if (typeof talerTipUri !== "string") {
+    throw Error("talerTipUri must be a string");
+  }
 
-    return <TipDisplay talerTipUri={talerTipUri} />;
+  return <TipDisplay talerTipUri={talerTipUri} />;
 }
diff --git a/src/webex/pages/welcome.tsx b/src/webex/pages/welcome.tsx
index a99cadb0..eecbe2be 100644
--- a/src/webex/pages/welcome.tsx
+++ b/src/webex/pages/welcome.tsx
@@ -69,7 +69,7 @@ function Diagnostics(): JSX.Element {
           <p>Problems detected:</p>
           <ol>
             {diagnostics.errors.map((errMsg) => (
-              <li>{errMsg}</li>
+              <li key={errMsg}>{errMsg}</li>
             ))}
           </ol>
           {diagnostics.firefoxIdbProblem ? (
@@ -112,4 +112,4 @@ function Welcome(): JSX.Element {
 
 export function createWelcomePage(): JSX.Element {
   return <Welcome />;
-}
\ No newline at end of file
+}
diff --git a/src/webex/pages/withdraw.tsx b/src/webex/pages/withdraw.tsx
index 9020ddb0..efd0adc8 100644
--- a/src/webex/pages/withdraw.tsx
+++ b/src/webex/pages/withdraw.tsx
@@ -30,7 +30,9 @@ import { WithdrawDetailView, renderAmount } from 
"../renderHtml";
 import React, { useState, useEffect } from "react";
 import { getWithdrawDetails, acceptWithdrawal } from "../wxApi";
 
-function NewExchangeSelection(props: { talerWithdrawUri: string }): 
JSX.Element {
+function NewExchangeSelection(props: {
+  talerWithdrawUri: string;
+}): JSX.Element {
   const [details, setDetails] = useState<WithdrawDetails | undefined>();
   const [selectedExchange, setSelectedExchange] = useState<
     string | undefined
@@ -63,7 +65,7 @@ function NewExchangeSelection(props: { talerWithdrawUri: 
string }): JSX.Element
       setDetails(d);
     };
     fetchData();
-  }, [selectedExchange, errMsg, selecting]);
+  }, [selectedExchange, errMsg, selecting, talerWithdrawUri]);
 
   if (errMsg) {
     return (
@@ -145,8 +147,11 @@ function NewExchangeSelection(props: { talerWithdrawUri: 
string }): JSX.Element
   }
 
   const accept = async (): Promise<void> => {
+    if (!selectedExchange) {
+      throw Error("can't accept, no exchange selected");
+    }
     console.log("accepting exchange", selectedExchange);
-    const res = await acceptWithdrawal(talerWithdrawUri, selectedExchange!);
+    const res = await acceptWithdrawal(talerWithdrawUri, selectedExchange);
     console.log("accept withdrawal response", res);
     if (res.confirmTransferUrl) {
       document.location.href = res.confirmTransferUrl;
@@ -198,9 +203,9 @@ function NewExchangeSelection(props: { talerWithdrawUri: 
string }): JSX.Element
 
 export function createWithdrawPage(): JSX.Element {
   const url = new URL(document.location.href);
-    const talerWithdrawUri = url.searchParams.get("talerWithdrawUri");
-    if (!talerWithdrawUri) {
-      throw Error("withdraw URI required");
-    }
-    return <NewExchangeSelection talerWithdrawUri={talerWithdrawUri} />;
-}
\ No newline at end of file
+  const talerWithdrawUri = url.searchParams.get("talerWithdrawUri");
+  if (!talerWithdrawUri) {
+    throw Error("withdraw URI required");
+  }
+  return <NewExchangeSelection talerWithdrawUri={talerWithdrawUri} />;
+}
diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx
index f5a6a7e4..b1363abf 100644
--- a/src/webex/renderHtml.tsx
+++ b/src/webex/renderHtml.tsx
@@ -127,6 +127,37 @@ export class Collapsible extends React.Component<
   }
 }
 
+function WireFee(props: {
+  s: string;
+  rci: ExchangeWithdrawDetails;
+}): JSX.Element {
+  return (
+    <>
+      <thead>
+        <tr>
+          <th colSpan={3}>Wire Method {props.s}</th>
+        </tr>
+        <tr>
+          <th>Applies Until</th>
+          <th>Wire Fee</th>
+          <th>Closing Fee</th>
+        </tr>
+      </thead>
+      ,
+      <tbody>
+        {props.rci.wireFees.feesForType[props.s].map((f) => (
+          <tr key={f.sig}>
+            <td>{stringifyTimestamp(f.endStamp)}</td>
+            <td>{renderAmount(f.wireFee)}</td>
+            <td>{renderAmount(f.closingFee)}</td>
+          </tr>
+        ))}
+      </tbody>
+      ,
+    </>
+  );
+}
+
 function AuditorDetailsView(props: {
   rci: ExchangeWithdrawDetails | null;
 }): JSX.Element {
@@ -145,7 +176,7 @@ function AuditorDetailsView(props: {
   return (
     <div>
       {(rci.exchangeInfo.details?.auditors ?? []).map((a) => (
-        <div>
+        <div key={a.auditor_pub}>
           <h3>Auditor {a.auditor_url}</h3>
           <p>
             Public key: <ExpanderText text={a.auditor_pub} />
@@ -202,30 +233,6 @@ function FeeDetailsView(props: {
     );
   }
 
-  function wireFee(s: string) {
-    return [
-      <thead>
-        <tr>
-          <th colSpan={3}>Wire Method {s}</th>
-        </tr>
-        <tr>
-          <th>Applies Until</th>
-          <th>Wire Fee</th>
-          <th>Closing Fee</th>
-        </tr>
-      </thead>,
-      <tbody>
-        {rci!.wireFees.feesForType[s].map((f) => (
-          <tr>
-            <td>{stringifyTimestamp(f.endStamp)}</td>
-            <td>{renderAmount(f.wireFee)}</td>
-            <td>{renderAmount(f.closingFee)}</td>
-          </tr>
-        ))}
-      </tbody>,
-    ];
-  }
-
   const withdrawFee = renderAmount(rci.withdrawFee);
   const overhead = renderAmount(rci.overhead);
 
@@ -265,7 +272,9 @@ function FeeDetailsView(props: {
       <h3>Wire Fees</h3>
       <div style={{ overflow: "auto" }}>
         <table className="pure-table">
-          {Object.keys(rci.wireFees.feesForType).map(wireFee)}
+          {Object.keys(rci.wireFees.feesForType).map((s) => (
+            <WireFee key={s} s={s} rci={rci} />
+          ))}
         </table>
       </div>
     </div>
@@ -337,7 +346,12 @@ export function PageLink(
 ): JSX.Element {
   const url = chrome.extension.getURL(`/${props.pageName}`);
   return (
-    <a className="actionLink" href={url} target="_blank" rel="noopener 
noreferrer">
+    <a
+      className="actionLink"
+      href={url}
+      target="_blank"
+      rel="noopener noreferrer"
+    >
       {props.children}
     </a>
   );
diff --git a/yarn.lock b/yarn.lock
index 88e58bb2..a877779b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -175,6 +175,13 @@
     core-js-pure "^3.0.0"
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.4.5":
+  version "7.9.2"
+  resolved 
"https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06";
+  integrity 
sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
   version "7.8.6"
   resolved 
"https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b";
@@ -411,6 +418,26 @@
     eslint-scope "^5.0.0"
     eslint-utils "^2.0.0"
 
+"@typescript-eslint/experimental-utils@2.27.0":
+  version "2.27.0"
+  resolved 
"https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.27.0.tgz#801a952c10b58e486c9a0b36cf21e2aab1e9e01a";
+  integrity 
sha512-vOsYzjwJlY6E0NJRXPTeCGqjv5OHgRU1kzxHKWJVPjDYGbPgLudBXjIlc+OD1hDBZ4l1DLbOc5VjofKahsu9Jw==
+  dependencies:
+    "@types/json-schema" "^7.0.3"
+    "@typescript-eslint/typescript-estree" "2.27.0"
+    eslint-scope "^5.0.0"
+    eslint-utils "^2.0.0"
+
+"@typescript-eslint/parser@^2.24.0":
+  version "2.27.0"
+  resolved 
"https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.27.0.tgz#d91664335b2c46584294e42eb4ff35838c427287";
+  integrity 
sha512-HFUXZY+EdwrJXZo31DW4IS1ujQW3krzlRjBrFRrJcMDh0zCu107/nRfhk/uBasO8m0NVDbBF5WZKcIUMRO7vPg==
+  dependencies:
+    "@types/eslint-visitor-keys" "^1.0.0"
+    "@typescript-eslint/experimental-utils" "2.27.0"
+    "@typescript-eslint/typescript-estree" "2.27.0"
+    eslint-visitor-keys "^1.1.0"
+
 "@typescript-eslint/parser@^2.26.0":
   version "2.26.0"
   resolved 
"https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.26.0.tgz#385463615818b33acb72a25b39c03579df93d76f";
@@ -434,6 +461,19 @@
     semver "^6.3.0"
     tsutils "^3.17.1"
 
+"@typescript-eslint/typescript-estree@2.27.0":
+  version "2.27.0"
+  resolved 
"https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.27.0.tgz#a288e54605412da8b81f1660b56c8b2e42966ce8";
+  integrity 
sha512-t2miCCJIb/FU8yArjAvxllxbTiyNqaXJag7UOpB5DVoM3+xnjeOngtqlJkLRnMtzaRcJhe3CIR9RmL40omubhg==
+  dependencies:
+    debug "^4.1.1"
+    eslint-visitor-keys "^1.1.0"
+    glob "^7.1.6"
+    is-glob "^4.0.1"
+    lodash "^4.17.15"
+    semver "^6.3.0"
+    tsutils "^3.17.1"
+
 acorn-jsx@^5.2.0:
   version "5.2.0"
   resolved 
"https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe";
@@ -546,6 +586,14 @@ argparse@^1.0.7:
   dependencies:
     sprintf-js "~1.0.2"
 
+aria-query@^3.0.0:
+  version "3.0.0"
+  resolved 
"https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc";
+  integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=
+  dependencies:
+    ast-types-flow "0.0.7"
+    commander "^2.11.0"
+
 array-find-index@^1.0.1:
   version "1.0.2"
   resolved 
"https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1";
@@ -588,6 +636,11 @@ arrify@^2.0.1:
   resolved 
"https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa";
   integrity 
sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
 
+ast-types-flow@0.0.7, ast-types-flow@^0.0.7:
+  version "0.0.7"
+  resolved 
"https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad";
+  integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
+
 astral-regex@^1.0.0:
   version "1.0.0"
   resolved 
"https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9";
@@ -663,6 +716,11 @@ axios@^0.19.2:
   dependencies:
     follow-redirects "1.5.10"
 
+axobject-query@^2.0.2:
+  version "2.1.2"
+  resolved 
"https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799";
+  integrity 
sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ==
+
 balanced-match@^1.0.0:
   version "1.0.0"
   resolved 
"https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767";
@@ -916,7 +974,7 @@ color-name@~1.1.4:
   resolved 
"https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2";
   integrity 
sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
-commander@^2.20.0, commander@~2.20.3:
+commander@^2.11.0, commander@^2.20.0, commander@~2.20.3:
   version "2.20.3"
   resolved 
"https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33";
   integrity 
sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -965,6 +1023,11 @@ configstore@^5.0.1:
     write-file-atomic "^3.0.0"
     xdg-basedir "^4.0.0"
 
+confusing-browser-globals@^1.0.9:
+  version "1.0.9"
+  resolved 
"https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd";
+  integrity 
sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==
+
 contains-path@^0.1.0:
   version "0.1.0"
   resolved 
"https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a";
@@ -1024,6 +1087,11 @@ currently-unhandled@^0.4.1:
   dependencies:
     array-find-index "^1.0.1"
 
+damerau-levenshtein@^1.0.4:
+  version "1.0.6"
+  resolved 
"https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791";
+  integrity 
sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==
+
 date-time@^2.1.0:
   version "2.1.0"
   resolved 
"https://registry.yarnpkg.com/date-time/-/date-time-2.1.0.tgz#0286d1b4c769633b3ca13e1e62558d2dbdc2eba2";
@@ -1160,7 +1228,7 @@ emittery@^0.6.0:
   resolved 
"https://registry.yarnpkg.com/emittery/-/emittery-0.6.0.tgz#e85312468d77c3ed9a6adf43bb57d34849e0c95a";
   integrity 
sha512-6EMRGr9KzYWp8DzHFZsKVZBsMO6QhAeHMeHND8rhyBNCHKMLpgW9tZv40bwN3rAIKRS5CxcK8oLRKUJSB9h7yQ==
 
-emoji-regex@^7.0.1:
+emoji-regex@^7.0.1, emoji-regex@^7.0.2:
   version "7.0.3"
   resolved 
"https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156";
   integrity 
sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
@@ -1242,6 +1310,33 @@ escape-string-regexp@^2.0.0:
   resolved 
"https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344";
   integrity 
sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
 
+eslint-config-airbnb-base@^14.1.0:
+  version "14.1.0"
+  resolved 
"https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz#2ba4592dd6843258221d9bff2b6831bd77c874e4";
+  integrity 
sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw==
+  dependencies:
+    confusing-browser-globals "^1.0.9"
+    object.assign "^4.1.0"
+    object.entries "^1.1.1"
+
+eslint-config-airbnb-typescript@^7.2.0:
+  version "7.2.0"
+  resolved 
"https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-7.2.0.tgz#afa7cea5657c9f70c0b96d52bf7d470e9827b66a";
+  integrity 
sha512-W7IZUIJpBZIzU3p65KoyJPl2vGSy6FS3R+K91Cp3NLK/0m1oyvCFeBHI2QlWdqxkJ4FvwnLvsoRTutwpKNIT+A==
+  dependencies:
+    "@typescript-eslint/parser" "^2.24.0"
+    eslint-config-airbnb "^18.1.0"
+    eslint-config-airbnb-base "^14.1.0"
+
+eslint-config-airbnb@^18.1.0:
+  version "18.1.0"
+  resolved 
"https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.1.0.tgz#724d7e93dadd2169492ff5363c5aaa779e01257d";
+  integrity 
sha512-kZFuQC/MPnH7KJp6v95xsLBf63G/w7YqdPfQ0MUanxQ7zcKUNG8j+sSY860g3NwCBOa62apw16J6pRN+AOgXzw==
+  dependencies:
+    eslint-config-airbnb-base "^14.1.0"
+    object.assign "^4.1.0"
+    object.entries "^1.1.1"
+
 eslint-import-resolver-node@^0.3.2:
   version "0.3.3"
   resolved 
"https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404";
@@ -1276,6 +1371,21 @@ eslint-plugin-import@^2.20.2:
     read-pkg-up "^2.0.0"
     resolve "^1.12.0"
 
+eslint-plugin-jsx-a11y@^6.2.3:
+  version "6.2.3"
+  resolved 
"https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa";
+  integrity 
sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==
+  dependencies:
+    "@babel/runtime" "^7.4.5"
+    aria-query "^3.0.0"
+    array-includes "^3.0.3"
+    ast-types-flow "^0.0.7"
+    axobject-query "^2.0.2"
+    damerau-levenshtein "^1.0.4"
+    emoji-regex "^7.0.2"
+    has "^1.0.3"
+    jsx-ast-utils "^2.2.1"
+
 eslint-plugin-react-hooks@^3.0.0:
   version "3.0.0"
   resolved 
"https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz#9e80c71846eb68dd29c3b21d832728aa66e5bd35";
@@ -2217,7 +2327,7 @@ jsonfile@^4.0.0:
   optionalDependencies:
     graceful-fs "^4.1.6"
 
-jsx-ast-utils@^2.2.3:
+jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3:
   version "2.2.3"
   resolved 
"https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz#8a9364e402448a3ce7f14d357738310d9248054f";
   integrity 
sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==

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



reply via email to

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