gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: wallet-core: rename Operation


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: rename OperationAttempt->TaskRun, do not allow task result values anymore
Date: Fri, 30 Jun 2023 16:15:01 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new d4ee96138 wallet-core: rename OperationAttempt->TaskRun, do not allow 
task result values anymore
d4ee96138 is described below

commit d4ee96138774e8bc469f172bbb6276af89d6f240
Author: Florian Dold <florian@dold.me>
AuthorDate: Fri Jun 30 16:14:58 2023 +0200

    wallet-core: rename OperationAttempt->TaskRun, do not allow task result 
values anymore
---
 packages/taler-util/src/wallet-types.ts            |  1 +
 .../src/operations/backup/index.ts                 | 44 ++++-------
 .../taler-wallet-core/src/operations/common.ts     | 86 +++++++++-------------
 .../taler-wallet-core/src/operations/deposits.ts   | 38 +++++-----
 .../taler-wallet-core/src/operations/exchanges.ts  | 60 ++++++++-------
 .../src/operations/pay-merchant.ts                 | 79 ++++++++------------
 .../src/operations/pay-peer-pull-credit.ts         | 56 ++++++--------
 .../src/operations/pay-peer-pull-debit.ts          | 30 +++-----
 .../src/operations/pay-peer-push-credit.ts         | 38 ++++------
 .../src/operations/pay-peer-push-debit.ts          | 39 +++++-----
 .../taler-wallet-core/src/operations/recoup.ts     | 14 ++--
 .../taler-wallet-core/src/operations/refresh.ts    | 32 +++-----
 .../taler-wallet-core/src/operations/testing.ts    | 14 ++++
 packages/taler-wallet-core/src/operations/tip.ts   | 25 ++-----
 .../taler-wallet-core/src/operations/withdraw.ts   | 51 +++++--------
 packages/taler-wallet-core/src/wallet.ts           |  4 +-
 packages/taler-wallet-embedded/src/wallet-qjs.ts   | 37 ++++++----
 17 files changed, 284 insertions(+), 364 deletions(-)

diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index 237c1c39d..66b5e7262 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -1754,6 +1754,7 @@ export interface CoreApiRequestEnvelope {
   operation: string;
   args: unknown;
 }
+
 export type CoreApiResponse = CoreApiResponseSuccess | CoreApiResponseError;
 
 export type CoreApiMessageEnvelope = CoreApiResponse | CoreApiNotification;
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts 
b/packages/taler-wallet-core/src/operations/backup/index.ts
index 364e876ec..236ef1e0f 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -94,8 +94,8 @@ import {
 } from "../../util/invariants.js";
 import { addAttentionRequest, removeAttentionRequest } from "../attention.js";
 import {
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
   TaskIdentifiers,
 } from "../common.js";
 import { checkPaymentByProposalId, preparePayForUri } from 
"../pay-merchant.js";
@@ -250,7 +250,7 @@ function getNextBackupTimestamp(): TalerPreciseTimestamp {
 async function runBackupCycleForProvider(
   ws: InternalWalletState,
   args: BackupForProviderArgs,
-): Promise<OperationAttemptResult<unknown, { talerUri?: string }>> {
+): Promise<TaskRunResult> {
   const provider = await ws.db
     .mktx((x) => [x.backupProviders])
     .runReadOnly(async (tx) => {
@@ -259,10 +259,7 @@ async function runBackupCycleForProvider(
 
   if (!provider) {
     logger.warn("provider disappeared");
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished();
   }
 
   const backupJson = await exportBackup(ws);
@@ -333,10 +330,7 @@ async function runBackupCycleForProvider(
       type: AttentionType.BackupUnpaid,
     });
 
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished();
   }
 
   if (resp.status === HttpStatusCode.PaymentRequired) {
@@ -378,10 +372,7 @@ async function runBackupCycleForProvider(
         });
 
       return {
-        type: OperationAttemptResultType.Pending,
-        result: {
-          talerUri,
-        },
+        type: TaskRunResultType.Pending,
       };
     }
     const result = res;
@@ -415,10 +406,7 @@ async function runBackupCycleForProvider(
     );
 
     return {
-      type: OperationAttemptResultType.Pending,
-      result: {
-        talerUri,
-      },
+      type: TaskRunResultType.Pending,
     };
   }
 
@@ -445,8 +433,7 @@ async function runBackupCycleForProvider(
     });
 
     return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
+      type: TaskRunResultType.Finished,
     };
   }
 
@@ -487,7 +474,7 @@ async function runBackupCycleForProvider(
   const err = await readTalerErrorResponse(resp);
   logger.error(`got error response from backup provider: ${j2s(err)}`);
   return {
-    type: OperationAttemptResultType.Error,
+    type: TaskRunResultType.Error,
     errorDetail: err,
   };
 }
@@ -495,7 +482,7 @@ async function runBackupCycleForProvider(
 export async function processBackupForProvider(
   ws: InternalWalletState,
   backupProviderBaseUrl: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const provider = await ws.db
     .mktx((x) => [x.backupProviders])
     .runReadOnly(async (tx) => {
@@ -720,23 +707,24 @@ async function runFirstBackupCycleForProvider(
 ): Promise<AddBackupProviderResponse> {
   const resp = await runBackupCycleForProvider(ws, args);
   switch (resp.type) {
-    case OperationAttemptResultType.Error:
+    case TaskRunResultType.Error:
       throw TalerError.fromDetail(
         TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
         resp.errorDetail as any, //FIXME create an error for backup problems
       );
-    case OperationAttemptResultType.Finished:
+    case TaskRunResultType.Finished:
       return {
         status: "ok",
       };
-    case OperationAttemptResultType.Longpoll:
+    case TaskRunResultType.Longpoll:
       throw Error(
         "unexpected runFirstBackupCycleForProvider result (longpoll)",
       );
-    case OperationAttemptResultType.Pending:
+    case TaskRunResultType.Pending:
       return {
         status: "payment-required",
-        talerUri: resp.result.talerUri,
+        talerUri: "FIXME",
+        //talerUri: resp.result.talerUri,
       };
     default:
       assertUnreachable(resp);
diff --git a/packages/taler-wallet-core/src/operations/common.ts 
b/packages/taler-wallet-core/src/operations/common.ts
index 620054cae..cc16a4704 100644
--- a/packages/taler-wallet-core/src/operations/common.ts
+++ b/packages/taler-wallet-core/src/operations/common.ts
@@ -433,25 +433,25 @@ async function storePendingTaskFinished(
     });
 }
 
-export async function runTaskWithErrorReporting<T1, T2>(
+export async function runTaskWithErrorReporting(
   ws: InternalWalletState,
   opId: TaskId,
-  f: () => Promise<OperationAttemptResult<T1, T2>>,
-): Promise<OperationAttemptResult<T1, T2>> {
+  f: () => Promise<TaskRunResult>,
+): Promise<TaskRunResult> {
   let maybeError: TalerErrorDetail | undefined;
   try {
     const resp = await f();
     switch (resp.type) {
-      case OperationAttemptResultType.Error:
+      case TaskRunResultType.Error:
         await storePendingTaskError(ws, opId, resp.errorDetail);
         return resp;
-      case OperationAttemptResultType.Finished:
+      case TaskRunResultType.Finished:
         await storePendingTaskFinished(ws, opId);
         return resp;
-      case OperationAttemptResultType.Pending:
+      case TaskRunResultType.Pending:
         await storePendingTaskPending(ws, opId);
         return resp;
-      case OperationAttemptResultType.Longpoll:
+      case TaskRunResultType.Longpoll:
         return resp;
     }
   } catch (e) {
@@ -459,7 +459,7 @@ export async function runTaskWithErrorReporting<T1, T2>(
       if (ws.stopped) {
         logger.warn("crypto API stopped during shutdown, ignoring error");
         return {
-          type: OperationAttemptResultType.Error,
+          type: TaskRunResultType.Error,
           errorDetail: makeErrorDetail(
             TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
             {},
@@ -474,7 +474,7 @@ export async function runTaskWithErrorReporting<T1, T2>(
       maybeError = e.errorDetail;
       await storePendingTaskError(ws, opId, maybeError!);
       return {
-        type: OperationAttemptResultType.Error,
+        type: TaskRunResultType.Error,
         errorDetail: e.errorDetail,
       };
     } else if (e instanceof Error) {
@@ -492,7 +492,7 @@ export async function runTaskWithErrorReporting<T1, T2>(
       );
       await storePendingTaskError(ws, opId, maybeError);
       return {
-        type: OperationAttemptResultType.Error,
+        type: TaskRunResultType.Error,
         errorDetail: maybeError,
       };
     } else {
@@ -504,7 +504,7 @@ export async function runTaskWithErrorReporting<T1, T2>(
       );
       await storePendingTaskError(ws, opId, maybeError);
       return {
-        type: OperationAttemptResultType.Error,
+        type: TaskRunResultType.Error,
         errorDetail: maybeError,
       };
     }
@@ -654,59 +654,55 @@ export interface TransactionManager {
   abort(): Promise<void>;
   suspend(): Promise<void>;
   resume(): Promise<void>;
-  process(): Promise<OperationAttemptResult>;
+  process(): Promise<TaskRunResult>;
 }
 
-export enum OperationAttemptResultType {
+export enum TaskRunResultType {
   Finished = "finished",
   Pending = "pending",
   Error = "error",
   Longpoll = "longpoll",
 }
 
-export type OperationAttemptResult<TSuccess = unknown, TPending = unknown> =
-  | OperationAttemptFinishedResult<TSuccess>
-  | OperationAttemptErrorResult
-  | OperationAttemptLongpollResult
-  | OperationAttemptPendingResult<TPending>;
+export type TaskRunResult =
+  | TaskRunFinishedResult
+  | TaskRunErrorResult
+  | TaskRunLongpollResult
+  | TaskRunPendingResult;
 
-export namespace OperationAttemptResult {
-  export function finishedEmpty(): OperationAttemptResult<unknown, unknown> {
+export namespace TaskRunResult {
+  export function finished(): TaskRunResult {
     return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
+      type: TaskRunResultType.Finished,
     };
   }
-  export function pendingEmpty(): OperationAttemptResult<unknown, unknown> {
+  export function pending(): TaskRunResult {
     return {
-      type: OperationAttemptResultType.Pending,
-      result: undefined,
+      type: TaskRunResultType.Pending,
     };
   }
-  export function longpoll(): OperationAttemptResult<unknown, unknown> {
+  export function longpoll(): TaskRunResult {
     return {
-      type: OperationAttemptResultType.Longpoll,
+      type: TaskRunResultType.Longpoll,
     };
   }
 }
 
-export interface OperationAttemptFinishedResult<T> {
-  type: OperationAttemptResultType.Finished;
-  result: T;
+export interface TaskRunFinishedResult {
+  type: TaskRunResultType.Finished;
 }
 
-export interface OperationAttemptPendingResult<T> {
-  type: OperationAttemptResultType.Pending;
-  result: T;
+export interface TaskRunPendingResult {
+  type: TaskRunResultType.Pending;
 }
 
-export interface OperationAttemptErrorResult {
-  type: OperationAttemptResultType.Error;
+export interface TaskRunErrorResult {
+  type: TaskRunResultType.Error;
   errorDetail: TalerErrorDetail;
 }
 
-export interface OperationAttemptLongpollResult {
-  type: OperationAttemptResultType.Longpoll;
+export interface TaskRunLongpollResult {
+  type: TaskRunResultType.Longpoll;
 }
 
 export interface RetryInfo {
@@ -942,19 +938,3 @@ export namespace TaskIdentifiers {
     return 
`${PendingTaskType.PeerPushCredit}:${ppi.peerPushPaymentIncomingId}` as TaskId;
   }
 }
-
-/**
- * Run an operation handler, expect a success result and extract the success 
value.
- */
-export async function unwrapOperationHandlerResultOrThrow<T>(
-  res: OperationAttemptResult<T>,
-): Promise<T> {
-  switch (res.type) {
-    case OperationAttemptResultType.Finished:
-      return res.result;
-    case OperationAttemptResultType.Error:
-      throw TalerError.fromUncheckedDetail(res.errorDetail);
-    default:
-      throw Error(`unexpected operation result (${res.type})`);
-  }
-}
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index 236fa6b59..a8ec859cf 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -81,7 +81,7 @@ import { InternalWalletState } from 
"../internal-wallet-state.js";
 import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
 import {
   constructTaskIdentifier,
-  OperationAttemptResult,
+  TaskRunResult,
   runLongpollAsync,
   spendCoins,
   TombstoneTag,
@@ -462,7 +462,7 @@ async function checkDepositKycStatus(
 async function waitForRefreshOnDepositGroup(
   ws: InternalWalletState,
   depositGroup: DepositGroupRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const abortRefreshGroupId = depositGroup.abortRefreshGroupId;
   checkLogicInvariant(!!abortRefreshGroupId);
   const transactionId = constructTransactionIdentifier({
@@ -503,13 +503,13 @@ async function waitForRefreshOnDepositGroup(
     });
 
   notifyTransition(ws, transactionId, transitionInfo);
-  return OperationAttemptResult.pendingEmpty();
+  return TaskRunResult.pending();
 }
 
 async function refundDepositGroup(
   ws: InternalWalletState,
   depositGroup: DepositGroupRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const newTxPerCoin = [...depositGroup.transactionPerCoin];
   logger.info(`status per coin: ${j2s(depositGroup.transactionPerCoin)}`);
   for (let i = 0; i < depositGroup.transactionPerCoin.length; i++) {
@@ -614,13 +614,13 @@ async function refundDepositGroup(
       await tx.depositGroups.put(newDg);
     });
 
-  return OperationAttemptResult.pendingEmpty();
+  return TaskRunResult.pending();
 }
 
 async function processDepositGroupAborting(
   ws: InternalWalletState,
   depositGroup: DepositGroupRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   logger.info("processing deposit tx in 'aborting'");
   const abortRefreshGroupId = depositGroup.abortRefreshGroupId;
   if (!abortRefreshGroupId) {
@@ -634,7 +634,7 @@ async function processDepositGroupAborting(
 async function processDepositGroupPendingKyc(
   ws: InternalWalletState,
   depositGroup: DepositGroupRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const { depositGroupId } = depositGroup;
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.Deposit,
@@ -696,7 +696,7 @@ async function processDepositGroupPendingKyc(
       );
     }
   });
-  return OperationAttemptResult.longpoll();
+  return TaskRunResult.longpoll();
 }
 
 /**
@@ -709,7 +709,7 @@ async function transitionToKycRequired(
   depositGroup: DepositGroupRecord,
   kycInfo: KycPendingInfo,
   exchangeUrl: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const { depositGroupId } = depositGroup;
   const userType = "individual";
 
@@ -728,7 +728,7 @@ async function transitionToKycRequired(
   });
   if (kycStatusReq.status === HttpStatusCode.Ok) {
     logger.warn("kyc requested, but already fulfilled");
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   } else if (kycStatusReq.status === HttpStatusCode.Accepted) {
     const kycStatus = await kycStatusReq.json();
     logger.info(`kyc status: ${j2s(kycStatus)}`);
@@ -754,7 +754,7 @@ async function transitionToKycRequired(
         return { oldTxState, newTxState };
       });
     notifyTransition(ws, transactionId, transitionInfo);
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   } else {
     throw Error(`unexpected response from kyc-check (${kycStatusReq.status})`);
   }
@@ -764,7 +764,7 @@ async function processDepositGroupPendingTrack(
   ws: InternalWalletState,
   depositGroup: DepositGroupRecord,
   cancellationToken?: CancellationToken,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const { depositGroupId } = depositGroup;
   for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) {
     const coinPub = depositGroup.payCoinSelection.coinPubs[i];
@@ -905,10 +905,10 @@ async function processDepositGroupPendingTrack(
   });
   notifyTransition(ws, transactionId, transitionInfo);
   if (allWired) {
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   } else {
     // FIXME: Use long-polling.
-    return OperationAttemptResult.pendingEmpty();
+    return TaskRunResult.pending();
   }
 }
 
@@ -916,7 +916,7 @@ async function processDepositGroupPendingDeposit(
   ws: InternalWalletState,
   depositGroup: DepositGroupRecord,
   cancellationToken?: CancellationToken,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   logger.info("processing deposit group in pending(deposit)");
   const depositGroupId = depositGroup.depositGroupId;
   const contractData = extractContractData(
@@ -1000,7 +1000,7 @@ async function processDepositGroupPendingDeposit(
     });
 
   notifyTransition(ws, transactionId, transitionInfo);
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 /**
@@ -1012,7 +1012,7 @@ export async function processDepositGroup(
   options: {
     cancellationToken?: CancellationToken;
   } = {},
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const depositGroup = await ws.db
     .mktx((x) => [x.depositGroups])
     .runReadOnly(async (tx) => {
@@ -1020,7 +1020,7 @@ export async function processDepositGroup(
     });
   if (!depositGroup) {
     logger.warn(`deposit group ${depositGroupId} not found`);
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
 
   switch (depositGroup.operationStatus) {
@@ -1042,7 +1042,7 @@ export async function processDepositGroup(
       return processDepositGroupAborting(ws, depositGroup);
   }
 
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 async function getExchangeWireFee(
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 56ef672dc..c0373704a 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -76,11 +76,10 @@ import {
 } from "../util/query.js";
 import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "../versions.js";
 import {
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResultType,
   runTaskWithErrorReporting,
   TaskIdentifiers,
-  unwrapOperationHandlerResultOrThrow,
+  TaskRunResult,
 } from "./common.js";
 
 const logger = new Logger("exchanges.ts");
@@ -559,13 +558,34 @@ export async function updateExchangeFromUrl(
   exchangeDetails: ExchangeDetailsRecord;
 }> {
   const canonUrl = canonicalizeBaseUrl(baseUrl);
-  return unwrapOperationHandlerResultOrThrow(
-    await runTaskWithErrorReporting(
-      ws,
-      TaskIdentifiers.forExchangeUpdateFromUrl(canonUrl),
-      () => updateExchangeFromUrlHandler(ws, canonUrl, options),
-    ),
+  const res = await runTaskWithErrorReporting(
+    ws,
+    TaskIdentifiers.forExchangeUpdateFromUrl(canonUrl),
+    () => updateExchangeFromUrlHandler(ws, canonUrl, options),
   );
+  switch (res.type) {
+    case TaskRunResultType.Finished: {
+      const now = AbsoluteTime.now();
+      const { exchange, exchangeDetails } = await ws.db
+        .mktx((x) => [x.exchanges, x.exchangeDetails])
+        .runReadWrite(async (tx) => {
+          let exchange = await tx.exchanges.get(canonUrl);
+          const exchangeDetails = await getExchangeDetails(tx, baseUrl);
+          return { exchange, exchangeDetails };
+        });
+      if (!exchange) {
+        throw Error("exchange not found");
+      }
+      if (!exchangeDetails) {
+        throw Error("exchange details not found");
+      }
+      return { exchange, exchangeDetails };
+    }
+    case TaskRunResultType.Error:
+      throw TalerError.fromUncheckedDetail(res.errorDetail);
+    default:
+      throw Error(`unexpected operation result (${res.type})`);
+  }
 }
 
 /**
@@ -581,12 +601,7 @@ export async function updateExchangeFromUrlHandler(
     forceNow?: boolean;
     cancellationToken?: CancellationToken;
   } = {},
-): Promise<
-  OperationAttemptResult<{
-    exchange: ExchangeRecord;
-    exchangeDetails: ExchangeDetailsRecord;
-  }>
-> {
+): Promise<TaskRunResult> {
   const forceNow = options.forceNow ?? false;
   logger.trace(
     `updating exchange info for ${exchangeBaseUrl}, forced: ${forceNow}`,
@@ -620,10 +635,7 @@ export async function updateExchangeFromUrlHandler(
       }
     }
 
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: { exchange, exchangeDetails },
-    };
+    return TaskRunResult.finished();
   }
 
   logger.info("updating exchange /keys info");
@@ -679,7 +691,7 @@ export async function updateExchangeFromUrlHandler(
       },
     );
     return {
-      type: OperationAttemptResultType.Error,
+      type: TaskRunResultType.Error,
       errorDetail,
     };
   }
@@ -911,13 +923,7 @@ export async function updateExchangeFromUrlHandler(
     });
   }
 
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: {
-      exchange: updated.exchange,
-      exchangeDetails: updated.exchangeDetails,
-    },
-  };
+  return TaskRunResult.finished();
 }
 
 /**
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts 
b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index f2df08247..c74fcedcf 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -112,8 +112,8 @@ import { checkDbInvariant } from "../util/invariants.js";
 import { GetReadOnlyAccess } from "../util/query.js";
 import {
   constructTaskIdentifier,
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
   RetryInfo,
   TaskIdentifiers,
 } from "./common.js";
@@ -325,7 +325,7 @@ export function extractContractData(
 async function processDownloadProposal(
   ws: InternalWalletState,
   proposalId: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const proposal = await ws.db
     .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
@@ -333,17 +333,11 @@ async function processDownloadProposal(
     });
 
   if (!proposal) {
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished();
   }
 
   if (proposal.purchaseStatus != PurchaseStatus.PendingDownloadingProposal) {
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished();
   }
 
   const transactionId = constructTransactionIdentifier({
@@ -560,10 +554,7 @@ async function processDownloadProposal(
 
   notifyTransition(ws, transactionId, transitionInfo);
 
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 /**
@@ -1065,7 +1056,7 @@ export async function checkPaymentByProposalId(
     notifyTransition(ws, transactionId, transitionInfo);
     // FIXME: What about error handling?! This doesn't properly store errors 
in the DB.
     const r = await processPurchasePay(ws, proposalId, { forceNow: true });
-    if (r.type !== OperationAttemptResultType.Finished) {
+    if (r.type !== TaskRunResultType.Finished) {
       // FIXME: This does not surface the original error
       throw Error("submitting pay failed");
     }
@@ -1253,7 +1244,7 @@ export async function runPayForConfirmPay(
   });
   logger.trace(`processPurchasePay response type ${res.type}`);
   switch (res.type) {
-    case OperationAttemptResultType.Finished: {
+    case TaskRunResultType.Finished: {
       const purchase = await ws.db
         .mktx((x) => [x.purchases])
         .runReadOnly(async (tx) => {
@@ -1272,7 +1263,7 @@ export async function runPayForConfirmPay(
         }),
       };
     }
-    case OperationAttemptResultType.Error: {
+    case TaskRunResultType.Error: {
       // We hide transient errors from the caller.
       const opRetry = await ws.db
         .mktx((x) => [x.operationRetries])
@@ -1286,7 +1277,7 @@ export async function runPayForConfirmPay(
         }),
       };
     }
-    case OperationAttemptResultType.Pending:
+    case TaskRunResultType.Pending:
       logger.trace("reporting pending as confirmPay response");
       return {
         type: ConfirmPayResultType.Pending,
@@ -1296,7 +1287,7 @@ export async function runPayForConfirmPay(
         }),
         lastError: undefined,
       };
-    case OperationAttemptResultType.Longpoll:
+    case TaskRunResultType.Longpoll:
       throw Error("unexpected processPurchasePay result (longpoll)");
     default:
       assertUnreachable(res);
@@ -1456,7 +1447,7 @@ export async function confirmPay(
 export async function processPurchase(
   ws: InternalWalletState,
   proposalId: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const purchase = await ws.db
     .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
@@ -1464,7 +1455,7 @@ export async function processPurchase(
     });
   if (!purchase) {
     return {
-      type: OperationAttemptResultType.Error,
+      type: TaskRunResultType.Error,
       errorDetail: {
         // FIXME: allocate more specific error code
         code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
@@ -1504,10 +1495,7 @@ export async function processPurchase(
     case PurchaseStatus.SuspendedQueryingAutoRefund:
     case PurchaseStatus.SuspendedQueryingRefund:
     case PurchaseStatus.FailedAbort:
-      return {
-        type: OperationAttemptResultType.Finished,
-        result: undefined,
-      };
+      return TaskRunResult.finished();
     default:
       assertUnreachable(purchase.purchaseStatus);
     // throw Error(`unexpected purchase status (${purchase.purchaseStatus})`);
@@ -1518,7 +1506,7 @@ export async function processPurchasePay(
   ws: InternalWalletState,
   proposalId: string,
   options: unknown = {},
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const purchase = await ws.db
     .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
@@ -1526,7 +1514,7 @@ export async function processPurchasePay(
     });
   if (!purchase) {
     return {
-      type: OperationAttemptResultType.Error,
+      type: TaskRunResultType.Error,
       errorDetail: {
         // FIXME: allocate more specific error code
         code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
@@ -1541,7 +1529,7 @@ export async function processPurchasePay(
     case PurchaseStatus.PendingPayingReplay:
       break;
     default:
-      return OperationAttemptResult.finishedEmpty();
+      return TaskRunResult.finished();
   }
   logger.trace(`processing purchase pay ${proposalId}`);
 
@@ -1589,7 +1577,7 @@ export async function processPurchasePay(
     if (resp.status >= 500 && resp.status <= 599) {
       const errDetails = await readUnexpectedResponseDetails(resp);
       return {
-        type: OperationAttemptResultType.Error,
+        type: TaskRunResultType.Error,
         errorDetail: makeErrorDetail(
           TalerErrorCode.WALLET_PAY_MERCHANT_SERVER_ERROR,
           {
@@ -1613,10 +1601,7 @@ export async function processPurchasePay(
 
         // FIXME: Should we really consider this to be pending?
 
-        return {
-          type: OperationAttemptResultType.Pending,
-          result: undefined,
-        };
+        return TaskRunResult.pending();
       }
     }
 
@@ -1677,7 +1662,7 @@ export async function processPurchasePay(
     await unblockBackup(ws, proposalId);
   }
 
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 export async function refuseProposal(
@@ -2114,7 +2099,7 @@ export function computePayMerchantTransactionActions(
 async function processPurchaseAutoRefund(
   ws: InternalWalletState,
   purchase: PurchaseRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const proposalId = purchase.proposalId;
   logger.trace(`processing auto-refund for proposal ${proposalId}`);
 
@@ -2130,7 +2115,7 @@ async function processPurchaseAutoRefund(
 
   // FIXME: Put this logic into runLongpollAsync?
   if (ws.activeLongpoll[taskId]) {
-    return OperationAttemptResult.longpoll();
+    return TaskRunResult.longpoll();
   }
 
   const download = await expectProposalDownload(ws, purchase);
@@ -2215,13 +2200,13 @@ async function processPurchaseAutoRefund(
     }
   });
 
-  return OperationAttemptResult.longpoll();
+  return TaskRunResult.longpoll();
 }
 
 async function processPurchaseAbortingRefund(
   ws: InternalWalletState,
   purchase: PurchaseRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const proposalId = purchase.proposalId;
   const download = await expectProposalDownload(ws, purchase);
   logger.trace(`processing aborting-refund for proposal ${proposalId}`);
@@ -2296,7 +2281,7 @@ async function processPurchaseAbortingRefund(
 async function processPurchaseQueryRefund(
   ws: InternalWalletState,
   purchase: PurchaseRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const proposalId = purchase.proposalId;
   logger.trace(`processing query-refund for proposal ${proposalId}`);
 
@@ -2341,7 +2326,7 @@ async function processPurchaseQueryRefund(
         return { oldTxState, newTxState };
       });
     notifyTransition(ws, transactionId, transitionInfo);
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   } else {
     const refundAwaiting = Amounts.sub(
       Amounts.parseOrThrow(orderStatus.refund_amount),
@@ -2367,14 +2352,14 @@ async function processPurchaseQueryRefund(
         return { oldTxState, newTxState };
       });
     notifyTransition(ws, transactionId, transitionInfo);
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
 }
 
 async function processPurchaseAcceptRefund(
   ws: InternalWalletState,
   purchase: PurchaseRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const proposalId = purchase.proposalId;
 
   const download = await expectProposalDownload(ws, purchase);
@@ -2472,7 +2457,7 @@ async function storeRefunds(
   purchase: PurchaseRecord,
   refunds: MerchantCoinRefundStatus[],
   reason: RefundReason,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   logger.info(`storing refunds: ${j2s(refunds)}`);
 
   const transactionId = constructTransactionIdentifier({
@@ -2699,16 +2684,16 @@ async function storeRefunds(
     });
 
   if (!result) {
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
 
   notifyTransition(ws, transactionId, result.transitionInfo);
 
   if (result.numPendingItemsTotal > 0) {
-    return OperationAttemptResult.pendingEmpty();
+    return TaskRunResult.pending();
   }
 
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 export function computeRefundTransactionState(
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
index 88bdcb90e..4c00ed592 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
@@ -66,8 +66,8 @@ import { assertUnreachable } from 
"../util/assertUnreachable.js";
 import { checkDbInvariant } from "../util/invariants.js";
 import {
   LongpollResult,
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
   constructTaskIdentifier,
   runLongpollAsync,
 } from "./common.js";
@@ -184,7 +184,7 @@ async function longpollKycStatus(
   exchangeUrl: string,
   kycInfo: KycPendingInfo,
   userType: KycUserType,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.PeerPullCredit,
     pursePub,
@@ -242,14 +242,14 @@ async function longpollKycStatus(
     }
   });
   return {
-    type: OperationAttemptResultType.Longpoll,
+    type: TaskRunResultType.Longpoll,
   };
 }
 
 async function processPeerPullCreditAbortingDeletePurse(
   ws: InternalWalletState,
   peerPullIni: PeerPullPaymentInitiationRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const { pursePub, pursePriv } = peerPullIni;
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.PeerPushDebit,
@@ -296,13 +296,13 @@ async function processPeerPullCreditAbortingDeletePurse(
     });
   notifyTransition(ws, transactionId, transitionInfo);
 
-  return OperationAttemptResult.pendingEmpty();
+  return TaskRunResult.pending();
 }
 
 async function handlePeerPullCreditWithdrawing(
   ws: InternalWalletState,
   pullIni: PeerPullPaymentInitiationRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   if (!pullIni.withdrawalGroupId) {
     throw Error("invalid db state (withdrawing, but no withdrawal group ID");
   }
@@ -346,17 +346,17 @@ async function handlePeerPullCreditWithdrawing(
     });
   notifyTransition(ws, transactionId, transitionInfo);
   if (finished) {
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   } else {
     // FIXME: Return indicator that we depend on the other operation!
-    return OperationAttemptResult.pendingEmpty();
+    return TaskRunResult.pending();
   }
 }
 
 async function handlePeerPullCreditCreatePurse(
   ws: InternalWalletState,
   pullIni: PeerPullPaymentInitiationRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount));
   const pursePub = pullIni.pursePub;
   const mergeReserve = await ws.db
@@ -447,16 +447,13 @@ async function handlePeerPullCreditCreatePurse(
       await tx.peerPullPaymentInitiations.put(pi2);
     });
 
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 export async function processPeerPullCredit(
   ws: InternalWalletState,
   pursePub: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const pullIni = await ws.db
     .mktx((x) => [x.peerPullPaymentInitiations])
     .runReadOnly(async (tx) => {
@@ -475,7 +472,7 @@ export async function processPeerPullCredit(
   if (ws.activeLongpoll[retryTag]) {
     logger.info("peer-pull-credit already in long-polling, returning!");
     return {
-      type: OperationAttemptResultType.Longpoll,
+      type: TaskRunResultType.Longpoll,
     };
   }
 
@@ -483,10 +480,7 @@ export async function processPeerPullCredit(
 
   switch (pullIni.status) {
     case PeerPullPaymentInitiationStatus.Done: {
-      return {
-        type: OperationAttemptResultType.Finished,
-        result: undefined,
-      };
+      return TaskRunResult.finished();
     }
     case PeerPullPaymentInitiationStatus.PendingReady:
       runLongpollAsync(ws, retryTag, async (cancellationToken) =>
@@ -496,7 +490,7 @@ export async function processPeerPullCredit(
         "returning early from processPeerPullCredit for long-polling in 
background",
       );
       return {
-        type: OperationAttemptResultType.Longpoll,
+        type: TaskRunResultType.Longpoll,
       };
     case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: {
       if (!pullIni.kycInfo) {
@@ -528,14 +522,14 @@ export async function processPeerPullCredit(
       assertUnreachable(pullIni.status);
   }
 
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 async function processPeerPullCreditKycRequired(
   ws: InternalWalletState,
   peerIni: PeerPullPaymentInitiationRecord,
   kycPending: WalletKycUuid,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.PeerPullCredit,
     pursePub: peerIni.pursePub,
@@ -560,10 +554,7 @@ async function processPeerPullCreditKycRequired(
     kycStatusRes.status === HttpStatusCode.NoContent
   ) {
     logger.warn("kyc requested, but already fulfilled");
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished();
   } else if (kycStatusRes.status === HttpStatusCode.Accepted) {
     const kycStatus = await kycStatusRes.json();
     logger.info(`kyc status: ${j2s(kycStatus)}`);
@@ -574,7 +565,7 @@ async function processPeerPullCreditKycRequired(
         if (!peerInc) {
           return {
             transitionInfo: undefined,
-            result: OperationAttemptResult.finishedEmpty(),
+            result: TaskRunResult.finished(),
           };
         }
         const oldTxState = computePeerPullCreditTransactionState(peerInc);
@@ -589,8 +580,8 @@ async function processPeerPullCreditKycRequired(
         await tx.peerPullPaymentInitiations.put(peerInc);
         // We'll remove this eventually!  New clients should rely on the
         // kycUrl field of the transaction, not the error code.
-        const res: OperationAttemptResult = {
-          type: OperationAttemptResultType.Error,
+        const res: TaskRunResult = {
+          type: TaskRunResultType.Error,
           errorDetail: makeErrorDetail(
             TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
             {
@@ -604,10 +595,7 @@ async function processPeerPullCreditKycRequired(
         };
       });
     notifyTransition(ws, transactionId, transitionInfo);
-    return {
-      type: OperationAttemptResultType.Pending,
-      result: undefined,
-    };
+    return TaskRunResult.pending();
   } else {
     throw Error(`unexpected response from kyc-check (${kycStatusRes.status})`);
   }
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
index 9d8fabfb2..1aa332439 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
@@ -62,8 +62,8 @@ import {
 import { assertUnreachable } from "../util/assertUnreachable.js";
 import { checkLogicInvariant } from "../util/invariants.js";
 import {
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
   TaskIdentifiers,
   constructTaskIdentifier,
   runTaskWithErrorReporting,
@@ -89,12 +89,12 @@ async function handlePurseCreationConflict(
   ws: InternalWalletState,
   peerPullInc: PeerPullPaymentIncomingRecord,
   resp: HttpResponse,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const pursePub = peerPullInc.pursePub;
   const errResp = await readTalerErrorResponse(resp);
   if (errResp.code !== TalerErrorCode.EXCHANGE_GENERIC_INSUFFICIENT_FUNDS) {
     await failPeerPullDebitTransaction(ws, pursePub);
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
 
   // FIXME: Properly parse!
@@ -167,13 +167,13 @@ async function handlePurseCreationConflict(
       }
       await tx.peerPullPaymentIncoming.put(myPpi);
     });
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 async function processPeerPullDebitPendingDeposit(
   ws: InternalWalletState,
   peerPullInc: PeerPullPaymentIncomingRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId;
   const pursePub = peerPullInc.pursePub;
 
@@ -299,21 +299,18 @@ async function processPeerPullDebitPendingDeposit(
     default: {
       const errResp = await readTalerErrorResponse(httpResp);
       return {
-        type: OperationAttemptResultType.Error,
+        type: TaskRunResultType.Error,
         errorDetail: errResp,
       };
     }
   }
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 async function processPeerPullDebitAbortingRefresh(
   ws: InternalWalletState,
   peerPullInc: PeerPullPaymentIncomingRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId;
   const abortRefreshGroupId = peerPullInc.abortRefreshGroupId;
   checkLogicInvariant(!!abortRefreshGroupId);
@@ -357,13 +354,13 @@ async function processPeerPullDebitAbortingRefresh(
     });
   notifyTransition(ws, transactionId, transitionInfo);
   // FIXME: Shouldn't this be finished in some cases?!
-  return OperationAttemptResult.pendingEmpty();
+  return TaskRunResult.pending();
 }
 
 export async function processPeerPullDebit(
   ws: InternalWalletState,
   peerPullPaymentIncomingId: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const peerPullInc = await ws.db
     .mktx((x) => [x.peerPullPaymentIncoming])
     .runReadOnly(async (tx) => {
@@ -379,10 +376,7 @@ export async function processPeerPullDebit(
     case PeerPullDebitRecordStatus.AbortingRefresh:
       return await processPeerPullDebitAbortingRefresh(ws, peerPullInc);
   }
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 export async function confirmPeerPullDebit(
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
index 3e5750af7..e76b934fa 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
@@ -62,8 +62,8 @@ import {
 import { assertUnreachable } from "../util/assertUnreachable.js";
 import { checkDbInvariant } from "../util/invariants.js";
 import {
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
   constructTaskIdentifier,
   runLongpollAsync,
 } from "./common.js";
@@ -233,7 +233,7 @@ async function longpollKycStatus(
   exchangeUrl: string,
   kycInfo: KycPendingInfo,
   userType: KycUserType,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.PeerPushCredit,
     peerPushPaymentIncomingId,
@@ -293,7 +293,7 @@ async function longpollKycStatus(
     }
   });
   return {
-    type: OperationAttemptResultType.Longpoll,
+    type: TaskRunResultType.Longpoll,
   };
 }
 
@@ -301,7 +301,7 @@ async function processPeerPushCreditKycRequired(
   ws: InternalWalletState,
   peerInc: PeerPushPaymentIncomingRecord,
   kycPending: WalletKycUuid,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.PeerPushCredit,
     peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId,
@@ -326,10 +326,7 @@ async function processPeerPushCreditKycRequired(
     kycStatusRes.status === HttpStatusCode.NoContent
   ) {
     logger.warn("kyc requested, but already fulfilled");
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished();
   } else if (kycStatusRes.status === HttpStatusCode.Accepted) {
     const kycStatus = await kycStatusRes.json();
     logger.info(`kyc status: ${j2s(kycStatus)}`);
@@ -342,7 +339,7 @@ async function processPeerPushCreditKycRequired(
         if (!peerInc) {
           return {
             transitionInfo: undefined,
-            result: OperationAttemptResult.finishedEmpty(),
+            result: TaskRunResult.finished(),
           };
         }
         const oldTxState = computePeerPushCreditTransactionState(peerInc);
@@ -356,8 +353,8 @@ async function processPeerPushCreditKycRequired(
         await tx.peerPushPaymentIncoming.put(peerInc);
         // We'll remove this eventually!  New clients should rely on the
         // kycUrl field of the transaction, not the error code.
-        const res: OperationAttemptResult = {
-          type: OperationAttemptResultType.Error,
+        const res: TaskRunResult = {
+          type: TaskRunResultType.Error,
           errorDetail: makeErrorDetail(
             TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
             {
@@ -381,7 +378,7 @@ async function handlePendingMerge(
   ws: InternalWalletState,
   peerInc: PeerPushPaymentIncomingRecord,
   contractTerms: PeerContractTerms,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const { peerPushPaymentIncomingId } = peerInc;
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.PeerPushCredit,
@@ -506,16 +503,13 @@ async function handlePendingMerge(
   );
   notifyTransition(ws, transactionId, txRes?.peerPushCreditTransition);
 
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 async function handlePendingWithdrawing(
   ws: InternalWalletState,
   peerInc: PeerPushPaymentIncomingRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   if (!peerInc.withdrawalGroupId) {
     throw Error("invalid db state (withdrawing, but no withdrawal group ID");
   }
@@ -561,17 +555,17 @@ async function handlePendingWithdrawing(
     });
   notifyTransition(ws, transactionId, transitionInfo);
   if (finished) {
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   } else {
     // FIXME: Return indicator that we depend on the other operation!
-    return OperationAttemptResult.pendingEmpty();
+    return TaskRunResult.pending();
   }
 }
 
 export async function processPeerPushCredit(
   ws: InternalWalletState,
   peerPushPaymentIncomingId: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   let peerInc: PeerPushPaymentIncomingRecord | undefined;
   let contractTerms: PeerContractTerms | undefined;
   await ws.db
@@ -617,7 +611,7 @@ export async function processPeerPushCredit(
       return handlePendingWithdrawing(ws, peerInc);
 
     default:
-      return OperationAttemptResult.finishedEmpty();
+      return TaskRunResult.finished();
   }
 }
 
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
index 9ae94fff8..c853bc0ef 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
@@ -61,8 +61,8 @@ import { PendingTaskType } from "../pending-types.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
 import { checkLogicInvariant } from "../util/invariants.js";
 import {
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
   constructTaskIdentifier,
   runLongpollAsync,
   spendCoins,
@@ -110,12 +110,12 @@ async function handlePurseCreationConflict(
   ws: InternalWalletState,
   peerPushInitiation: PeerPushPaymentInitiationRecord,
   resp: HttpResponse,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const pursePub = peerPushInitiation.pursePub;
   const errResp = await readTalerErrorResponse(resp);
   if (errResp.code !== TalerErrorCode.EXCHANGE_GENERIC_INSUFFICIENT_FUNDS) {
     await failPeerPushDebitTransaction(ws, pursePub);
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
 
   // FIXME: Properly parse!
@@ -176,13 +176,13 @@ async function handlePurseCreationConflict(
       }
       await tx.peerPushPaymentInitiations.put(myPpi);
     });
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 async function processPeerPushDebitCreateReserve(
   ws: InternalWalletState,
   peerPushInitiation: PeerPushPaymentInitiationRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   logger.info("processing peer-push-debit pending(create-reserve)");
   const pursePub = peerPushInitiation.pursePub;
   const purseExpiration = peerPushInitiation.purseExpiration;
@@ -264,7 +264,7 @@ async function processPeerPushDebitCreateReserve(
     case HttpStatusCode.Forbidden: {
       // FIXME: Store this error!
       await failPeerPushDebitTransaction(ws, pursePub);
-      return OperationAttemptResult.finishedEmpty();
+      return TaskRunResult.finished();
     }
     case HttpStatusCode.Conflict: {
       // Handle double-spending
@@ -273,7 +273,7 @@ async function processPeerPushDebitCreateReserve(
     default: {
       const errResp = await readTalerErrorResponse(httpResp);
       return {
-        type: OperationAttemptResultType.Error,
+        type: TaskRunResultType.Error,
         errorDetail: errResp,
       };
     }
@@ -289,13 +289,13 @@ async function processPeerPushDebitCreateReserve(
     stTo: PeerPushPaymentInitiationStatus.PendingReady,
   });
 
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 async function processPeerPushDebitAbortingDeletePurse(
   ws: InternalWalletState,
   peerPushInitiation: PeerPushPaymentInitiationRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const { pursePub, pursePriv } = peerPushInitiation;
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.PeerPushDebit,
@@ -364,7 +364,7 @@ async function processPeerPushDebitAbortingDeletePurse(
     });
   notifyTransition(ws, transactionId, transitionInfo);
 
-  return OperationAttemptResult.pendingEmpty();
+  return TaskRunResult.pending();
 }
 
 interface SimpleTransition {
@@ -406,7 +406,7 @@ async function transitionPeerPushDebitTransaction(
 async function processPeerPushDebitAbortingRefresh(
   ws: InternalWalletState,
   peerPushInitiation: PeerPushPaymentInitiationRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const pursePub = peerPushInitiation.pursePub;
   const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId;
   checkLogicInvariant(!!abortRefreshGroupId);
@@ -448,7 +448,7 @@ async function processPeerPushDebitAbortingRefresh(
     });
   notifyTransition(ws, transactionId, transitionInfo);
   // FIXME: Shouldn't this be finished in some cases?!
-  return OperationAttemptResult.pendingEmpty();
+  return TaskRunResult.pending();
 }
 
 /**
@@ -457,7 +457,7 @@ async function processPeerPushDebitAbortingRefresh(
 async function processPeerPushDebitReady(
   ws: InternalWalletState,
   peerPushInitiation: PeerPushPaymentInitiationRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   logger.info("processing peer-push-debit pending(ready)");
   const pursePub = peerPushInitiation.pursePub;
   const retryTag = constructTaskIdentifier({
@@ -520,14 +520,14 @@ async function processPeerPushDebitReady(
     "returning early from peer-push-debit for long-polling in background",
   );
   return {
-    type: OperationAttemptResultType.Longpoll,
+    type: TaskRunResultType.Longpoll,
   };
 }
 
 export async function processPeerPushDebit(
   ws: InternalWalletState,
   pursePub: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const peerPushInitiation = await ws.db
     .mktx((x) => [x.peerPushPaymentInitiations])
     .runReadOnly(async (tx) => {
@@ -546,7 +546,7 @@ export async function processPeerPushDebit(
   if (ws.activeLongpoll[retryTag]) {
     logger.info("peer-push-debit task already in long-polling, returning!");
     return {
-      type: OperationAttemptResultType.Longpoll,
+      type: TaskRunResultType.Longpoll,
     };
   }
 
@@ -567,10 +567,7 @@ export async function processPeerPushDebit(
     }
   }
 
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 /**
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts 
b/packages/taler-wallet-core/src/operations/recoup.ts
index 056aa83b8..c8c766d1b 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -55,7 +55,7 @@ import { checkDbInvariant } from "../util/invariants.js";
 import { GetReadWriteAccess } from "../util/query.js";
 import { createRefreshGroup, processRefreshGroup } from "./refresh.js";
 import { internalCreateWithdrawalGroup } from "./withdraw.js";
-import { OperationAttemptResult } from "./common.js";
+import { TaskRunResult } from "./common.js";
 
 const logger = new Logger("operations/recoup.ts");
 
@@ -289,18 +289,18 @@ async function recoupRefreshCoin(
 export async function processRecoupGroup(
   ws: InternalWalletState,
   recoupGroupId: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   let recoupGroup = await ws.db
     .mktx((x) => [x.recoupGroups])
     .runReadOnly(async (tx) => {
       return tx.recoupGroups.get(recoupGroupId);
     });
   if (!recoupGroup) {
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
   if (recoupGroup.timestampFinished) {
     logger.trace("recoup group finished");
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
   const ps = recoupGroup.coinPubs.map(async (x, i) => {
     try {
@@ -318,12 +318,12 @@ export async function processRecoupGroup(
       return tx.recoupGroups.get(recoupGroupId);
     });
   if (!recoupGroup) {
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
 
   for (const b of recoupGroup.recoupFinishedPerCoin) {
     if (!b) {
-      return OperationAttemptResult.finishedEmpty();
+      return TaskRunResult.finished();
     }
   }
 
@@ -408,7 +408,7 @@ export async function processRecoupGroup(
       }
       await tx.recoupGroups.put(rg2);
     });
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 export async function createRecoupGroup(
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index c1a16badf..caa5f9c9f 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -89,8 +89,8 @@ import {
   constructTaskIdentifier,
   makeCoinAvailable,
   makeCoinsVisible,
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
 } from "./common.js";
 import { updateExchangeFromUrl } from "./exchanges.js";
 import {
@@ -770,23 +770,17 @@ export async function processRefreshGroup(
   ws: InternalWalletState,
   refreshGroupId: string,
   options: Record<string, never> = {},
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   logger.info(`processing refresh group ${refreshGroupId}`);
 
   const refreshGroup = await ws.db
     .mktx((x) => [x.refreshGroups])
     .runReadOnly(async (tx) => tx.refreshGroups.get(refreshGroupId));
   if (!refreshGroup) {
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished()
   }
   if (refreshGroup.timestampFinished) {
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished();
   }
   // Process refresh sessions of the group in parallel.
   logger.trace("processing refresh sessions for old coins");
@@ -823,14 +817,11 @@ export async function processRefreshGroup(
     logger.warn(`exception: ${e}`);
   }
   if (inShutdown) {
-    return {
-      type: OperationAttemptResultType.Pending,
-      result: undefined,
-    };
+    return TaskRunResult.pending();
   }
   if (errors.length > 0) {
     return {
-      type: OperationAttemptResultType.Error,
+      type: TaskRunResultType.Error,
       errorDetail: makeErrorDetail(
         TalerErrorCode.WALLET_REFRESH_GROUP_INCOMPLETE,
         {
@@ -841,10 +832,7 @@ export async function processRefreshGroup(
     };
   }
 
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.pending();
 }
 
 async function processRefreshSession(
@@ -1122,7 +1110,7 @@ function getAutoRefreshExecuteThreshold(d: 
DenominationRecord): AbsoluteTime {
 export async function autoRefresh(
   ws: InternalWalletState,
   exchangeBaseUrl: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   logger.info(`doing auto-refresh check for '${exchangeBaseUrl}'`);
 
   // We must make sure that the exchange is up-to-date so that
@@ -1204,7 +1192,7 @@ export async function autoRefresh(
         AbsoluteTime.toPreciseTimestamp(minCheckThreshold);
       await tx.exchanges.put(exchange);
     });
-  return OperationAttemptResult.finishedEmpty();
+  return TaskRunResult.finished();
 }
 
 export function computeRefreshTransactionState(
diff --git a/packages/taler-wallet-core/src/operations/testing.ts 
b/packages/taler-wallet-core/src/operations/testing.ts
index ece71439c..77e218cd7 100644
--- a/packages/taler-wallet-core/src/operations/testing.ts
+++ b/packages/taler-wallet-core/src/operations/testing.ts
@@ -24,6 +24,7 @@ import {
   Duration,
   IntegrationTestV2Args,
   Logger,
+  NotificationType,
   stringToBytes,
   TestPayResult,
   WithdrawTestBalanceRequest,
@@ -64,6 +65,7 @@ import {
   confirmPeerPushCredit,
 } from "./pay-peer-push-credit.js";
 import { initiatePeerPushDebit } from "./pay-peer-push-debit.js";
+import { OpenedPromise, openPromise } from "../index.js";
 
 const logger = new Logger("operations/testing.ts");
 
@@ -445,6 +447,18 @@ export async function runIntegrationTest(
   logger.trace("integration test: all done!");
 }
 
+async function waitUntilDone(ws: InternalWalletState): Promise<void> {
+  let p: OpenedPromise<void> | undefined = undefined;
+  ws.addNotificationListener((notif) => {
+    if (!p) {
+      return;
+    }
+    if (notif.type === NotificationType.TransactionStateTransition) {
+      p.resolve();
+    }
+  });
+}
+
 export async function runIntegrationTest2(
   ws: InternalWalletState,
   args: IntegrationTestV2Args,
diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index 18ef03c51..e56fb1e8d 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -62,8 +62,8 @@ import {
   constructTaskIdentifier,
   makeCoinAvailable,
   makeCoinsVisible,
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
 } from "./common.js";
 import { updateExchangeFromUrl } from "./exchanges.js";
 import {
@@ -241,17 +241,14 @@ export async function prepareTip(
 export async function processTip(
   ws: InternalWalletState,
   walletTipId: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const tipRecord = await ws.db
     .mktx((x) => [x.tips])
     .runReadOnly(async (tx) => {
       return tx.tips.get(walletTipId);
     });
   if (!tipRecord) {
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+    return TaskRunResult.finished();
   }
 
   switch (tipRecord.status) {
@@ -259,10 +256,7 @@ export async function processTip(
     case TipRecordStatus.DialogAccept:
     case TipRecordStatus.Done:
     case TipRecordStatus.SuspendidPickup:
-      return {
-        type: OperationAttemptResultType.Finished,
-        result: undefined,
-      };
+      return TaskRunResult.finished();
   }
 
   const transactionId = constructTransactionIdentifier({
@@ -324,7 +318,7 @@ export async function processTip(
     logger.trace(`got transient tip error`);
     // FIXME: wrap in another error code that indicates a transient error
     return {
-      type: OperationAttemptResultType.Error,
+      type: TaskRunResultType.Error,
       errorDetail: makeErrorDetail(
         TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
         getHttpResponseErrorDetails(merchantResp),
@@ -376,7 +370,7 @@ export async function processTip(
 
     if (!isValid) {
       return {
-        type: OperationAttemptResultType.Error,
+        type: TaskRunResultType.Error,
         errorDetail: makeErrorDetail(
           TalerErrorCode.WALLET_TIPPING_COIN_SIGNATURE_INVALID,
           {},
@@ -430,10 +424,7 @@ export async function processTip(
   notifyTransition(ws, transactionId, transitionInfo);
   ws.notify({ type: NotificationType.BalanceChange });
 
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 export async function acceptTip(
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index 1362ca278..f972d3cb1 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -90,8 +90,8 @@ import {
 } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import {
-  OperationAttemptResult,
-  OperationAttemptResultType,
+  TaskRunResult,
+  TaskRunResultType,
   TaskIdentifiers,
   constructTaskIdentifier,
   makeCoinAvailable,
@@ -1326,7 +1326,7 @@ export interface WithdrawalGroupContext {
 async function processWithdrawalGroupAbortingBank(
   ws: InternalWalletState,
   withdrawalGroup: WithdrawalGroupRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const { withdrawalGroupId } = withdrawalGroup;
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.Withdrawal,
@@ -1363,10 +1363,7 @@ async function processWithdrawalGroupAbortingBank(
       };
     });
   notifyTransition(ws, transactionId, transitionInfo);
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 /**
@@ -1413,7 +1410,7 @@ async function transitionKycSatisfied(
 async function processWithdrawalGroupPendingKyc(
   ws: InternalWalletState,
   withdrawalGroup: WithdrawalGroupRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const userType = "individual";
   const kycInfo = withdrawalGroup.kycPending;
   if (!kycInfo) {
@@ -1456,13 +1453,13 @@ async function processWithdrawalGroupPendingKyc(
       );
     }
   });
-  return OperationAttemptResult.longpoll();
+  return TaskRunResult.longpoll();
 }
 
 async function processWithdrawalGroupPendingReady(
   ws: InternalWalletState,
   withdrawalGroup: WithdrawalGroupRecord,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   const { withdrawalGroupId } = withdrawalGroup;
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.Withdrawal,
@@ -1494,7 +1491,7 @@ async function processWithdrawalGroupPendingReady(
         };
       });
     notifyTransition(ws, transactionId, transitionInfo);
-    return OperationAttemptResult.finishedEmpty();
+    return TaskRunResult.finished();
   }
 
   const numTotalCoins = withdrawalGroup.denomsSel.selectedDenoms
@@ -1608,7 +1605,7 @@ async function processWithdrawalGroupPendingReady(
 
   if (numPlanchetErrors > 0) {
     return {
-      type: OperationAttemptResultType.Error,
+      type: TaskRunResultType.Error,
       errorDetail: makeErrorDetail(
         TalerErrorCode.WALLET_WITHDRAWAL_GROUP_INCOMPLETE,
         {
@@ -1619,16 +1616,13 @@ async function processWithdrawalGroupPendingReady(
     };
   }
 
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
+  return TaskRunResult.finished();
 }
 
 export async function processWithdrawalGroup(
   ws: InternalWalletState,
   withdrawalGroupId: string,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   logger.trace("processing withdrawal group", withdrawalGroupId);
   const withdrawalGroup = await ws.db
     .mktx((x) => [x.withdrawalGroups])
@@ -1646,7 +1640,7 @@ export async function processWithdrawalGroup(
   if (ws.activeLongpoll[retryTag]) {
     logger.info("withdrawal group already in long-polling, returning!");
     return {
-      type: OperationAttemptResultType.Longpoll,
+      type: TaskRunResultType.Longpoll,
     };
   }
 
@@ -1663,7 +1657,7 @@ export async function processWithdrawalGroup(
         "returning early from withdrawal for long-polling in background",
       );
       return {
-        type: OperationAttemptResultType.Longpoll,
+        type: TaskRunResultType.Longpoll,
       };
     }
     case WithdrawalGroupStatus.PendingWaitConfirmBank: {
@@ -1671,15 +1665,9 @@ export async function processWithdrawalGroup(
       switch (res.status) {
         case BankStatusResultCode.Aborted:
         case BankStatusResultCode.Done:
-          return {
-            type: OperationAttemptResultType.Finished,
-            result: undefined,
-          };
+          return TaskRunResult.finished();
         case BankStatusResultCode.Waiting: {
-          return {
-            type: OperationAttemptResultType.Pending,
-            result: undefined,
-          };
+          return TaskRunResult.pending();
         }
       }
       break;
@@ -1687,14 +1675,11 @@ export async function processWithdrawalGroup(
     case WithdrawalGroupStatus.Finished:
     case WithdrawalGroupStatus.FailedBankAborted: {
       // FIXME
-      return {
-        type: OperationAttemptResultType.Pending,
-        result: undefined,
-      };
+      return TaskRunResult.pending();
     }
     case WithdrawalGroupStatus.PendingAml:
       // FIXME: Handle this case, withdrawal doesn't support AML yet.
-      return OperationAttemptResult.pendingEmpty();
+      return TaskRunResult.pending();
     case WithdrawalGroupStatus.PendingKyc:
       return processWithdrawalGroupPendingKyc(ws, withdrawalGroup);
     case WithdrawalGroupStatus.PendingReady:
@@ -1713,7 +1698,7 @@ export async function processWithdrawalGroup(
     case WithdrawalGroupStatus.SuspendedRegisteringBank:
     case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
       // Nothing to do.
-      return OperationAttemptResult.finishedEmpty();
+      return TaskRunResult.finished();
     default:
       assertUnreachable(withdrawalGroup.status);
   }
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index c72c7236f..583dc33d4 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -171,6 +171,7 @@ import {
 import { setWalletDeviceId } from "./operations/backup/state.js";
 import { getBalanceDetail, getBalances } from "./operations/balance.js";
 import {
+  TaskRunResult,
   getExchangeTosStatus,
   makeExchangeListItem,
   runTaskWithErrorReporting,
@@ -287,7 +288,6 @@ import {
   GetReadWriteAccess,
 } from "./util/query.js";
 import {
-  OperationAttemptResult,
   TaskIdentifiers,
 } from "./operations/common.js";
 import { TimerAPI, TimerGroup } from "./util/timer.js";
@@ -320,7 +320,7 @@ const logger = new Logger("wallet.ts");
 async function callOperationHandler(
   ws: InternalWalletState,
   pending: PendingTaskInfo,
-): Promise<OperationAttemptResult> {
+): Promise<TaskRunResult> {
   switch (pending.type) {
     case PendingTaskType.ExchangeUpdate:
       return await updateExchangeFromUrlHandler(ws, pending.exchangeBaseUrl);
diff --git a/packages/taler-wallet-embedded/src/wallet-qjs.ts 
b/packages/taler-wallet-embedded/src/wallet-qjs.ts
index 57452301f..7e2ee1b2d 100644
--- a/packages/taler-wallet-embedded/src/wallet-qjs.ts
+++ b/packages/taler-wallet-embedded/src/wallet-qjs.ts
@@ -54,6 +54,11 @@ setPRNG(function (x: Uint8Array, n: number) {
 
 const logger = new Logger("taler-wallet-embedded/index.ts");
 
+/**
+ * Sends JSON to the host application, i.e. the process that
+ * runs the JavaScript interpreter (quickjs / qtart) to run
+ * the embedded wallet.
+ */
 function sendNativeMessage(ev: CoreApiMessageEnvelope): void {
   const m = JSON.stringify(ev);
   qjsOs.postMessageToHost(m);
@@ -183,21 +188,25 @@ export function installNativeWalletListener(): void {
     const id = msg.id;
     logger.info(`native listener: got request for ${operation} (${id})`);
 
-    let respMsg: CoreApiResponse;
-    try {
-      respMsg = await handler.handleMessage(operation, id, msg.args ?? {});
-    } catch (e) {
-      respMsg = {
-        type: "error",
-        id,
-        operation,
-        error: getErrorDetailFromException(e),
-      };
+    if (operation === "anastasisReduce") {
+      sendNativeMessage(respMsg);
+    } else {
+      let respMsg: CoreApiResponse;
+      try {
+        respMsg = await handler.handleMessage(operation, id, msg.args ?? {});
+      } catch (e) {
+        respMsg = {
+          type: "error",
+          id,
+          operation,
+          error: getErrorDetailFromException(e),
+        };
+      }
+      logger.info(
+        `native listener: sending back ${respMsg.type} message for operation 
${operation} (${id})`,
+      );
+      sendNativeMessage(respMsg);
     }
-    logger.info(
-      `native listener: sending back ${respMsg.type} message for operation 
${operation} (${id})`,
-    );
-    sendNativeMessage(respMsg);
   };
 
   qjsOs.setMessageFromHostHandler((m) => onMessage(m));

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



reply via email to

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