finish refresh correctly, display fees correctly

This commit is contained in:
Florian Dold 2019-12-16 21:10:57 +01:00
parent c2ee8fd9ab
commit fb6508de9d
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
8 changed files with 190 additions and 107 deletions

View File

@ -1,17 +1,17 @@
/* /*
This file is part of TALER This file is part of GNU Taler
(C) 2019 GNUnet e.V. (C) 2019 Taler Systems S.A.
TALER is free software; you can redistribute it and/or modify it under the 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 terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version. Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY 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 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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 You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import os = require("os"); import os = require("os");
@ -167,7 +167,10 @@ walletCli
}); });
walletCli walletCli
.subcommand("", "history", { help: "Show wallet event history." }) .subcommand("history", "history", { help: "Show wallet event history." })
.flag("json", ["--json"], {
default: false,
})
.maybeOption("from", ["--from"], clk.STRING) .maybeOption("from", ["--from"], clk.STRING)
.maybeOption("to", ["--to"], clk.STRING) .maybeOption("to", ["--to"], clk.STRING)
.maybeOption("limit", ["--limit"], clk.STRING) .maybeOption("limit", ["--limit"], clk.STRING)
@ -175,7 +178,17 @@ walletCli
.action(async args => { .action(async args => {
await withWallet(args, async wallet => { await withWallet(args, async wallet => {
const history = await wallet.getHistory(); const history = await wallet.getHistory();
console.log(JSON.stringify(history, undefined, 2)); if (args.history.json) {
console.log(JSON.stringify(history, undefined, 2));
} else {
for (const h of history.history) {
console.log(
`event at ${new Date(h.timestamp.t_ms).toISOString()} with type ${h.type}:`,
);
console.log(JSON.stringify(h, undefined, 2));
console.log();
}
}
}); });
}); });
@ -231,7 +244,8 @@ walletCli
case TalerUriType.TalerWithdraw: case TalerUriType.TalerWithdraw:
{ {
const withdrawInfo = await wallet.getWithdrawDetailsForUri(uri); const withdrawInfo = await wallet.getWithdrawDetailsForUri(uri);
const selectedExchange = withdrawInfo.bankWithdrawDetails.suggestedExchange; const selectedExchange =
withdrawInfo.bankWithdrawDetails.suggestedExchange;
if (!selectedExchange) { if (!selectedExchange) {
console.error("no suggested exchange!"); console.error("no suggested exchange!");
process.exit(1); process.exit(1);

View File

@ -61,7 +61,6 @@ function getOrderShortInfo(
}; };
} }
async function collectProposalHistory( async function collectProposalHistory(
tx: TransactionHandle, tx: TransactionHandle,
history: HistoryEvent[], history: HistoryEvent[],
@ -162,6 +161,7 @@ export async function getHistory(
await ws.db.runWithReadTransaction( await ws.db.runWithReadTransaction(
[ [
Stores.currencies, Stores.currencies,
Stores.coins,
Stores.exchanges, Stores.exchanges,
Stores.exchangeUpdatedEvents, Stores.exchangeUpdatedEvents,
Stores.proposals, Stores.proposals,
@ -220,15 +220,22 @@ export async function getHistory(
await collectProposalHistory(tx, history, historyQuery); await collectProposalHistory(tx, history, historyQuery);
await tx.iter(Stores.payEvents).forEachAsync(async (pe) => { await tx.iter(Stores.payEvents).forEachAsync(async pe => {
const proposal = await tx.get(Stores.proposals, pe.proposalId); const proposal = await tx.get(Stores.proposals, pe.proposalId);
if (!proposal) { if (!proposal) {
return; return;
} }
const purchase = await tx.get(Stores.purchases, pe.proposalId);
if (!purchase) {
return;
}
const orderShortInfo = getOrderShortInfo(proposal); const orderShortInfo = getOrderShortInfo(proposal);
if (!orderShortInfo) { if (!orderShortInfo) {
return; return;
} }
const amountPaidWithFees = Amounts.sum(
purchase.payReq.coins.map(x => Amounts.parseOrThrow(x.contribution)),
).amount;
history.push({ history.push({
type: HistoryEventType.PaymentSent, type: HistoryEventType.PaymentSent,
eventId: makeEventId(HistoryEventType.PaymentSent, pe.proposalId), eventId: makeEventId(HistoryEventType.PaymentSent, pe.proposalId),
@ -236,10 +243,12 @@ export async function getHistory(
replay: pe.isReplay, replay: pe.isReplay,
sessionId: pe.sessionId, sessionId: pe.sessionId,
timestamp: pe.timestamp, timestamp: pe.timestamp,
numCoins: purchase.payReq.coins.length,
amountPaidWithFees: Amounts.toString(amountPaidWithFees),
}); });
}); });
await tx.iter(Stores.refreshGroups).forEachAsync(async (rg) => { await tx.iter(Stores.refreshGroups).forEachAsync(async rg => {
if (!rg.timestampFinished) { if (!rg.timestampFinished) {
return; return;
} }
@ -251,23 +260,26 @@ export async function getHistory(
for (let i = 0; i < rg.refreshSessionPerCoin.length; i++) { for (let i = 0; i < rg.refreshSessionPerCoin.length; i++) {
const session = rg.refreshSessionPerCoin[i]; const session = rg.refreshSessionPerCoin[i];
numInputCoins++; numInputCoins++;
const c = await tx.get(Stores.coins, rg.oldCoinPubs[i]);
if (!c) {
continue;
}
if (session) { if (session) {
numRefreshedInputCoins++; numRefreshedInputCoins++;
amountsRaw.push(session.amountRefreshInput); amountsRaw.push(session.amountRefreshInput);
amountsRaw.push(c.currentAmount);
amountsEffective.push(session.amountRefreshOutput); amountsEffective.push(session.amountRefreshOutput);
numOutputCoins += session.newDenoms.length; numOutputCoins += session.newDenoms.length;
} else { } else {
const c = await tx.get(Stores.coins, rg.oldCoinPubs[i]);
if (!c) {
continue;
}
amountsRaw.push(c.currentAmount); amountsRaw.push(c.currentAmount);
} }
} }
let amountRefreshedRaw = Amounts.sum(amountsRaw).amount; let amountRefreshedRaw = Amounts.sum(amountsRaw).amount;
let amountRefreshedEffective: AmountJson; let amountRefreshedEffective: AmountJson;
if (amountsEffective.length == 0) { if (amountsEffective.length == 0) {
amountRefreshedEffective = Amounts.getZero(amountRefreshedRaw.currency); amountRefreshedEffective = Amounts.getZero(
amountRefreshedRaw.currency,
);
} else { } else {
amountRefreshedEffective = Amounts.sum(amountsEffective).amount; amountRefreshedEffective = Amounts.sum(amountsEffective).amount;
} }
@ -285,7 +297,7 @@ export async function getHistory(
}); });
}); });
tx.iter(Stores.reserveUpdatedEvents).forEachAsync(async (ru) => { tx.iter(Stores.reserveUpdatedEvents).forEachAsync(async ru => {
const reserve = await tx.get(Stores.reserves, ru.reservePub); const reserve = await tx.get(Stores.reserves, ru.reservePub);
if (!reserve) { if (!reserve) {
return; return;
@ -295,28 +307,31 @@ export async function getHistory(
reserveCreationDetail = { reserveCreationDetail = {
type: ReserveType.TalerBankWithdraw, type: ReserveType.TalerBankWithdraw,
bankUrl: reserve.bankWithdrawStatusUrl, bankUrl: reserve.bankWithdrawStatusUrl,
} };
} else { } else {
reserveCreationDetail = { reserveCreationDetail = {
type: ReserveType.Manual, type: ReserveType.Manual,
} };
} }
history.push({ history.push({
type: HistoryEventType.ReserveBalanceUpdated, type: HistoryEventType.ReserveBalanceUpdated,
eventId: makeEventId(HistoryEventType.ReserveBalanceUpdated, ru.reserveUpdateId), eventId: makeEventId(
HistoryEventType.ReserveBalanceUpdated,
ru.reserveUpdateId,
),
amountExpected: ru.amountExpected, amountExpected: ru.amountExpected,
amountReserveBalance: ru.amountReserveBalance, amountReserveBalance: ru.amountReserveBalance,
timestamp: reserve.timestampCreated, timestamp: ru.timestamp,
newHistoryTransactions: ru.newHistoryTransactions, newHistoryTransactions: ru.newHistoryTransactions,
reserveShortInfo: { reserveShortInfo: {
exchangeBaseUrl: reserve.exchangeBaseUrl, exchangeBaseUrl: reserve.exchangeBaseUrl,
reserveCreationDetail, reserveCreationDetail,
reservePub: reserve.reservePub, reservePub: reserve.reservePub,
} },
}); });
}); });
tx.iter(Stores.tips).forEach((tip) => { tx.iter(Stores.tips).forEach(tip => {
if (tip.acceptedTimestamp) { if (tip.acceptedTimestamp) {
history.push({ history.push({
type: HistoryEventType.TipAccepted, type: HistoryEventType.TipAccepted,
@ -328,7 +343,7 @@ export async function getHistory(
} }
}); });
tx.iter(Stores.refundEvents).forEachAsync(async (re) => { tx.iter(Stores.refundEvents).forEachAsync(async re => {
const proposal = await tx.get(Stores.proposals, re.proposalId); const proposal = await tx.get(Stores.proposals, re.proposalId);
if (!proposal) { if (!proposal) {
return; return;
@ -341,7 +356,9 @@ export async function getHistory(
if (!orderShortInfo) { if (!orderShortInfo) {
return; return;
} }
const purchaseAmount = Amounts.parseOrThrow(purchase.contractTerms.amount); const purchaseAmount = Amounts.parseOrThrow(
purchase.contractTerms.amount,
);
let amountRefundedRaw = Amounts.getZero(purchaseAmount.currency); let amountRefundedRaw = Amounts.getZero(purchaseAmount.currency);
let amountRefundedInvalid = Amounts.getZero(purchaseAmount.currency); let amountRefundedInvalid = Amounts.getZero(purchaseAmount.currency);
let amountRefundedEffective = Amounts.getZero(purchaseAmount.currency); let amountRefundedEffective = Amounts.getZero(purchaseAmount.currency);
@ -352,9 +369,16 @@ export async function getHistory(
} }
const refundAmount = Amounts.parseOrThrow(r.perm.refund_amount); const refundAmount = Amounts.parseOrThrow(r.perm.refund_amount);
const refundFee = Amounts.parseOrThrow(r.perm.refund_fee); const refundFee = Amounts.parseOrThrow(r.perm.refund_fee);
amountRefundedRaw = Amounts.add(amountRefundedRaw, refundAmount).amount; amountRefundedRaw = Amounts.add(amountRefundedRaw, refundAmount)
amountRefundedEffective = Amounts.add(amountRefundedEffective, refundAmount).amount; .amount;
amountRefundedEffective = Amounts.sub(amountRefundedEffective, refundFee).amount; amountRefundedEffective = Amounts.add(
amountRefundedEffective,
refundAmount,
).amount;
amountRefundedEffective = Amounts.sub(
amountRefundedEffective,
refundFee,
).amount;
}); });
Object.keys(purchase.refundState.refundsFailed).forEach((x, i) => { Object.keys(purchase.refundState.refundsFailed).forEach((x, i) => {
const r = purchase.refundState.refundsFailed[x]; const r = purchase.refundState.refundsFailed[x];
@ -365,7 +389,10 @@ export async function getHistory(
const refundFee = Amounts.parseOrThrow(r.perm.refund_fee); const refundFee = Amounts.parseOrThrow(r.perm.refund_fee);
amountRefundedRaw = Amounts.add(amountRefundedRaw, ra).amount; amountRefundedRaw = Amounts.add(amountRefundedRaw, ra).amount;
amountRefundedInvalid = Amounts.add(amountRefundedInvalid, ra).amount; amountRefundedInvalid = Amounts.add(amountRefundedInvalid, ra).amount;
amountRefundedEffective = Amounts.sub(amountRefundedEffective, refundFee).amount; amountRefundedEffective = Amounts.sub(
amountRefundedEffective,
refundFee,
).amount;
}); });
history.push({ history.push({
type: HistoryEventType.Refund, type: HistoryEventType.Refund,

View File

@ -224,6 +224,8 @@ async function gatherRefreshPending(
type: PendingOperationType.Refresh, type: PendingOperationType.Refresh,
givesLifeness: true, givesLifeness: true,
refreshGroupId: r.refreshGroupId, refreshGroupId: r.refreshGroupId,
finishedPerCoin: r.finishedPerCoin,
retryInfo: r.retryInfo,
}); });
}); });
} }

View File

@ -144,10 +144,22 @@ async function refreshCreateSession(
return; return;
} }
rg.finishedPerCoin[coinIndex] = true; rg.finishedPerCoin[coinIndex] = true;
rg.finishedPerCoin[coinIndex] = true;
let allDone = true;
for (const f of rg.finishedPerCoin) {
if (!f) {
allDone = false;
break;
}
}
if (allDone) {
rg.timestampFinished = getTimestampNow();
rg.retryInfo = initRetryInfo(false);
}
await tx.put(Stores.refreshGroups, rg); await tx.put(Stores.refreshGroups, rg);
}, },
); );
ws.notify({ type: NotificationType.RefreshRefused }); ws.notify({ type: NotificationType.RefreshUnwarranted });
return; return;
} }

View File

@ -34,12 +34,14 @@ import {
updateRetryInfoTimeout, updateRetryInfoTimeout,
ReserveUpdatedEventRecord, ReserveUpdatedEventRecord,
} from "../types/dbTypes"; } from "../types/dbTypes";
import { import { TransactionAbort } from "../util/query";
TransactionAbort,
} from "../util/query";
import { Logger } from "../util/logging"; import { Logger } from "../util/logging";
import * as Amounts from "../util/amounts"; import * as Amounts from "../util/amounts";
import { updateExchangeFromUrl, getExchangeTrust, getExchangePaytoUri } from "./exchanges"; import {
updateExchangeFromUrl,
getExchangeTrust,
getExchangePaytoUri,
} from "./exchanges";
import { WithdrawOperationStatusResponse } from "../types/talerTypes"; import { WithdrawOperationStatusResponse } from "../types/talerTypes";
import { assertUnreachable } from "../util/assertUnreachable"; import { assertUnreachable } from "../util/assertUnreachable";
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto"; import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
@ -49,7 +51,10 @@ import {
processWithdrawSession, processWithdrawSession,
getBankWithdrawalInfo, getBankWithdrawalInfo,
} from "./withdraw"; } from "./withdraw";
import { guardOperationException, OperationFailedAndReportedError } from "./errors"; import {
guardOperationException,
OperationFailedAndReportedError,
} from "./errors";
import { NotificationType } from "../types/notifications"; import { NotificationType } from "../types/notifications";
import { codecForReserveStatus } from "../types/ReserveStatus"; import { codecForReserveStatus } from "../types/ReserveStatus";
@ -206,7 +211,6 @@ export async function processReserve(
}); });
} }
async function registerReserveWithBank( async function registerReserveWithBank(
ws: InternalWalletState, ws: InternalWalletState,
reservePub: string, reservePub: string,
@ -231,7 +235,6 @@ async function registerReserveWithBank(
reserve_pub: reservePub, reserve_pub: reservePub,
selected_exchange: reserve.exchangeWire, selected_exchange: reserve.exchangeWire,
}); });
console.log("got response", bankResp);
await ws.db.mutate(Stores.reserves, reservePub, r => { await ws.db.mutate(Stores.reserves, reservePub, r => {
switch (r.reserveStatus) { switch (r.reserveStatus) {
case ReserveRecordStatus.REGISTERING_BANK: case ReserveRecordStatus.REGISTERING_BANK:
@ -245,7 +248,7 @@ async function registerReserveWithBank(
r.retryInfo = initRetryInfo(); r.retryInfo = initRetryInfo();
return r; return r;
}); });
ws.notify( { type: NotificationType.Wildcard }); ws.notify({ type: NotificationType.Wildcard });
return processReserveBankStatus(ws, reservePub); return processReserveBankStatus(ws, reservePub);
} }
@ -282,14 +285,16 @@ async function processReserveBankStatusImpl(
try { try {
const statusResp = await ws.http.get(bankStatusUrl); const statusResp = await ws.http.get(bankStatusUrl);
if (statusResp.status !== 200) { if (statusResp.status !== 200) {
throw Error(`unexpected status ${statusResp.status} for bank status query`); throw Error(
`unexpected status ${statusResp.status} for bank status query`,
);
} }
status = WithdrawOperationStatusResponse.checked(await statusResp.json()); status = WithdrawOperationStatusResponse.checked(await statusResp.json());
} catch (e) { } catch (e) {
throw e; throw e;
} }
ws.notify( { type: NotificationType.Wildcard }); ws.notify({ type: NotificationType.Wildcard });
if (status.selection_done) { if (status.selection_done) {
if (reserve.reserveStatus === ReserveRecordStatus.REGISTERING_BANK) { if (reserve.reserveStatus === ReserveRecordStatus.REGISTERING_BANK) {
@ -330,7 +335,7 @@ async function processReserveBankStatusImpl(
}); });
await incrementReserveRetry(ws, reservePub, undefined); await incrementReserveRetry(ws, reservePub, undefined);
} }
ws.notify( { type: NotificationType.Wildcard }); ws.notify({ type: NotificationType.Wildcard });
} }
async function incrementReserveRetry( async function incrementReserveRetry(
@ -351,7 +356,12 @@ async function incrementReserveRetry(
r.lastError = err; r.lastError = err;
await tx.put(Stores.reserves, r); await tx.put(Stores.reserves, r);
}); });
ws.notify({ type: NotificationType.ReserveOperationError }); if (err) {
ws.notify({
type: NotificationType.ReserveOperationError,
operationError: err,
});
}
} }
/** /**
@ -386,7 +396,7 @@ async function updateReserve(
return; return;
} }
if (resp.status !== 200) { if (resp.status !== 200) {
throw Error(`unexpected status code ${resp.status} for reserve/status`) throw Error(`unexpected status code ${resp.status} for reserve/status`);
} }
} catch (e) { } catch (e) {
const m = e.message; const m = e.message;
@ -400,68 +410,73 @@ async function updateReserve(
const respJson = await resp.json(); const respJson = await resp.json();
const reserveInfo = codecForReserveStatus.decode(respJson); const reserveInfo = codecForReserveStatus.decode(respJson);
const balance = Amounts.parseOrThrow(reserveInfo.balance); const balance = Amounts.parseOrThrow(reserveInfo.balance);
await ws.db.runWithWriteTransaction([Stores.reserves, Stores.reserveUpdatedEvents], async (tx) => { await ws.db.runWithWriteTransaction(
const r = await tx.get(Stores.reserves, reservePub); [Stores.reserves, Stores.reserveUpdatedEvents],
if (!r) { async tx => {
return; const r = await tx.get(Stores.reserves, reservePub);
} if (!r) {
if (r.reserveStatus !== ReserveRecordStatus.QUERYING_STATUS) {
return;
}
const newHistoryTransactions = reserveInfo.history.slice(r.reserveTransactions.length);
const reserveUpdateId = encodeCrock(getRandomBytes(32));
// FIXME: check / compare history!
if (!r.lastSuccessfulStatusQuery) {
// FIXME: check if this matches initial expectations
r.amountWithdrawRemaining = balance;
const reserveUpdate: ReserveUpdatedEventRecord = {
reservePub: r.reservePub,
timestamp: getTimestampNow(),
amountReserveBalance: Amounts.toString(balance),
amountExpected: Amounts.toString(reserve.amountInitiallyRequested),
newHistoryTransactions,
reserveUpdateId,
};
await tx.put(Stores.reserveUpdatedEvents, reserveUpdate);
} else {
const expectedBalance = Amounts.sub(
r.amountWithdrawAllocated,
r.amountWithdrawCompleted,
);
const cmp = Amounts.cmp(balance, expectedBalance.amount);
if (cmp == 0) {
// Nothing changed.
return; return;
} }
if (cmp > 0) { if (r.reserveStatus !== ReserveRecordStatus.QUERYING_STATUS) {
const extra = Amounts.sub(balance, expectedBalance.amount).amount; return;
r.amountWithdrawRemaining = Amounts.add(
r.amountWithdrawRemaining,
extra,
).amount;
} else {
// We're missing some money.
} }
const reserveUpdate: ReserveUpdatedEventRecord = {
reservePub: r.reservePub, const newHistoryTransactions = reserveInfo.history.slice(
timestamp: getTimestampNow(), r.reserveTransactions.length,
amountReserveBalance: Amounts.toString(balance), );
amountExpected: Amounts.toString(expectedBalance.amount),
newHistoryTransactions, const reserveUpdateId = encodeCrock(getRandomBytes(32));
reserveUpdateId,
}; // FIXME: check / compare history!
await tx.put(Stores.reserveUpdatedEvents, reserveUpdate); if (!r.lastSuccessfulStatusQuery) {
} // FIXME: check if this matches initial expectations
r.lastSuccessfulStatusQuery = getTimestampNow(); r.amountWithdrawRemaining = balance;
r.reserveStatus = ReserveRecordStatus.WITHDRAWING; const reserveUpdate: ReserveUpdatedEventRecord = {
r.retryInfo = initRetryInfo(); reservePub: r.reservePub,
r.reserveTransactions = reserveInfo.history; timestamp: getTimestampNow(),
await tx.put(Stores.reserves, r); amountReserveBalance: Amounts.toString(balance),
}); amountExpected: Amounts.toString(reserve.amountInitiallyRequested),
ws.notify( { type: NotificationType.ReserveUpdated }); newHistoryTransactions,
reserveUpdateId,
};
await tx.put(Stores.reserveUpdatedEvents, reserveUpdate);
} else {
const expectedBalance = Amounts.sub(
r.amountWithdrawAllocated,
r.amountWithdrawCompleted,
);
const cmp = Amounts.cmp(balance, expectedBalance.amount);
if (cmp == 0) {
// Nothing changed.
return;
}
if (cmp > 0) {
const extra = Amounts.sub(balance, expectedBalance.amount).amount;
r.amountWithdrawRemaining = Amounts.add(
r.amountWithdrawRemaining,
extra,
).amount;
} else {
// We're missing some money.
}
const reserveUpdate: ReserveUpdatedEventRecord = {
reservePub: r.reservePub,
timestamp: getTimestampNow(),
amountReserveBalance: Amounts.toString(balance),
amountExpected: Amounts.toString(expectedBalance.amount),
newHistoryTransactions,
reserveUpdateId,
};
await tx.put(Stores.reserveUpdatedEvents, reserveUpdate);
}
r.lastSuccessfulStatusQuery = getTimestampNow();
r.reserveStatus = ReserveRecordStatus.WITHDRAWING;
r.retryInfo = initRetryInfo();
r.reserveTransactions = reserveInfo.history;
await tx.put(Stores.reserves, r);
},
);
ws.notify({ type: NotificationType.ReserveUpdated });
} }
async function processReserveImpl( async function processReserveImpl(
@ -655,8 +670,6 @@ async function depleteReserve(
} }
} }
export async function createTalerWithdrawReserve( export async function createTalerWithdrawReserve(
ws: InternalWalletState, ws: InternalWalletState,
talerWithdrawUri: string, talerWithdrawUri: string,
@ -683,4 +696,4 @@ export async function createTalerWithdrawReserve(
reservePub: reserve.reservePub, reservePub: reserve.reservePub,
confirmTransferUrl: withdrawInfo.confirmTransferUrl, confirmTransferUrl: withdrawInfo.confirmTransferUrl,
}; };
} }

View File

@ -485,6 +485,16 @@ export interface HistoryPaymentSent {
*/ */
replay: boolean; replay: boolean;
/**
* Number of coins that were involved in the payment.
*/
numCoins: number;
/**
* Amount that was paid, including deposit and wire fees.
*/
amountPaidWithFees: string;
/** /**
* Session ID that the payment was (re-)submitted under. * Session ID that the payment was (re-)submitted under.
*/ */

View File

@ -1,3 +1,5 @@
import { OperationError } from "./walletTypes";
/* /*
This file is part of GNU Taler This file is part of GNU Taler
(C) 2019 GNUnet e.V. (C) 2019 GNUnet e.V.
@ -29,7 +31,7 @@ export const enum NotificationType {
RefreshRevealed = "refresh-revealed", RefreshRevealed = "refresh-revealed",
RefreshMelted = "refresh-melted", RefreshMelted = "refresh-melted",
RefreshStarted = "refresh-started", RefreshStarted = "refresh-started",
RefreshRefused = "refresh-refused", RefreshUnwarranted = "refresh-unwarranted",
ReserveUpdated = "reserve-updated", ReserveUpdated = "reserve-updated",
ReserveConfirmed = "reserve-confirmed", ReserveConfirmed = "reserve-confirmed",
ReserveDepleted = "reserve-depleted", ReserveDepleted = "reserve-depleted",
@ -100,7 +102,7 @@ export interface RefreshStartedNotification {
} }
export interface RefreshRefusedNotification { export interface RefreshRefusedNotification {
type: NotificationType.RefreshRefused; type: NotificationType.RefreshUnwarranted;
} }
export interface ReserveUpdatedNotification { export interface ReserveUpdatedNotification {
@ -170,6 +172,7 @@ export interface WithdrawOperationErrorNotification {
export interface ReserveOperationErrorNotification { export interface ReserveOperationErrorNotification {
type: NotificationType.ReserveOperationError; type: NotificationType.ReserveOperationError;
operationError: OperationError;
} }
export interface ReserveCreatedNotification { export interface ReserveCreatedNotification {

View File

@ -87,6 +87,8 @@ export interface PendingRefreshOperation {
type: PendingOperationType.Refresh; type: PendingOperationType.Refresh;
lastError?: OperationError; lastError?: OperationError;
refreshGroupId: string; refreshGroupId: string;
finishedPerCoin: boolean[];
retryInfo: RetryInfo;
} }
export interface PendingProposalDownloadOperation { export interface PendingProposalDownloadOperation {