do withdrawal with fewer DB accesses
This commit is contained in:
parent
dd66e43b3c
commit
9f6e398884
@ -158,7 +158,7 @@ export interface ReserveRecord {
|
|||||||
*
|
*
|
||||||
* Only applies if bankWithdrawStatusUrl is defined.
|
* Only applies if bankWithdrawStatusUrl is defined.
|
||||||
*
|
*
|
||||||
* Set to 0 if that hasn't happened yet.
|
* Set to undefined if that hasn't happened yet.
|
||||||
*/
|
*/
|
||||||
timestampReserveInfoPosted: Timestamp | undefined;
|
timestampReserveInfoPosted: Timestamp | undefined;
|
||||||
|
|
||||||
@ -210,7 +210,7 @@ export interface ReserveRecord {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Is there any work to be done for this reserve?
|
* Is there any work to be done for this reserve?
|
||||||
*
|
*
|
||||||
* FIXME: Technically redundant, since the reserveStatus would indicate this.
|
* FIXME: Technically redundant, since the reserveStatus would indicate this.
|
||||||
*/
|
*/
|
||||||
operationStatus: OperationStatus;
|
operationStatus: OperationStatus;
|
||||||
@ -1341,6 +1341,9 @@ export interface DenomSelectionState {
|
|||||||
* the coin selection we want to withdraw.
|
* the coin selection we want to withdraw.
|
||||||
*/
|
*/
|
||||||
export interface WithdrawalGroupRecord {
|
export interface WithdrawalGroupRecord {
|
||||||
|
/**
|
||||||
|
* Unique identifier for the withdrawal group.
|
||||||
|
*/
|
||||||
withdrawalGroupId: string;
|
withdrawalGroupId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1348,8 +1351,15 @@ export interface WithdrawalGroupRecord {
|
|||||||
*/
|
*/
|
||||||
secretSeed: string;
|
secretSeed: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public key of the reserve that we're withdrawing from.
|
||||||
|
*/
|
||||||
reservePub: string;
|
reservePub: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The exchange base URL that we're withdrawing from.
|
||||||
|
* (Redundantly stored, as the reserve record also has this info.)
|
||||||
|
*/
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1363,6 +1373,10 @@ export interface WithdrawalGroupRecord {
|
|||||||
*/
|
*/
|
||||||
timestampFinish?: Timestamp;
|
timestampFinish?: Timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operation status of the withdrawal group.
|
||||||
|
* Used for indexing in the database.
|
||||||
|
*/
|
||||||
operationStatus: OperationStatus;
|
operationStatus: OperationStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1371,8 +1385,18 @@ export interface WithdrawalGroupRecord {
|
|||||||
*/
|
*/
|
||||||
rawWithdrawalAmount: AmountJson;
|
rawWithdrawalAmount: AmountJson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denominations selected for withdrawal.
|
||||||
|
*/
|
||||||
denomsSel: DenomSelectionState;
|
denomsSel: DenomSelectionState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UID of the denomination selection.
|
||||||
|
*
|
||||||
|
* Used for merging backups.
|
||||||
|
*
|
||||||
|
* FIXME: Should this not also include a timestamp for more logical merging?
|
||||||
|
*/
|
||||||
denomSelUid: string;
|
denomSelUid: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,13 +37,9 @@ import {
|
|||||||
} from "../pending-types.js";
|
} from "../pending-types.js";
|
||||||
import {
|
import {
|
||||||
getTimestampNow,
|
getTimestampNow,
|
||||||
isTimestampExpired,
|
|
||||||
j2s,
|
|
||||||
Logger,
|
|
||||||
Timestamp,
|
Timestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../common.js";
|
import { InternalWalletState } from "../common.js";
|
||||||
import { getBalancesInsideTransaction } from "./balance.js";
|
|
||||||
import { GetReadOnlyAccess } from "../util/query.js";
|
import { GetReadOnlyAccess } from "../util/query.js";
|
||||||
|
|
||||||
async function gatherExchangePending(
|
async function gatherExchangePending(
|
||||||
@ -353,9 +349,7 @@ export async function getPendingOperations(
|
|||||||
recoupGroups: x.recoupGroups,
|
recoupGroups: x.recoupGroups,
|
||||||
}))
|
}))
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const walletBalance = await getBalancesInsideTransaction(ws, tx);
|
|
||||||
const resp: PendingOperationsResponse = {
|
const resp: PendingOperationsResponse = {
|
||||||
walletBalance,
|
|
||||||
pendingOperations: [],
|
pendingOperations: [],
|
||||||
};
|
};
|
||||||
await gatherExchangePending(tx, now, resp);
|
await gatherExchangePending(tx, now, resp);
|
||||||
|
@ -55,6 +55,7 @@ import {
|
|||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
OperationStatus,
|
OperationStatus,
|
||||||
PlanchetRecord,
|
PlanchetRecord,
|
||||||
|
WithdrawalGroupRecord,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { walletCoreDebugFlags } from "../util/debugFlags.js";
|
import { walletCoreDebugFlags } from "../util/debugFlags.js";
|
||||||
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
|
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
|
||||||
@ -73,7 +74,7 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Logger for this file.
|
* Logger for this file.
|
||||||
*/
|
*/
|
||||||
const logger = new Logger("withdraw.ts");
|
const logger = new Logger("operations/withdraw.ts");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIXME: Eliminate this in favor of DenomSelectionState.
|
* FIXME: Eliminate this in favor of DenomSelectionState.
|
||||||
@ -351,106 +352,95 @@ export async function getCandidateWithdrawalDenoms(
|
|||||||
*/
|
*/
|
||||||
async function processPlanchetGenerate(
|
async function processPlanchetGenerate(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
withdrawalGroupId: string,
|
withdrawalGroup: WithdrawalGroupRecord,
|
||||||
coinIdx: number,
|
coinIdx: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const withdrawalGroup = await ws.db
|
|
||||||
.mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
|
|
||||||
.runReadOnly(async (tx) => {
|
|
||||||
return await tx.withdrawalGroups.get(withdrawalGroupId);
|
|
||||||
});
|
|
||||||
if (!withdrawalGroup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let planchet = await ws.db
|
let planchet = await ws.db
|
||||||
.mktx((x) => ({
|
.mktx((x) => ({
|
||||||
planchets: x.planchets,
|
planchets: x.planchets,
|
||||||
}))
|
}))
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
return tx.planchets.indexes.byGroupAndIndex.get([
|
return tx.planchets.indexes.byGroupAndIndex.get([
|
||||||
withdrawalGroupId,
|
withdrawalGroup.withdrawalGroupId,
|
||||||
coinIdx,
|
coinIdx,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
if (!planchet) {
|
if (planchet) {
|
||||||
let ci = 0;
|
return;
|
||||||
let denomPubHash: string | undefined;
|
|
||||||
for (
|
|
||||||
let di = 0;
|
|
||||||
di < withdrawalGroup.denomsSel.selectedDenoms.length;
|
|
||||||
di++
|
|
||||||
) {
|
|
||||||
const d = withdrawalGroup.denomsSel.selectedDenoms[di];
|
|
||||||
if (coinIdx >= ci && coinIdx < ci + d.count) {
|
|
||||||
denomPubHash = d.denomPubHash;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ci += d.count;
|
|
||||||
}
|
|
||||||
if (!denomPubHash) {
|
|
||||||
throw Error("invariant violated");
|
|
||||||
}
|
|
||||||
|
|
||||||
const { denom, reserve } = await ws.db
|
|
||||||
.mktx((x) => ({
|
|
||||||
reserves: x.reserves,
|
|
||||||
denominations: x.denominations,
|
|
||||||
}))
|
|
||||||
.runReadOnly(async (tx) => {
|
|
||||||
const denom = await tx.denominations.get([
|
|
||||||
withdrawalGroup.exchangeBaseUrl,
|
|
||||||
denomPubHash!,
|
|
||||||
]);
|
|
||||||
if (!denom) {
|
|
||||||
throw Error("invariant violated");
|
|
||||||
}
|
|
||||||
const reserve = await tx.reserves.get(withdrawalGroup.reservePub);
|
|
||||||
if (!reserve) {
|
|
||||||
throw Error("invariant violated");
|
|
||||||
}
|
|
||||||
return { denom, reserve };
|
|
||||||
});
|
|
||||||
const r = await ws.cryptoApi.createPlanchet({
|
|
||||||
denomPub: denom.denomPub,
|
|
||||||
feeWithdraw: denom.feeWithdraw,
|
|
||||||
reservePriv: reserve.reservePriv,
|
|
||||||
reservePub: reserve.reservePub,
|
|
||||||
value: denom.value,
|
|
||||||
coinIndex: coinIdx,
|
|
||||||
secretSeed: withdrawalGroup.secretSeed,
|
|
||||||
});
|
|
||||||
const newPlanchet: PlanchetRecord = {
|
|
||||||
blindingKey: r.blindingKey,
|
|
||||||
coinEv: r.coinEv,
|
|
||||||
coinEvHash: r.coinEvHash,
|
|
||||||
coinIdx,
|
|
||||||
coinPriv: r.coinPriv,
|
|
||||||
coinPub: r.coinPub,
|
|
||||||
coinValue: r.coinValue,
|
|
||||||
denomPub: r.denomPub,
|
|
||||||
denomPubHash: r.denomPubHash,
|
|
||||||
isFromTip: false,
|
|
||||||
reservePub: r.reservePub,
|
|
||||||
withdrawalDone: false,
|
|
||||||
withdrawSig: r.withdrawSig,
|
|
||||||
withdrawalGroupId: withdrawalGroupId,
|
|
||||||
lastError: undefined,
|
|
||||||
};
|
|
||||||
await ws.db
|
|
||||||
.mktx((x) => ({ planchets: x.planchets }))
|
|
||||||
.runReadWrite(async (tx) => {
|
|
||||||
const p = await tx.planchets.indexes.byGroupAndIndex.get([
|
|
||||||
withdrawalGroupId,
|
|
||||||
coinIdx,
|
|
||||||
]);
|
|
||||||
if (p) {
|
|
||||||
planchet = p;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await tx.planchets.put(newPlanchet);
|
|
||||||
planchet = newPlanchet;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
let ci = 0;
|
||||||
|
let denomPubHash: string | undefined;
|
||||||
|
for (let di = 0; di < withdrawalGroup.denomsSel.selectedDenoms.length; di++) {
|
||||||
|
const d = withdrawalGroup.denomsSel.selectedDenoms[di];
|
||||||
|
if (coinIdx >= ci && coinIdx < ci + d.count) {
|
||||||
|
denomPubHash = d.denomPubHash;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ci += d.count;
|
||||||
|
}
|
||||||
|
if (!denomPubHash) {
|
||||||
|
throw Error("invariant violated");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { denom, reserve } = await ws.db
|
||||||
|
.mktx((x) => ({
|
||||||
|
reserves: x.reserves,
|
||||||
|
denominations: x.denominations,
|
||||||
|
}))
|
||||||
|
.runReadOnly(async (tx) => {
|
||||||
|
const denom = await tx.denominations.get([
|
||||||
|
withdrawalGroup.exchangeBaseUrl,
|
||||||
|
denomPubHash!,
|
||||||
|
]);
|
||||||
|
if (!denom) {
|
||||||
|
throw Error("invariant violated");
|
||||||
|
}
|
||||||
|
const reserve = await tx.reserves.get(withdrawalGroup.reservePub);
|
||||||
|
if (!reserve) {
|
||||||
|
throw Error("invariant violated");
|
||||||
|
}
|
||||||
|
return { denom, reserve };
|
||||||
|
});
|
||||||
|
const r = await ws.cryptoApi.createPlanchet({
|
||||||
|
denomPub: denom.denomPub,
|
||||||
|
feeWithdraw: denom.feeWithdraw,
|
||||||
|
reservePriv: reserve.reservePriv,
|
||||||
|
reservePub: reserve.reservePub,
|
||||||
|
value: denom.value,
|
||||||
|
coinIndex: coinIdx,
|
||||||
|
secretSeed: withdrawalGroup.secretSeed,
|
||||||
|
});
|
||||||
|
const newPlanchet: PlanchetRecord = {
|
||||||
|
blindingKey: r.blindingKey,
|
||||||
|
coinEv: r.coinEv,
|
||||||
|
coinEvHash: r.coinEvHash,
|
||||||
|
coinIdx,
|
||||||
|
coinPriv: r.coinPriv,
|
||||||
|
coinPub: r.coinPub,
|
||||||
|
coinValue: r.coinValue,
|
||||||
|
denomPub: r.denomPub,
|
||||||
|
denomPubHash: r.denomPubHash,
|
||||||
|
isFromTip: false,
|
||||||
|
reservePub: r.reservePub,
|
||||||
|
withdrawalDone: false,
|
||||||
|
withdrawSig: r.withdrawSig,
|
||||||
|
withdrawalGroupId: withdrawalGroup.withdrawalGroupId,
|
||||||
|
lastError: undefined,
|
||||||
|
};
|
||||||
|
await ws.db
|
||||||
|
.mktx((x) => ({ planchets: x.planchets }))
|
||||||
|
.runReadWrite(async (tx) => {
|
||||||
|
const p = await tx.planchets.indexes.byGroupAndIndex.get([
|
||||||
|
withdrawalGroup.withdrawalGroupId,
|
||||||
|
coinIdx,
|
||||||
|
]);
|
||||||
|
if (p) {
|
||||||
|
planchet = p;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await tx.planchets.put(newPlanchet);
|
||||||
|
planchet = newPlanchet;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -460,7 +450,7 @@ async function processPlanchetGenerate(
|
|||||||
*/
|
*/
|
||||||
async function processPlanchetExchangeRequest(
|
async function processPlanchetExchangeRequest(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
withdrawalGroupId: string,
|
withdrawalGroup: WithdrawalGroupRecord,
|
||||||
coinIdx: number,
|
coinIdx: number,
|
||||||
): Promise<WithdrawResponse | undefined> {
|
): Promise<WithdrawResponse | undefined> {
|
||||||
const d = await ws.db
|
const d = await ws.db
|
||||||
@ -471,12 +461,8 @@ async function processPlanchetExchangeRequest(
|
|||||||
denominations: x.denominations,
|
denominations: x.denominations,
|
||||||
}))
|
}))
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
const withdrawalGroup = await tx.withdrawalGroups.get(withdrawalGroupId);
|
|
||||||
if (!withdrawalGroup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
||||||
withdrawalGroupId,
|
withdrawalGroup.withdrawalGroupId,
|
||||||
coinIdx,
|
coinIdx,
|
||||||
]);
|
]);
|
||||||
if (!planchet) {
|
if (!planchet) {
|
||||||
@ -503,7 +489,7 @@ async function processPlanchetExchangeRequest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.trace(
|
logger.trace(
|
||||||
`processing planchet #${coinIdx} in withdrawal ${withdrawalGroupId}`,
|
`processing planchet #${coinIdx} in withdrawal ${withdrawalGroup.withdrawalGroupId}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const reqBody: any = {
|
const reqBody: any = {
|
||||||
@ -543,7 +529,7 @@ async function processPlanchetExchangeRequest(
|
|||||||
.mktx((x) => ({ planchets: x.planchets }))
|
.mktx((x) => ({ planchets: x.planchets }))
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
||||||
withdrawalGroupId,
|
withdrawalGroup.withdrawalGroupId,
|
||||||
coinIdx,
|
coinIdx,
|
||||||
]);
|
]);
|
||||||
if (!planchet) {
|
if (!planchet) {
|
||||||
@ -558,7 +544,7 @@ async function processPlanchetExchangeRequest(
|
|||||||
|
|
||||||
async function processPlanchetVerifyAndStoreCoin(
|
async function processPlanchetVerifyAndStoreCoin(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
withdrawalGroupId: string,
|
withdrawalGroup: WithdrawalGroupRecord,
|
||||||
coinIdx: number,
|
coinIdx: number,
|
||||||
resp: WithdrawResponse,
|
resp: WithdrawResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -568,12 +554,8 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
planchets: x.planchets,
|
planchets: x.planchets,
|
||||||
}))
|
}))
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
const withdrawalGroup = await tx.withdrawalGroups.get(withdrawalGroupId);
|
|
||||||
if (!withdrawalGroup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
||||||
withdrawalGroupId,
|
withdrawalGroup.withdrawalGroupId,
|
||||||
coinIdx,
|
coinIdx,
|
||||||
]);
|
]);
|
||||||
if (!planchet) {
|
if (!planchet) {
|
||||||
@ -635,7 +617,7 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
.mktx((x) => ({ planchets: x.planchets }))
|
.mktx((x) => ({ planchets: x.planchets }))
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
|
||||||
withdrawalGroupId,
|
withdrawalGroup.withdrawalGroupId,
|
||||||
coinIdx,
|
coinIdx,
|
||||||
]);
|
]);
|
||||||
if (!planchet) {
|
if (!planchet) {
|
||||||
@ -679,7 +661,7 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
type: CoinSourceType.Withdraw,
|
type: CoinSourceType.Withdraw,
|
||||||
coinIndex: coinIdx,
|
coinIndex: coinIdx,
|
||||||
reservePub: planchet.reservePub,
|
reservePub: planchet.reservePub,
|
||||||
withdrawalGroupId: withdrawalGroupId,
|
withdrawalGroupId: withdrawalGroup.withdrawalGroupId,
|
||||||
},
|
},
|
||||||
suspended: false,
|
suspended: false,
|
||||||
};
|
};
|
||||||
@ -694,10 +676,6 @@ async function processPlanchetVerifyAndStoreCoin(
|
|||||||
planchets: x.planchets,
|
planchets: x.planchets,
|
||||||
}))
|
}))
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const ws = await tx.withdrawalGroups.get(withdrawalGroupId);
|
|
||||||
if (!ws) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const p = await tx.planchets.get(planchetCoinPub);
|
const p = await tx.planchets.get(planchetCoinPub);
|
||||||
if (!p || p.withdrawalDone) {
|
if (!p || p.withdrawalDone) {
|
||||||
return false;
|
return false;
|
||||||
@ -914,7 +892,7 @@ async function processWithdrawGroupImpl(
|
|||||||
let work: Promise<void>[] = [];
|
let work: Promise<void>[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < numTotalCoins; i++) {
|
for (let i = 0; i < numTotalCoins; i++) {
|
||||||
work.push(processPlanchetGenerate(ws, withdrawalGroupId, i));
|
work.push(processPlanchetGenerate(ws, withdrawalGroup, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate coins concurrently (parallelism only happens in the crypto API workers)
|
// Generate coins concurrently (parallelism only happens in the crypto API workers)
|
||||||
@ -925,14 +903,14 @@ async function processWithdrawGroupImpl(
|
|||||||
for (let coinIdx = 0; coinIdx < numTotalCoins; coinIdx++) {
|
for (let coinIdx = 0; coinIdx < numTotalCoins; coinIdx++) {
|
||||||
const resp = await processPlanchetExchangeRequest(
|
const resp = await processPlanchetExchangeRequest(
|
||||||
ws,
|
ws,
|
||||||
withdrawalGroupId,
|
withdrawalGroup,
|
||||||
coinIdx,
|
coinIdx,
|
||||||
);
|
);
|
||||||
if (!resp) {
|
if (!resp) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
work.push(
|
work.push(
|
||||||
processPlanchetVerifyAndStoreCoin(ws, withdrawalGroupId, coinIdx, resp),
|
processPlanchetVerifyAndStoreCoin(ws, withdrawalGroup, coinIdx, resp),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1089,6 +1067,13 @@ export async function getExchangeWithdrawalInfo(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get more information about a taler://withdraw URI.
|
||||||
|
*
|
||||||
|
* As side effects, the bank (via the bank integration API) is queried
|
||||||
|
* and the exchange suggested by the bank is permanently added
|
||||||
|
* to the wallet's list of known exchanges.
|
||||||
|
*/
|
||||||
export async function getWithdrawalDetailsForUri(
|
export async function getWithdrawalDetailsForUri(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
talerWithdrawUri: string,
|
talerWithdrawUri: string,
|
||||||
@ -1110,6 +1095,9 @@ export async function getWithdrawalDetailsForUri(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract information about possible exchanges for the withdrawal
|
||||||
|
// operation from the database.
|
||||||
|
|
||||||
const exchanges: ExchangeListItem[] = [];
|
const exchanges: ExchangeListItem[] = [];
|
||||||
|
|
||||||
await ws.db
|
await ws.db
|
||||||
|
@ -248,9 +248,4 @@ export interface PendingOperationsResponse {
|
|||||||
* List of pending operations.
|
* List of pending operations.
|
||||||
*/
|
*/
|
||||||
pendingOperations: PendingTaskInfo[];
|
pendingOperations: PendingTaskInfo[];
|
||||||
|
|
||||||
/**
|
|
||||||
* Current wallet balance, including pending balances.
|
|
||||||
*/
|
|
||||||
walletBalance: BalancesResponse;
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user