pending ops / history / notification tweaks

This commit is contained in:
Florian Dold 2019-12-06 02:52:16 +01:00
parent d8170c8159
commit 4159367d8c
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
12 changed files with 302 additions and 148 deletions

View File

@ -31,6 +31,8 @@ import { HttpRequestLibrary, HttpResponse } from "../util/http";
// @ts-ignore: special built-in module // @ts-ignore: special built-in module
//import akono = require("akono"); //import akono = require("akono");
export { handleWorkerError, handleWorkerMessage } from "../crypto/workers/nodeThreadWorker";
export class AndroidHttpLib implements HttpRequestLibrary { export class AndroidHttpLib implements HttpRequestLibrary {
useNfcTunnel: boolean = false; useNfcTunnel: boolean = false;

View File

@ -1003,10 +1003,7 @@ export interface PurchaseRecord {
*/ */
merchantSig: string; merchantSig: string;
/** firstSuccessfulPayTimestamp: Timestamp | undefined;
* A successful payment has been made.
*/
payFinished: boolean;
/** /**
* Pending refunds for the purchase. * Pending refunds for the purchase.

View File

@ -32,7 +32,8 @@ import { Bank } from "./bank";
import fs = require("fs"); import fs = require("fs");
import { Logger } from "../util/logging"; import { Logger } from "../util/logging";
import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker"; import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker";
import { NotificationType } from "../walletTypes"; import { NotificationType, WalletNotification } from "../walletTypes";
import { SynchronousCryptoWorkerFactory } from "../crypto/workers/synchronousWorker";
const logger = new Logger("helpers.ts"); const logger = new Logger("helpers.ts");
@ -87,7 +88,7 @@ export interface DefaultNodeWalletArgs {
/** /**
* Handler for asynchronous notifications from the wallet. * Handler for asynchronous notifications from the wallet.
*/ */
notifyHandler?: (reason: string) => void; notifyHandler?: (n: WalletNotification) => void;
/** /**
* If specified, use this as HTTP request library instead * If specified, use this as HTTP request library instead
@ -163,11 +164,15 @@ export async function getDefaultNodeWallet(
const worker = new NodeThreadCryptoWorkerFactory(); const worker = new NodeThreadCryptoWorkerFactory();
return new Wallet( const w = new Wallet(
myDb, myDb,
myHttpLib, myHttpLib,
worker, worker,
); );
if (args.notifyHandler) {
w.addNotificationListener(args.notifyHandler);
}
return w;
} }
export async function withdrawTestBalance( export async function withdrawTestBalance(

View File

@ -138,7 +138,7 @@ export async function getBalances(
}); });
await tx.iter(Stores.purchases).forEach(t => { await tx.iter(Stores.purchases).forEach(t => {
if (t.payFinished) { if (t.firstSuccessfulPayTimestamp) {
return; return;
} }
for (const c of t.payReq.coins) { for (const c of t.payReq.coins) {

View File

@ -14,11 +14,11 @@
GNU 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/>
*/ */
/** /**
* Imports. * Imports.
*/ */
import { HistoryQuery, HistoryEvent } from "../walletTypes"; import { HistoryQuery, HistoryEvent } from "../walletTypes";
import { oneShotIter } from "../util/query"; import { oneShotIter, runWithReadTransaction } from "../util/query";
import { InternalWalletState } from "./state"; import { InternalWalletState } from "./state";
import { Stores, TipRecord } from "../dbTypes"; import { Stores, TipRecord } from "../dbTypes";
import * as Amounts from "../util/amounts"; import * as Amounts from "../util/amounts";
@ -34,139 +34,186 @@ export async function getHistory(
const history: HistoryEvent[] = []; const history: HistoryEvent[] = [];
// FIXME: do pagination instead of generating the full history // FIXME: do pagination instead of generating the full history
// We uniquely identify history rows via their timestamp. // We uniquely identify history rows via their timestamp.
// This works as timestamps are guaranteed to be monotonically // This works as timestamps are guaranteed to be monotonically
// increasing even // increasing even
/* await runWithReadTransaction(
const proposals = await oneShotIter(ws.db, Stores.proposals).toArray();
for (const p of proposals) {
history.push({
detail: {
contractTermsHash: p.contractTermsHash,
merchantName: p.contractTerms.merchant.name,
},
timestamp: p.timestamp,
type: "claim-order",
explicit: false,
});
}
*/
const withdrawals = await oneShotIter(
ws.db, ws.db,
Stores.withdrawalSession, [
).toArray(); Stores.currencies,
for (const w of withdrawals) { Stores.coins,
history.push({ Stores.denominations,
detail: { Stores.exchanges,
withdrawalAmount: w.rawWithdrawalAmount, Stores.proposals,
}, Stores.purchases,
timestamp: w.startTimestamp, Stores.refresh,
type: "withdraw", Stores.reserves,
explicit: false, Stores.tips,
}); Stores.withdrawalSession,
} ],
async tx => {
const purchases = await oneShotIter(ws.db, Stores.purchases).toArray(); await tx.iter(Stores.proposals).forEach(p => {
for (const p of purchases) { history.push({
history.push({ detail: {},
detail: { timestamp: p.timestamp,
amount: p.contractTerms.amount, type: "claim-order",
contractTermsHash: p.contractTermsHash, explicit: false,
fulfillmentUrl: p.contractTerms.fulfillment_url, });
merchantName: p.contractTerms.merchant.name,
},
timestamp: p.acceptTimestamp,
type: "pay",
explicit: false,
});
if (p.lastRefundStatusTimestamp) {
const contractAmount = Amounts.parseOrThrow(p.contractTerms.amount);
const amountsPending = Object.keys(p.refundsPending).map(x =>
Amounts.parseOrThrow(p.refundsPending[x].refund_amount),
);
const amountsDone = Object.keys(p.refundsDone).map(x =>
Amounts.parseOrThrow(p.refundsDone[x].refund_amount),
);
const amounts: AmountJson[] = amountsPending.concat(amountsDone);
const amount = Amounts.add(
Amounts.getZero(contractAmount.currency),
...amounts,
).amount;
history.push({
detail: {
contractTermsHash: p.contractTermsHash,
fulfillmentUrl: p.contractTerms.fulfillment_url,
merchantName: p.contractTerms.merchant.name,
refundAmount: amount,
},
timestamp: p.lastRefundStatusTimestamp,
type: "refund",
explicit: false,
}); });
}
}
const reserves = await oneShotIter(ws.db, Stores.reserves).toArray(); await tx.iter(Stores.withdrawalSession).forEach(w => {
history.push({
for (const r of reserves) { detail: {
const reserveType = r.bankWithdrawStatusUrl ? "taler-bank" : "manual"; withdrawalAmount: w.rawWithdrawalAmount,
history.push({ },
detail: { timestamp: w.startTimestamp,
exchangeBaseUrl: r.exchangeBaseUrl, type: "withdraw-started",
requestedAmount: Amounts.toString(r.initiallyRequestedAmount), explicit: false,
reservePub: r.reservePub, });
reserveType, if (w.finishTimestamp) {
bankWithdrawStatusUrl: r.bankWithdrawStatusUrl, history.push({
}, detail: {
timestamp: r.created, withdrawalAmount: w.rawWithdrawalAmount,
type: "reserve-created", },
explicit: false, timestamp: w.finishTimestamp,
}); type: "withdraw-finished",
if (r.timestampConfirmed) { explicit: false,
history.push({ });
detail: { }
exchangeBaseUrl: r.exchangeBaseUrl,
requestedAmount: Amounts.toString(r.initiallyRequestedAmount),
reservePub: r.reservePub,
reserveType,
bankWithdrawStatusUrl: r.bankWithdrawStatusUrl,
},
timestamp: r.created,
type: "reserve-confirmed",
explicit: false,
}); });
}
}
const tips: TipRecord[] = await oneShotIter(ws.db, Stores.tips).toArray(); await tx.iter(Stores.purchases).forEach(p => {
for (const tip of tips) { history.push({
history.push({ detail: {
detail: { amount: p.contractTerms.amount,
accepted: tip.accepted, contractTermsHash: p.contractTermsHash,
amount: tip.amount, fulfillmentUrl: p.contractTerms.fulfillment_url,
merchantBaseUrl: tip.merchantBaseUrl, merchantName: p.contractTerms.merchant.name,
tipId: tip.merchantTipId, },
}, timestamp: p.acceptTimestamp,
timestamp: tip.createdTimestamp, type: "pay-started",
explicit: false, explicit: false,
type: "tip", });
}); if (p.firstSuccessfulPayTimestamp) {
} history.push({
detail: {
amount: p.contractTerms.amount,
contractTermsHash: p.contractTermsHash,
fulfillmentUrl: p.contractTerms.fulfillment_url,
merchantName: p.contractTerms.merchant.name,
},
timestamp: p.firstSuccessfulPayTimestamp,
type: "pay-finished",
explicit: false,
});
}
if (p.lastRefundStatusTimestamp) {
const contractAmount = Amounts.parseOrThrow(p.contractTerms.amount);
const amountsPending = Object.keys(p.refundsPending).map(x =>
Amounts.parseOrThrow(p.refundsPending[x].refund_amount),
);
const amountsDone = Object.keys(p.refundsDone).map(x =>
Amounts.parseOrThrow(p.refundsDone[x].refund_amount),
);
const amounts: AmountJson[] = amountsPending.concat(amountsDone);
const amount = Amounts.add(
Amounts.getZero(contractAmount.currency),
...amounts,
).amount;
await oneShotIter(ws.db, Stores.exchanges).forEach(exchange => { history.push({
history.push({ detail: {
type: "exchange-added", contractTermsHash: p.contractTermsHash,
explicit: false, fulfillmentUrl: p.contractTerms.fulfillment_url,
timestamp: exchange.timestampAdded, merchantName: p.contractTerms.merchant.name,
detail: { refundAmount: amount,
exchangeBaseUrl: exchange.baseUrl, },
}, timestamp: p.lastRefundStatusTimestamp,
}); type: "refund",
}); explicit: false,
});
}
});
await tx.iter(Stores.reserves).forEach(r => {
const reserveType = r.bankWithdrawStatusUrl ? "taler-bank" : "manual";
history.push({
detail: {
exchangeBaseUrl: r.exchangeBaseUrl,
requestedAmount: Amounts.toString(r.initiallyRequestedAmount),
reservePub: r.reservePub,
reserveType,
bankWithdrawStatusUrl: r.bankWithdrawStatusUrl,
},
timestamp: r.created,
type: "reserve-created",
explicit: false,
});
if (r.timestampConfirmed) {
history.push({
detail: {
exchangeBaseUrl: r.exchangeBaseUrl,
requestedAmount: Amounts.toString(r.initiallyRequestedAmount),
reservePub: r.reservePub,
reserveType,
bankWithdrawStatusUrl: r.bankWithdrawStatusUrl,
},
timestamp: r.created,
type: "reserve-confirmed",
explicit: false,
});
}
});
await tx.iter(Stores.tips).forEach(tip => {
history.push({
detail: {
accepted: tip.accepted,
amount: tip.amount,
merchantBaseUrl: tip.merchantBaseUrl,
tipId: tip.merchantTipId,
},
timestamp: tip.createdTimestamp,
explicit: false,
type: "tip",
});
});
await tx.iter(Stores.exchanges).forEach(exchange => {
history.push({
type: "exchange-added",
explicit: false,
timestamp: exchange.timestampAdded,
detail: {
exchangeBaseUrl: exchange.baseUrl,
},
});
});
await tx.iter(Stores.refresh).forEach((r) => {
history.push({
type: "refresh-started",
explicit: false,
timestamp: r.created,
detail: {
refreshSessionId: r.refreshSessionId,
},
});
if (r.finishedTimestamp) {
history.push({
type: "refresh-finished",
explicit: false,
timestamp: r.finishedTimestamp,
detail: {
refreshSessionId: r.refreshSessionId,
},
});
}
});
},
);
history.sort((h1, h2) => Math.sign(h1.timestamp.t_ms - h2.timestamp.t_ms)); history.sort((h1, h2) => Math.sign(h1.timestamp.t_ms - h2.timestamp.t_ms));

View File

@ -343,7 +343,6 @@ async function recordConfirmPay(
abortRequested: false, abortRequested: false,
contractTerms: d.contractTerms, contractTerms: d.contractTerms,
contractTermsHash: d.contractTermsHash, contractTermsHash: d.contractTermsHash,
payFinished: false,
lastSessionId: undefined, lastSessionId: undefined,
merchantSig: d.merchantSig, merchantSig: d.merchantSig,
payReq, payReq,
@ -359,6 +358,7 @@ async function recordConfirmPay(
refundStatusRequested: false, refundStatusRequested: false,
lastRefundApplyError: undefined, lastRefundApplyError: undefined,
refundApplyRetryInfo: initRetryInfo(), refundApplyRetryInfo: initRetryInfo(),
firstSuccessfulPayTimestamp: undefined,
}; };
await runWithWriteTransaction( await runWithWriteTransaction(
@ -405,7 +405,7 @@ export async function abortFailedPayment(
if (!purchase) { if (!purchase) {
throw Error("Purchase not found, unable to abort with refund"); throw Error("Purchase not found, unable to abort with refund");
} }
if (purchase.payFinished) { if (purchase.firstSuccessfulPayTimestamp) {
throw Error("Purchase already finished, not aborting"); throw Error("Purchase already finished, not aborting");
} }
if (purchase.abortDone) { if (purchase.abortDone) {
@ -465,6 +465,7 @@ async function incrementProposalRetry(
pr.lastError = err; pr.lastError = err;
await tx.put(Stores.proposals, pr); await tx.put(Stores.proposals, pr);
}); });
ws.notify({ type: NotificationType.ProposalOperationError });
} }
async function incrementPurchasePayRetry( async function incrementPurchasePayRetry(
@ -486,6 +487,7 @@ async function incrementPurchasePayRetry(
pr.lastPayError = err; pr.lastPayError = err;
await tx.put(Stores.purchases, pr); await tx.put(Stores.purchases, pr);
}); });
ws.notify({ type: NotificationType.PayOperationError });
} }
async function incrementPurchaseQueryRefundRetry( async function incrementPurchaseQueryRefundRetry(
@ -507,6 +509,7 @@ async function incrementPurchaseQueryRefundRetry(
pr.lastRefundStatusError = err; pr.lastRefundStatusError = err;
await tx.put(Stores.purchases, pr); await tx.put(Stores.purchases, pr);
}); });
ws.notify({ type: NotificationType.RefundStatusOperationError });
} }
async function incrementPurchaseApplyRefundRetry( async function incrementPurchaseApplyRefundRetry(
@ -528,6 +531,7 @@ async function incrementPurchaseApplyRefundRetry(
pr.lastRefundApplyError = err; pr.lastRefundApplyError = err;
await tx.put(Stores.purchases, pr); await tx.put(Stores.purchases, pr);
}); });
ws.notify({ type: NotificationType.RefundApplyOperationError });
} }
export async function processDownloadProposal( export async function processDownloadProposal(
@ -698,7 +702,7 @@ export async function submitPay(
// FIXME: properly display error // FIXME: properly display error
throw Error("merchant payment signature invalid"); throw Error("merchant payment signature invalid");
} }
purchase.payFinished = true; purchase.firstSuccessfulPayTimestamp = getTimestampNow();
purchase.lastPayError = undefined; purchase.lastPayError = undefined;
purchase.payRetryInfo = initRetryInfo(false); purchase.payRetryInfo = initRetryInfo(false);
const modifiedCoins: CoinRecord[] = []; const modifiedCoins: CoinRecord[] = [];
@ -1044,10 +1048,9 @@ async function acceptRefundResponse(
throw Error("empty refund"); throw Error("empty refund");
} }
let numNewRefunds = 0; let numNewRefunds = 0;
await runWithWriteTransaction(ws.db, [Stores.purchases], async (tx) => { await runWithWriteTransaction(ws.db, [Stores.purchases], async tx => {
const p = await tx.get(Stores.purchases, proposalId); const p = await tx.get(Stores.purchases, proposalId);
if (!p) { if (!p) {
console.error("purchase not found, not adding refunds"); console.error("purchase not found, not adding refunds");
@ -1080,6 +1083,9 @@ async function acceptRefundResponse(
await tx.put(Stores.purchases, p); await tx.put(Stores.purchases, p);
}); });
ws.notify({
type: NotificationType.RefundQueried,
});
if (numNewRefunds > 0) { if (numNewRefunds > 0) {
await processPurchaseApplyRefund(ws, proposalId); await processPurchaseApplyRefund(ws, proposalId);
} }
@ -1099,7 +1105,6 @@ async function startRefundQuery(
return false; return false;
} }
if (p.refundStatusRequested) { if (p.refundStatusRequested) {
} }
p.refundStatusRequested = true; p.refundStatusRequested = true;
p.lastRefundStatusError = undefined; p.lastRefundStatusError = undefined;
@ -1113,6 +1118,10 @@ async function startRefundQuery(
return; return;
} }
ws.notify({
type: NotificationType.RefundStarted,
});
await processPurchaseQueryRefund(ws, proposalId); await processPurchaseQueryRefund(ws, proposalId);
} }
@ -1169,7 +1178,7 @@ async function processPurchasePayImpl(
return; return;
} }
logger.trace(`processing purchase pay ${proposalId}`); logger.trace(`processing purchase pay ${proposalId}`);
if (purchase.payFinished) { if (purchase.firstSuccessfulPayTimestamp) {
return; return;
} }
await submitPay(ws, proposalId, purchase.lastSessionId); await submitPay(ws, proposalId, purchase.lastSessionId);

View File

@ -353,7 +353,7 @@ async function gatherPurchasePending(
onlyDue: boolean = false, onlyDue: boolean = false,
): Promise<void> { ): Promise<void> {
await tx.iter(Stores.purchases).forEach((pr) => { await tx.iter(Stores.purchases).forEach((pr) => {
if (!pr.payFinished) { if (!pr.firstSuccessfulPayTimestamp) {
resp.nextRetryDelay = updateRetryDelay( resp.nextRetryDelay = updateRetryDelay(
resp.nextRetryDelay, resp.nextRetryDelay,
now, now,

View File

@ -312,6 +312,7 @@ async function incrementRefreshRetry(
r.lastError = err; r.lastError = err;
await tx.put(Stores.refresh, r); await tx.put(Stores.refresh, r);
}); });
ws.notify({ type: NotificationType.RefreshOperationError });
} }

View File

@ -342,6 +342,7 @@ 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 });
} }
/** /**
@ -606,6 +607,10 @@ async function depleteReserve(
if (success) { if (success) {
console.log("processing new withdraw session"); console.log("processing new withdraw session");
ws.notify({
type: NotificationType.WithdrawSessionCreated,
withdrawSessionId: withdrawalSessionId,
});
await processWithdrawSession(ws, withdrawalSessionId); await processWithdrawSession(ws, withdrawalSessionId);
} else { } else {
console.trace("withdraw session already existed"); console.trace("withdraw session already existed");

View File

@ -18,7 +18,7 @@
import { oneShotGet, oneShotPut, oneShotMutate, runWithWriteTransaction } from "../util/query"; import { oneShotGet, oneShotPut, oneShotMutate, runWithWriteTransaction } from "../util/query";
import { InternalWalletState } from "./state"; import { InternalWalletState } from "./state";
import { parseTipUri } from "../util/taleruri"; import { parseTipUri } from "../util/taleruri";
import { TipStatus, getTimestampNow, OperationError } from "../walletTypes"; import { TipStatus, getTimestampNow, OperationError, NotificationType } from "../walletTypes";
import { TipPickupGetResponse, TipPlanchetDetail, TipResponse } from "../talerTypes"; import { TipPickupGetResponse, TipPlanchetDetail, TipResponse } from "../talerTypes";
import * as Amounts from "../util/amounts"; import * as Amounts from "../util/amounts";
import { Stores, PlanchetRecord, WithdrawalSessionRecord, initRetryInfo, updateRetryInfoTimeout } from "../dbTypes"; import { Stores, PlanchetRecord, WithdrawalSessionRecord, initRetryInfo, updateRetryInfoTimeout } from "../dbTypes";
@ -122,6 +122,7 @@ async function incrementTipRetry(
t.lastError = err; t.lastError = err;
await tx.put(Stores.tips, t); await tx.put(Stores.tips, t);
}); });
ws.notify({ type: NotificationType.TipOperationError });
} }
export async function processTip( export async function processTip(

View File

@ -260,17 +260,17 @@ async function processPlanchet(
let withdrawSessionFinished = false; let withdrawSessionFinished = false;
let reserveDepleted = false; let reserveDepleted = false;
await runWithWriteTransaction( const success = await runWithWriteTransaction(
ws.db, ws.db,
[Stores.coins, Stores.withdrawalSession, Stores.reserves], [Stores.coins, Stores.withdrawalSession, Stores.reserves],
async tx => { async tx => {
const ws = await tx.get(Stores.withdrawalSession, withdrawalSessionId); const ws = await tx.get(Stores.withdrawalSession, withdrawalSessionId);
if (!ws) { if (!ws) {
return; return false;
} }
if (ws.withdrawn[coinIdx]) { if (ws.withdrawn[coinIdx]) {
// Already withdrawn // Already withdrawn
return; return false;
} }
ws.withdrawn[coinIdx] = true; ws.withdrawn[coinIdx] = true;
ws.lastCoinErrors[coinIdx] = undefined; ws.lastCoinErrors[coinIdx] = undefined;
@ -301,9 +301,16 @@ async function processPlanchet(
} }
} }
await tx.add(Stores.coins, coin); await tx.add(Stores.coins, coin);
return true;
}, },
); );
if (success) {
ws.notify( {
type: NotificationType.CoinWithdrawn,
} );
}
if (withdrawSessionFinished) { if (withdrawSessionFinished) {
ws.notify({ ws.notify({
type: NotificationType.WithdrawSessionFinished, type: NotificationType.WithdrawSessionFinished,
@ -503,6 +510,7 @@ async function incrementWithdrawalRetry(
wsr.lastError = err; wsr.lastError = err;
await tx.put(Stores.withdrawalSession, wsr); await tx.put(Stores.withdrawalSession, wsr);
}); });
ws.notify({ type: NotificationType.WithdrawOperationError });
} }
export async function processWithdrawSession( export async function processWithdrawSession(

View File

@ -506,6 +506,7 @@ export interface PendingPayOperation {
} }
export const enum NotificationType { export const enum NotificationType {
CoinWithdrawn = "coin-withdrawn",
ProposalAccepted = "proposal-accepted", ProposalAccepted = "proposal-accepted",
ProposalDownloaded = "proposal-downloaded", ProposalDownloaded = "proposal-downloaded",
RefundsSubmitted = "refunds-submitted", RefundsSubmitted = "refunds-submitted",
@ -518,9 +519,21 @@ export const enum NotificationType {
ReserveUpdated = "reserve-updated", ReserveUpdated = "reserve-updated",
ReserveConfirmed = "reserve-confirmed", ReserveConfirmed = "reserve-confirmed",
ReserveDepleted = "reserve-depleted", ReserveDepleted = "reserve-depleted",
WithdrawSessionCreated = "withdraw-session-created",
WithdrawSessionFinished = "withdraw-session-finished", WithdrawSessionFinished = "withdraw-session-finished",
WaitingForRetry = "waiting-for-retry", WaitingForRetry = "waiting-for-retry",
RefundStarted = "refund-started",
RefundQueried = "refund-queried",
RefundFinished = "refund-finished", RefundFinished = "refund-finished",
ExchangeOperationError = "exchange-operation-error",
RefreshOperationError = "refresh-operation-error",
RefundApplyOperationError = "refund-apply-error",
RefundStatusOperationError = "refund-status-error",
ProposalOperationError = "proposal-error",
TipOperationError = "tip-error",
PayOperationError = "pay-error",
WithdrawOperationError = "withdraw-error",
ReserveOperationError = "reserve-error",
} }
export interface ProposalAcceptedNotification { export interface ProposalAcceptedNotification {
@ -528,6 +541,18 @@ export interface ProposalAcceptedNotification {
proposalId: string; proposalId: string;
} }
export interface CoinWithdrawnNotification {
type: NotificationType.CoinWithdrawn;
}
export interface RefundStartedNotification {
type: NotificationType.RefundStarted;
}
export interface RefundQueriedNotification {
type: NotificationType.RefundQueried;
}
export interface ProposalDownloadedNotification { export interface ProposalDownloadedNotification {
type: NotificationType.ProposalDownloaded; type: NotificationType.ProposalDownloaded;
proposalId: string; proposalId: string;
@ -570,6 +595,11 @@ export interface ReserveConfirmedNotification {
type: NotificationType.ReserveConfirmed; type: NotificationType.ReserveConfirmed;
} }
export interface WithdrawSessionCreatedNotification {
type: NotificationType.WithdrawSessionCreated;
withdrawSessionId: string;
}
export interface WithdrawSessionFinishedNotification { export interface WithdrawSessionFinishedNotification {
type: NotificationType.WithdrawSessionFinished; type: NotificationType.WithdrawSessionFinished;
withdrawSessionId: string; withdrawSessionId: string;
@ -590,7 +620,52 @@ export interface RefundFinishedNotification {
type: NotificationType.RefundFinished; type: NotificationType.RefundFinished;
} }
export interface ExchangeOperationErrorNotification {
type: NotificationType.ExchangeOperationError;
}
export interface RefreshOperationErrorNotification {
type: NotificationType.RefreshOperationError;
}
export interface RefundStatusOperationErrorNotification {
type: NotificationType.RefundStatusOperationError;
}
export interface RefundApplyOperationErrorNotification {
type: NotificationType.RefundApplyOperationError;
}
export interface PayOperationErrorNotification {
type: NotificationType.PayOperationError;
}
export interface ProposalOperationErrorNotification {
type: NotificationType.ProposalOperationError;
}
export interface TipOperationErrorNotification {
type: NotificationType.TipOperationError;
}
export interface WithdrawOperationErrorNotification {
type: NotificationType.WithdrawOperationError;
}
export interface ReserveOperationErrorNotification {
type: NotificationType.ReserveOperationError;
}
export type WalletNotification = export type WalletNotification =
| WithdrawOperationErrorNotification
| ReserveOperationErrorNotification
| ExchangeOperationErrorNotification
| RefreshOperationErrorNotification
| RefundStatusOperationErrorNotification
| RefundApplyOperationErrorNotification
| ProposalOperationErrorNotification
| PayOperationErrorNotification
| TipOperationErrorNotification
| ProposalAcceptedNotification | ProposalAcceptedNotification
| ProposalDownloadedNotification | ProposalDownloadedNotification
| RefundsSubmittedNotification | RefundsSubmittedNotification
@ -605,7 +680,11 @@ export type WalletNotification =
| WithdrawSessionFinishedNotification | WithdrawSessionFinishedNotification
| ReserveDepletedNotification | ReserveDepletedNotification
| WaitingForRetryNotification | WaitingForRetryNotification
| RefundFinishedNotification; | RefundStartedNotification
| RefundFinishedNotification
| RefundQueriedNotification
| WithdrawSessionCreatedNotification
| CoinWithdrawnNotification;
export interface OperationError { export interface OperationError {
type: string; type: string;