implement common wallet-core API handler
This commit is contained in:
parent
80433b9399
commit
56ece296e0
3079
src/TalerErrorCode.ts
Normal file
3079
src/TalerErrorCode.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,7 @@ import {
|
||||
WALLET_MERCHANT_PROTOCOL_VERSION,
|
||||
} from "../operations/versions";
|
||||
import { Amounts } from "../util/amounts";
|
||||
import { handleCoreApiRequest } from "../walletCoreApiHandler";
|
||||
|
||||
// @ts-ignore: special built-in module
|
||||
//import akono = require("akono");
|
||||
@ -168,88 +169,8 @@ class AndroidWalletMessageHandler {
|
||||
},
|
||||
};
|
||||
}
|
||||
case "getTransactions": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.getTransactions(args);
|
||||
}
|
||||
case "abortProposal": {
|
||||
const wallet = await this.wp.promise;
|
||||
if (typeof args.proposalId !== "string") {
|
||||
throw Error("propsalId must be a string");
|
||||
}
|
||||
return await wallet.refuseProposal(args.proposalId);
|
||||
}
|
||||
case "getBalances": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.getBalances();
|
||||
}
|
||||
case "getPendingOperations": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.getPendingOperations();
|
||||
}
|
||||
case "listExchanges": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.getExchanges();
|
||||
}
|
||||
case "addExchange": {
|
||||
const wallet = await this.wp.promise;
|
||||
await wallet.updateExchangeFromUrl(args.exchangeBaseUrl);
|
||||
return {};
|
||||
}
|
||||
case "getWithdrawalDetailsForAmount": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.getWithdrawalDetailsForAmount(
|
||||
args.exchangeBaseUrl,
|
||||
args.amount,
|
||||
);
|
||||
}
|
||||
case "withdrawTestkudos": {
|
||||
const wallet = await this.wp.promise;
|
||||
try {
|
||||
await withdrawTestBalance(wallet);
|
||||
} catch (e) {
|
||||
console.log("error during withdrawTestBalance", e);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case "getHistory": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.getHistory();
|
||||
}
|
||||
case "getExchangeTos": {
|
||||
const wallet = await this.wp.promise;
|
||||
const exchangeBaseUrl = args.exchangeBaseUrl;
|
||||
return wallet.getExchangeTos(exchangeBaseUrl);
|
||||
}
|
||||
case "setExchangeTosAccepted": {
|
||||
const wallet = await this.wp.promise;
|
||||
await wallet.acceptExchangeTermsOfService(
|
||||
args.exchangeBaseUrl,
|
||||
args.acceptedEtag,
|
||||
);
|
||||
return {};
|
||||
}
|
||||
case "retryPendingNow": {
|
||||
const wallet = await this.wp.promise;
|
||||
await wallet.runPending(true);
|
||||
return {};
|
||||
}
|
||||
case "preparePay": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.preparePayForUri(args.url);
|
||||
break;
|
||||
}
|
||||
case "confirmPay": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.confirmPay(args.proposalId, args.sessionId);
|
||||
}
|
||||
case "acceptManualWithdrawal": {
|
||||
const wallet = await this.wp.promise;
|
||||
const res = await wallet.acceptManualWithdrawal(
|
||||
args.exchangeBaseUrl,
|
||||
Amounts.parseOrThrow(args.amount),
|
||||
);
|
||||
return res;
|
||||
return [];
|
||||
}
|
||||
case "startTunnel": {
|
||||
// this.httpLib.useNfcTunnel = true;
|
||||
@ -263,31 +184,6 @@ class AndroidWalletMessageHandler {
|
||||
// httpLib.handleTunnelResponse(msg.args);
|
||||
throw Error("not implemented");
|
||||
}
|
||||
case "getWithdrawDetailsForUri": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.getWithdrawDetailsForUri(
|
||||
args.talerWithdrawUri,
|
||||
args.selectedExchange,
|
||||
);
|
||||
}
|
||||
case "applyRefund": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.applyRefund(args.talerRefundUri);
|
||||
}
|
||||
case "acceptExchangeTermsOfService": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.acceptExchangeTermsOfService(
|
||||
args.exchangeBaseUrl,
|
||||
args.etag,
|
||||
);
|
||||
}
|
||||
case "acceptWithdrawal": {
|
||||
const wallet = await this.wp.promise;
|
||||
return await wallet.acceptWithdrawal(
|
||||
args.talerWithdrawUri,
|
||||
args.selectedExchange,
|
||||
);
|
||||
}
|
||||
case "reset": {
|
||||
const oldArgs = this.walletArgs;
|
||||
this.walletArgs = { ...oldArgs };
|
||||
@ -312,8 +208,11 @@ class AndroidWalletMessageHandler {
|
||||
this.wp.resolve(w);
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
throw Error(`operation "${operation}" not understood`);
|
||||
default: {
|
||||
const wallet = await this.wp.promise;
|
||||
return await handleCoreApiRequest(wallet, operation, id, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,23 +199,22 @@ walletCli
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("api", "balance", { help: "Call the wallet-core API directly." })
|
||||
.subcommand("api", "api", { help: "Call the wallet-core API directly." })
|
||||
.requiredArgument("operation", clk.STRING)
|
||||
.requiredArgument("request", clk.STRING)
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
let requestJson;
|
||||
try {
|
||||
requestJson = JSON.parse(args.api.operation);
|
||||
requestJson = JSON.parse(args.api.request);
|
||||
} catch (e) {
|
||||
console.error("malformed request");
|
||||
console.error("Invalid JSON");
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
const resp = await handleCoreApiRequest(
|
||||
wallet,
|
||||
args.api.operation,
|
||||
1,
|
||||
"reqid-1",
|
||||
requestJson,
|
||||
);
|
||||
console.log(JSON.stringify(resp, undefined, 2));
|
||||
|
@ -51,6 +51,9 @@ import {
|
||||
readSuccessResponseJsonOrThrow,
|
||||
readSuccessResponseTextOrThrow,
|
||||
} from "../util/http";
|
||||
import { Logger } from "../util/logging";
|
||||
|
||||
const logger = new Logger("exchanges.ts");
|
||||
|
||||
async function denominationRecordFromKeys(
|
||||
ws: InternalWalletState,
|
||||
@ -197,7 +200,7 @@ async function updateExchangeWithKeys(
|
||||
// Handle recoup
|
||||
const recoupDenomList = exchangeKeysJson.recoup ?? [];
|
||||
const newlyRevokedCoinPubs: string[] = [];
|
||||
console.log("recoup list from exchange", recoupDenomList);
|
||||
logger.trace("recoup list from exchange", recoupDenomList);
|
||||
for (const recoupInfo of recoupDenomList) {
|
||||
const oldDenom = await tx.getIndexed(
|
||||
Stores.denominations.denomPubHashIndex,
|
||||
@ -354,7 +357,7 @@ async function updateExchangeWithWireInfo(
|
||||
);
|
||||
|
||||
for (const a of wireInfo.accounts) {
|
||||
console.log("validating exchange acct");
|
||||
logger.trace("validating exchange acct");
|
||||
const isValid = await ws.cryptoApi.isValidWireAccount(
|
||||
a.payto_uri,
|
||||
a.master_sig,
|
||||
|
@ -440,11 +440,8 @@ async function incrementReserveRetry(
|
||||
if (!r.retryInfo) {
|
||||
return;
|
||||
}
|
||||
console.log("updating retry info");
|
||||
console.log("before", r.retryInfo);
|
||||
r.retryInfo.retryCounter++;
|
||||
updateRetryInfoTimeout(r.retryInfo);
|
||||
console.log("after", r.retryInfo);
|
||||
r.lastError = err;
|
||||
await tx.put(Stores.reserves, r);
|
||||
});
|
||||
@ -528,16 +525,10 @@ async function updateReserve(
|
||||
reserveInfo.history,
|
||||
);
|
||||
|
||||
console.log(
|
||||
"reconciled history:",
|
||||
JSON.stringify(reconciled, undefined, 2),
|
||||
);
|
||||
|
||||
const summary = summarizeReserveHistory(
|
||||
reconciled.updatedLocalHistory,
|
||||
currency,
|
||||
);
|
||||
console.log("summary", summary);
|
||||
|
||||
if (
|
||||
reconciled.newAddedItems.length + reconciled.newMatchedItems.length !=
|
||||
@ -765,7 +756,7 @@ async function depleteReserve(
|
||||
);
|
||||
|
||||
if (newWithdrawalGroup) {
|
||||
console.log("processing new withdraw group");
|
||||
logger.trace("processing new withdraw group");
|
||||
ws.notify({
|
||||
type: NotificationType.WithdrawGroupCreated,
|
||||
withdrawalGroupId: newWithdrawalGroup.withdrawalGroupId,
|
||||
|
@ -26,6 +26,7 @@
|
||||
*/
|
||||
import { Timestamp } from "../util/time";
|
||||
import { AmountString, Product } from "./talerTypes";
|
||||
import { Codec, makeCodecForObject, makeCodecOptional, codecForString } from "../util/codec";
|
||||
|
||||
export interface TransactionsRequest {
|
||||
/**
|
||||
@ -300,3 +301,10 @@ interface TransactionRefresh extends TransactionCommon {
|
||||
// Amount that will be paid as fees for the refresh
|
||||
amountEffective: AmountString;
|
||||
}
|
||||
|
||||
|
||||
export const codecForTransactionsRequest = (): Codec<TransactionsRequest> =>
|
||||
makeCodecForObject<TransactionsRequest>()
|
||||
.property("currency", makeCodecOptional(codecForString))
|
||||
.property("search", makeCodecOptional(codecForString))
|
||||
.build("TransactionsRequest");
|
@ -25,6 +25,9 @@
|
||||
* Imports.
|
||||
*/
|
||||
import { Duration } from "./time";
|
||||
import { Logger } from "./logging";
|
||||
|
||||
const logger = new Logger("timer.ts");
|
||||
|
||||
/**
|
||||
* Cancelable timer.
|
||||
@ -119,7 +122,7 @@ export class TimerGroup {
|
||||
|
||||
after(delayMs: number, callback: () => void): TimerHandle {
|
||||
if (this.stopped) {
|
||||
console.warn("dropping timer since timer group is stopped");
|
||||
logger.warn("dropping timer since timer group is stopped");
|
||||
return nullTimerHandle;
|
||||
}
|
||||
const h = after(delayMs, callback);
|
||||
@ -138,7 +141,7 @@ export class TimerGroup {
|
||||
|
||||
every(delayMs: number, callback: () => void): TimerHandle {
|
||||
if (this.stopped) {
|
||||
console.warn("dropping timer since timer group is stopped");
|
||||
logger.warn("dropping timer since timer group is stopped");
|
||||
return nullTimerHandle;
|
||||
}
|
||||
const h = every(delayMs, callback);
|
||||
|
@ -195,7 +195,7 @@ export class Wallet {
|
||||
pending: PendingOperationInfo,
|
||||
forceNow = false,
|
||||
): Promise<void> {
|
||||
console.log("running pending", pending);
|
||||
logger.trace(`running pending ${JSON.stringify(pending, undefined, 2)}`);
|
||||
switch (pending.type) {
|
||||
case PendingOperationType.Bug:
|
||||
// Nothing to do, will just be displayed to the user
|
||||
@ -552,15 +552,6 @@ export class Wallet {
|
||||
return await this.db.get(Stores.exchanges, exchangeBaseUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive the full event history for this wallet.
|
||||
*/
|
||||
async getHistory(
|
||||
historyQuery?: any[],
|
||||
): Promise<{ history: any[] }> {
|
||||
return { history: [] };
|
||||
}
|
||||
|
||||
async getPendingOperations({ onlyDue = false } = {}): Promise<
|
||||
PendingOperationsResponse
|
||||
> {
|
||||
|
295
src/walletCoreApiHandler.ts
Normal file
295
src/walletCoreApiHandler.ts
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2020 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { Wallet } from "./wallet";
|
||||
import {
|
||||
OperationFailedError,
|
||||
OperationFailedAndReportedError,
|
||||
makeErrorDetails,
|
||||
} from "./operations/errors";
|
||||
import { TalerErrorCode } from "./TalerErrorCode";
|
||||
import { withdrawTestBalance } from "./headless/helpers";
|
||||
import { codecForTransactionsRequest } from "./types/transactions";
|
||||
import {
|
||||
makeCodecForObject,
|
||||
codecForString,
|
||||
Codec,
|
||||
makeCodecOptional,
|
||||
} from "./util/codec";
|
||||
import { Amounts } from "./util/amounts";
|
||||
|
||||
interface AddExchangeRequest {
|
||||
exchangeBaseUrl: string;
|
||||
}
|
||||
|
||||
const codecForAddExchangeRequest = (): Codec<AddExchangeRequest> =>
|
||||
makeCodecForObject<AddExchangeRequest>()
|
||||
.property("exchangeBaseUrl", codecForString)
|
||||
.build("AddExchangeRequest");
|
||||
|
||||
interface GetExchangeTosRequest {
|
||||
exchangeBaseUrl: string;
|
||||
}
|
||||
|
||||
const codecForGetExchangeTosRequest = (): Codec<GetExchangeTosRequest> =>
|
||||
makeCodecForObject<GetExchangeTosRequest>()
|
||||
.property("exchangeBaseUrl", codecForString)
|
||||
.build("GetExchangeTosRequest");
|
||||
|
||||
interface AcceptManualWithdrawalRequest {
|
||||
exchangeBaseUrl: string;
|
||||
amount: string;
|
||||
}
|
||||
|
||||
const codecForAcceptManualWithdrawalRequet = (): Codec<
|
||||
AcceptManualWithdrawalRequest
|
||||
> =>
|
||||
makeCodecForObject<AcceptManualWithdrawalRequest>()
|
||||
.property("exchangeBaseUrl", codecForString)
|
||||
.property("amount", codecForString)
|
||||
.build("AcceptManualWithdrawalRequest");
|
||||
|
||||
interface GetWithdrawalDetailsForAmountRequest {
|
||||
exchangeBaseUrl: string;
|
||||
amount: string;
|
||||
}
|
||||
|
||||
interface AcceptBankIntegratedWithdrawalRequest {
|
||||
talerWithdrawUri: string;
|
||||
exchangeBaseUrl: string;
|
||||
}
|
||||
|
||||
const codecForAcceptBankIntegratedWithdrawalRequest = (): Codec<
|
||||
AcceptBankIntegratedWithdrawalRequest
|
||||
> =>
|
||||
makeCodecForObject<AcceptBankIntegratedWithdrawalRequest>()
|
||||
.property("exchangeBaseUrl", codecForString)
|
||||
.property("talerWithdrawUri", codecForString)
|
||||
.build("AcceptBankIntegratedWithdrawalRequest");
|
||||
|
||||
const codecForGetWithdrawalDetailsForAmountRequest = (): Codec<
|
||||
GetWithdrawalDetailsForAmountRequest
|
||||
> =>
|
||||
makeCodecForObject<GetWithdrawalDetailsForAmountRequest>()
|
||||
.property("exchangeBaseUrl", codecForString)
|
||||
.property("amount", codecForString)
|
||||
.build("GetWithdrawalDetailsForAmountRequest");
|
||||
|
||||
interface AcceptExchangeTosRequest {
|
||||
exchangeBaseUrl: string;
|
||||
etag: string;
|
||||
}
|
||||
|
||||
const codecForAcceptExchangeTosRequest = (): Codec<AcceptExchangeTosRequest> =>
|
||||
makeCodecForObject<AcceptExchangeTosRequest>()
|
||||
.property("exchangeBaseUrl", codecForString)
|
||||
.property("etag", codecForString)
|
||||
.build("AcceptExchangeTosRequest");
|
||||
|
||||
interface ApplyRefundRequest {
|
||||
talerRefundUri: string;
|
||||
}
|
||||
|
||||
const codecForApplyRefundRequest = (): Codec<ApplyRefundRequest> =>
|
||||
makeCodecForObject<ApplyRefundRequest>()
|
||||
.property("talerRefundUri", codecForString)
|
||||
.build("ApplyRefundRequest");
|
||||
|
||||
interface GetWithdrawUriInfoRequest {
|
||||
talerWithdrawUri: string;
|
||||
}
|
||||
|
||||
const codecForGetWithdrawUriInfoRequest = (): Codec<
|
||||
GetWithdrawUriInfoRequest
|
||||
> =>
|
||||
makeCodecForObject<GetWithdrawUriInfoRequest>()
|
||||
.property("talerWithdrawUri", codecForString)
|
||||
.build("GetWithdrawUriInfoRequest");
|
||||
|
||||
interface AbortProposalRequest {
|
||||
proposalId: string;
|
||||
}
|
||||
|
||||
const codecForAbortProposalRequest = (): Codec<AbortProposalRequest> =>
|
||||
makeCodecForObject<AbortProposalRequest>()
|
||||
.property("proposalId", codecForString)
|
||||
.build("AbortProposalRequest");
|
||||
|
||||
interface PreparePayRequest {
|
||||
talerPayUri: string;
|
||||
}
|
||||
|
||||
const codecForPreparePayRequest = (): Codec<PreparePayRequest> =>
|
||||
makeCodecForObject<PreparePayRequest>()
|
||||
.property("talerPayUri", codecForString)
|
||||
.build("PreparePay");
|
||||
|
||||
interface ConfirmPayRequest {
|
||||
proposalId: string;
|
||||
sessionId?: string;
|
||||
}
|
||||
|
||||
const codecForConfirmPayRequest = (): Codec<ConfirmPayRequest> =>
|
||||
makeCodecForObject<ConfirmPayRequest>()
|
||||
.property("proposalId", codecForString)
|
||||
.property("sessionId", makeCodecOptional(codecForString))
|
||||
.build("ConfirmPay");
|
||||
|
||||
/**
|
||||
* Implementation of the "wallet-core" API.
|
||||
*/
|
||||
|
||||
async function dispatchRequestInternal(
|
||||
wallet: Wallet,
|
||||
operation: string,
|
||||
payload: unknown,
|
||||
): Promise<unknown> {
|
||||
switch (operation) {
|
||||
case "withdrawTestkudos":
|
||||
return await withdrawTestBalance(wallet);
|
||||
case "getTransactions": {
|
||||
const req = codecForTransactionsRequest().decode(payload);
|
||||
return await wallet.getTransactions(req);
|
||||
}
|
||||
case "addExchange": {
|
||||
const req = codecForAddExchangeRequest().decode(payload);
|
||||
await wallet.updateExchangeFromUrl(req.exchangeBaseUrl);
|
||||
return {};
|
||||
}
|
||||
case "listExchanges": {
|
||||
return await wallet.getExchanges();
|
||||
}
|
||||
case "getWithdrawUriInfo": {
|
||||
const req = codecForGetWithdrawUriInfoRequest().decode(payload);
|
||||
// FIXME: implement "natively"
|
||||
throw Error("not implemented");
|
||||
}
|
||||
case "acceptManualWithdrawal": {
|
||||
const req = codecForAcceptManualWithdrawalRequet().decode(payload);
|
||||
const res = await wallet.acceptManualWithdrawal(
|
||||
req.exchangeBaseUrl,
|
||||
Amounts.parseOrThrow(req.amount),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
case "getWithdrawalDetailsForAmount": {
|
||||
const req = codecForGetWithdrawalDetailsForAmountRequest().decode(
|
||||
payload,
|
||||
);
|
||||
return await wallet.getWithdrawalDetailsForAmount(
|
||||
req.exchangeBaseUrl,
|
||||
Amounts.parseOrThrow(req.amount),
|
||||
);
|
||||
}
|
||||
case "getBalances": {
|
||||
return await wallet.getBalances();
|
||||
}
|
||||
case "getPendingOperations": {
|
||||
return await wallet.getPendingOperations();
|
||||
}
|
||||
case "acceptExchangeTermsOfService": {
|
||||
const req = codecForAcceptExchangeTosRequest().decode(payload);
|
||||
return await wallet.acceptExchangeTermsOfService(
|
||||
req.exchangeBaseUrl,
|
||||
req.etag,
|
||||
);
|
||||
}
|
||||
case "applyRefund": {
|
||||
const req = codecForApplyRefundRequest().decode(payload);
|
||||
return await wallet.applyRefund(req.talerRefundUri);
|
||||
}
|
||||
case "acceptBankIntegratedWithdrawal": {
|
||||
const req = codecForAcceptBankIntegratedWithdrawalRequest().decode(
|
||||
payload,
|
||||
);
|
||||
return await wallet.acceptWithdrawal(
|
||||
req.talerWithdrawUri,
|
||||
req.exchangeBaseUrl,
|
||||
);
|
||||
}
|
||||
case "getExchangeTos": {
|
||||
const req = codecForGetExchangeTosRequest().decode(payload);
|
||||
return wallet.getExchangeTos(req.exchangeBaseUrl);
|
||||
}
|
||||
case "abortProposal": {
|
||||
const req = codecForAbortProposalRequest().decode(payload);
|
||||
return await wallet.refuseProposal(req.proposalId);
|
||||
}
|
||||
case "retryPendingNow": {
|
||||
await wallet.runPending(true);
|
||||
return {};
|
||||
}
|
||||
case "preparePay": {
|
||||
const req = codecForPreparePayRequest().decode(payload);
|
||||
return await wallet.preparePayForUri(req.talerPayUri);
|
||||
}
|
||||
case "confirmPay": {
|
||||
const req = codecForConfirmPayRequest().decode(payload);
|
||||
return await wallet.confirmPay(req.proposalId, req.sessionId);
|
||||
}
|
||||
}
|
||||
throw OperationFailedError.fromCode(
|
||||
TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN,
|
||||
"unknown operation",
|
||||
{
|
||||
operation,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request to the wallet-core API.
|
||||
*/
|
||||
export async function handleCoreApiRequest(
|
||||
w: Wallet,
|
||||
operation: string,
|
||||
id: string,
|
||||
payload: unknown,
|
||||
): Promise<unknown> {
|
||||
try {
|
||||
const result = await dispatchRequestInternal(w, operation, payload);
|
||||
const respMsg = {
|
||||
isError: false,
|
||||
operation,
|
||||
id,
|
||||
result,
|
||||
};
|
||||
return respMsg;
|
||||
} catch (e) {
|
||||
if (
|
||||
e instanceof OperationFailedError ||
|
||||
e instanceof OperationFailedAndReportedError
|
||||
) {
|
||||
return {
|
||||
isError: true,
|
||||
operation,
|
||||
id,
|
||||
error: e.operationError,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
isError: true,
|
||||
operation,
|
||||
id,
|
||||
error: makeErrorDetails(
|
||||
TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
|
||||
`unexpected exception: ${e}`,
|
||||
{},
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user