sunset history API (we have the transaction list now)
This commit is contained in:
parent
d88829cfa8
commit
60891b502a
@ -1,512 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2019 GNUnet e.V.
|
||||
|
||||
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/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { InternalWalletState } from "./state";
|
||||
import { Stores, ProposalStatus, ProposalRecord } from "../types/dbTypes";
|
||||
import { Amounts } from "../util/amounts";
|
||||
import { AmountJson } from "../util/amounts";
|
||||
import {
|
||||
HistoryQuery,
|
||||
HistoryEvent,
|
||||
HistoryEventType,
|
||||
OrderShortInfo,
|
||||
ReserveType,
|
||||
ReserveCreationDetail,
|
||||
VerbosePayCoinDetails,
|
||||
VerboseRefreshDetails,
|
||||
} from "../types/history";
|
||||
import { assertUnreachable } from "../util/assertUnreachable";
|
||||
import { TransactionHandle } from "../util/query";
|
||||
import { timestampCmp } from "../util/time";
|
||||
import { summarizeReserveHistory } from "../util/reserveHistoryUtil";
|
||||
|
||||
/**
|
||||
* Create an event ID from the type and the primary key for the event.
|
||||
*/
|
||||
function makeEventId(type: HistoryEventType, ...args: string[]): string {
|
||||
return type + ";" + args.map((x) => encodeURIComponent(x)).join(";");
|
||||
}
|
||||
|
||||
function getOrderShortInfo(
|
||||
proposal: ProposalRecord,
|
||||
): OrderShortInfo | undefined {
|
||||
const download = proposal.download;
|
||||
if (!download) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
amount: Amounts.stringify(download.contractData.amount),
|
||||
fulfillmentUrl: download.contractData.fulfillmentUrl,
|
||||
orderId: download.contractData.orderId,
|
||||
merchantBaseUrl: download.contractData.merchantBaseUrl,
|
||||
proposalId: proposal.proposalId,
|
||||
summary: download.contractData.summary,
|
||||
};
|
||||
}
|
||||
|
||||
async function collectProposalHistory(
|
||||
tx: TransactionHandle,
|
||||
history: HistoryEvent[],
|
||||
historyQuery?: HistoryQuery,
|
||||
): Promise<void> {
|
||||
tx.iter(Stores.proposals).forEachAsync(async (proposal) => {
|
||||
const status = proposal.proposalStatus;
|
||||
switch (status) {
|
||||
case ProposalStatus.ACCEPTED:
|
||||
{
|
||||
const shortInfo = getOrderShortInfo(proposal);
|
||||
if (!shortInfo) {
|
||||
break;
|
||||
}
|
||||
history.push({
|
||||
type: HistoryEventType.OrderAccepted,
|
||||
eventId: makeEventId(
|
||||
HistoryEventType.OrderAccepted,
|
||||
proposal.proposalId,
|
||||
),
|
||||
orderShortInfo: shortInfo,
|
||||
timestamp: proposal.timestamp,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case ProposalStatus.DOWNLOADING:
|
||||
case ProposalStatus.PROPOSED:
|
||||
// no history event needed
|
||||
break;
|
||||
case ProposalStatus.REFUSED:
|
||||
{
|
||||
const shortInfo = getOrderShortInfo(proposal);
|
||||
if (!shortInfo) {
|
||||
break;
|
||||
}
|
||||
history.push({
|
||||
type: HistoryEventType.OrderRefused,
|
||||
eventId: makeEventId(
|
||||
HistoryEventType.OrderRefused,
|
||||
proposal.proposalId,
|
||||
),
|
||||
orderShortInfo: shortInfo,
|
||||
timestamp: proposal.timestamp,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case ProposalStatus.REPURCHASE:
|
||||
{
|
||||
const alreadyPaidProposal = await tx.get(
|
||||
Stores.proposals,
|
||||
proposal.repurchaseProposalId,
|
||||
);
|
||||
if (!alreadyPaidProposal) {
|
||||
break;
|
||||
}
|
||||
const alreadyPaidOrderShortInfo = getOrderShortInfo(
|
||||
alreadyPaidProposal,
|
||||
);
|
||||
if (!alreadyPaidOrderShortInfo) {
|
||||
break;
|
||||
}
|
||||
const newOrderShortInfo = getOrderShortInfo(proposal);
|
||||
if (!newOrderShortInfo) {
|
||||
break;
|
||||
}
|
||||
history.push({
|
||||
type: HistoryEventType.OrderRedirected,
|
||||
eventId: makeEventId(
|
||||
HistoryEventType.OrderRedirected,
|
||||
proposal.proposalId,
|
||||
),
|
||||
alreadyPaidOrderShortInfo,
|
||||
newOrderShortInfo,
|
||||
timestamp: proposal.timestamp,
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(status);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive the full event history for this wallet.
|
||||
*/
|
||||
export async function getHistory(
|
||||
ws: InternalWalletState,
|
||||
historyQuery?: HistoryQuery,
|
||||
): Promise<{ history: HistoryEvent[] }> {
|
||||
const history: HistoryEvent[] = [];
|
||||
|
||||
// FIXME: do pagination instead of generating the full history
|
||||
// We uniquely identify history rows via their timestamp.
|
||||
// This works as timestamps are guaranteed to be monotonically
|
||||
// increasing even
|
||||
|
||||
await ws.db.runWithReadTransaction(
|
||||
[
|
||||
Stores.currencies,
|
||||
Stores.coins,
|
||||
Stores.denominations,
|
||||
Stores.exchanges,
|
||||
Stores.exchangeUpdatedEvents,
|
||||
Stores.proposals,
|
||||
Stores.purchases,
|
||||
Stores.refreshGroups,
|
||||
Stores.reserves,
|
||||
Stores.reserveHistory,
|
||||
Stores.tips,
|
||||
Stores.withdrawalGroups,
|
||||
Stores.payEvents,
|
||||
Stores.planchets,
|
||||
Stores.refundEvents,
|
||||
Stores.reserveUpdatedEvents,
|
||||
Stores.recoupGroups,
|
||||
],
|
||||
async (tx) => {
|
||||
tx.iter(Stores.exchanges).forEach((exchange) => {
|
||||
history.push({
|
||||
type: HistoryEventType.ExchangeAdded,
|
||||
builtIn: false,
|
||||
eventId: makeEventId(
|
||||
HistoryEventType.ExchangeAdded,
|
||||
exchange.baseUrl,
|
||||
),
|
||||
exchangeBaseUrl: exchange.baseUrl,
|
||||
timestamp: exchange.timestampAdded,
|
||||
});
|
||||
});
|
||||
|
||||
tx.iter(Stores.exchangeUpdatedEvents).forEach((eu) => {
|
||||
history.push({
|
||||
type: HistoryEventType.ExchangeUpdated,
|
||||
eventId: makeEventId(
|
||||
HistoryEventType.ExchangeUpdated,
|
||||
eu.exchangeBaseUrl,
|
||||
),
|
||||
exchangeBaseUrl: eu.exchangeBaseUrl,
|
||||
timestamp: eu.timestamp,
|
||||
});
|
||||
});
|
||||
|
||||
tx.iter(Stores.withdrawalGroups).forEach((wsr) => {
|
||||
if (wsr.timestampFinish) {
|
||||
history.push({
|
||||
type: HistoryEventType.Withdrawn,
|
||||
withdrawalGroupId: wsr.withdrawalGroupId,
|
||||
eventId: makeEventId(
|
||||
HistoryEventType.Withdrawn,
|
||||
wsr.withdrawalGroupId,
|
||||
),
|
||||
amountWithdrawnEffective: Amounts.stringify(
|
||||
wsr.denomsSel.totalCoinValue,
|
||||
),
|
||||
amountWithdrawnRaw: Amounts.stringify(wsr.rawWithdrawalAmount),
|
||||
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||
timestamp: wsr.timestampFinish,
|
||||
withdrawalSource: wsr.source,
|
||||
verboseDetails: undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await collectProposalHistory(tx, history, historyQuery);
|
||||
|
||||
await tx.iter(Stores.payEvents).forEachAsync(async (pe) => {
|
||||
const proposal = await tx.get(Stores.proposals, pe.proposalId);
|
||||
if (!proposal) {
|
||||
return;
|
||||
}
|
||||
const purchase = await tx.get(Stores.purchases, pe.proposalId);
|
||||
if (!purchase) {
|
||||
return;
|
||||
}
|
||||
const orderShortInfo = getOrderShortInfo(proposal);
|
||||
if (!orderShortInfo) {
|
||||
return;
|
||||
}
|
||||
let verboseDetails: VerbosePayCoinDetails | undefined = undefined;
|
||||
if (historyQuery?.extraDebug) {
|
||||
const coins: {
|
||||
value: string;
|
||||
contribution: string;
|
||||
denomPub: string;
|
||||
}[] = [];
|
||||
for (const x of purchase.coinDepositPermissions) {
|
||||
const c = await tx.get(Stores.coins, x.coin_pub);
|
||||
if (!c) {
|
||||
// FIXME: what to do here??
|
||||
continue;
|
||||
}
|
||||
const d = await tx.get(Stores.denominations, [
|
||||
c.exchangeBaseUrl,
|
||||
c.denomPub,
|
||||
]);
|
||||
if (!d) {
|
||||
// FIXME: what to do here??
|
||||
continue;
|
||||
}
|
||||
coins.push({
|
||||
contribution: x.contribution,
|
||||
denomPub: c.denomPub,
|
||||
value: Amounts.stringify(d.value),
|
||||
});
|
||||
}
|
||||
verboseDetails = { coins };
|
||||
}
|
||||
const amountPaidWithFees = Amounts.sum(
|
||||
purchase.coinDepositPermissions.map((x) =>
|
||||
Amounts.parseOrThrow(x.contribution),
|
||||
),
|
||||
).amount;
|
||||
history.push({
|
||||
type: HistoryEventType.PaymentSent,
|
||||
eventId: makeEventId(HistoryEventType.PaymentSent, pe.proposalId),
|
||||
orderShortInfo,
|
||||
replay: pe.isReplay,
|
||||
sessionId: pe.sessionId,
|
||||
timestamp: pe.timestamp,
|
||||
numCoins: purchase.coinDepositPermissions.length,
|
||||
amountPaidWithFees: Amounts.stringify(amountPaidWithFees),
|
||||
verboseDetails,
|
||||
});
|
||||
});
|
||||
|
||||
await tx.iter(Stores.refreshGroups).forEachAsync(async (rg) => {
|
||||
if (!rg.timestampFinished) {
|
||||
return;
|
||||
}
|
||||
let numInputCoins = 0;
|
||||
let numRefreshedInputCoins = 0;
|
||||
let numOutputCoins = 0;
|
||||
const amountsRaw: AmountJson[] = [];
|
||||
const amountsEffective: AmountJson[] = [];
|
||||
for (let i = 0; i < rg.refreshSessionPerCoin.length; i++) {
|
||||
const session = rg.refreshSessionPerCoin[i];
|
||||
numInputCoins++;
|
||||
const c = await tx.get(Stores.coins, rg.oldCoinPubs[i]);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
if (session) {
|
||||
numRefreshedInputCoins++;
|
||||
amountsRaw.push(session.amountRefreshInput);
|
||||
amountsRaw.push(c.currentAmount);
|
||||
amountsEffective.push(session.amountRefreshOutput);
|
||||
numOutputCoins += session.newDenoms.length;
|
||||
} else {
|
||||
amountsRaw.push(c.currentAmount);
|
||||
}
|
||||
}
|
||||
const amountRefreshedRaw = Amounts.sum(amountsRaw).amount;
|
||||
let amountRefreshedEffective: AmountJson;
|
||||
if (amountsEffective.length == 0) {
|
||||
amountRefreshedEffective = Amounts.getZero(
|
||||
amountRefreshedRaw.currency,
|
||||
);
|
||||
} else {
|
||||
amountRefreshedEffective = Amounts.sum(amountsEffective).amount;
|
||||
}
|
||||
let verboseDetails: VerboseRefreshDetails | undefined = undefined;
|
||||
if (historyQuery?.extraDebug) {
|
||||
const outputCoins: {
|
||||
value: string;
|
||||
denomPub: string;
|
||||
}[] = [];
|
||||
for (const rs of rg.refreshSessionPerCoin) {
|
||||
if (!rs) {
|
||||
continue;
|
||||
}
|
||||
for (const nd of rs.newDenoms) {
|
||||
if (!nd) {
|
||||
continue;
|
||||
}
|
||||
const d = await tx.get(Stores.denominations, [
|
||||
rs.exchangeBaseUrl,
|
||||
nd,
|
||||
]);
|
||||
if (!d) {
|
||||
continue;
|
||||
}
|
||||
outputCoins.push({
|
||||
denomPub: d.denomPub,
|
||||
value: Amounts.stringify(d.value),
|
||||
});
|
||||
}
|
||||
}
|
||||
verboseDetails = {
|
||||
outputCoins: outputCoins,
|
||||
};
|
||||
}
|
||||
history.push({
|
||||
type: HistoryEventType.Refreshed,
|
||||
refreshGroupId: rg.refreshGroupId,
|
||||
eventId: makeEventId(HistoryEventType.Refreshed, rg.refreshGroupId),
|
||||
timestamp: rg.timestampFinished,
|
||||
refreshReason: rg.reason,
|
||||
amountRefreshedEffective: Amounts.stringify(amountRefreshedEffective),
|
||||
amountRefreshedRaw: Amounts.stringify(amountRefreshedRaw),
|
||||
numInputCoins,
|
||||
numOutputCoins,
|
||||
numRefreshedInputCoins,
|
||||
verboseDetails,
|
||||
});
|
||||
});
|
||||
|
||||
tx.iter(Stores.reserveUpdatedEvents).forEachAsync(async (ru) => {
|
||||
const reserve = await tx.get(Stores.reserves, ru.reservePub);
|
||||
if (!reserve) {
|
||||
return;
|
||||
}
|
||||
let reserveCreationDetail: ReserveCreationDetail;
|
||||
if (reserve.bankInfo) {
|
||||
reserveCreationDetail = {
|
||||
type: ReserveType.TalerBankWithdraw,
|
||||
bankUrl: reserve.bankInfo.statusUrl,
|
||||
};
|
||||
} else {
|
||||
reserveCreationDetail = {
|
||||
type: ReserveType.Manual,
|
||||
};
|
||||
}
|
||||
const hist = await tx.get(Stores.reserveHistory, reserve.reservePub);
|
||||
if (!hist) {
|
||||
throw Error("inconsistent database");
|
||||
}
|
||||
const s = summarizeReserveHistory(
|
||||
hist.reserveTransactions,
|
||||
reserve.currency,
|
||||
);
|
||||
history.push({
|
||||
type: HistoryEventType.ReserveBalanceUpdated,
|
||||
eventId: makeEventId(
|
||||
HistoryEventType.ReserveBalanceUpdated,
|
||||
ru.reserveUpdateId,
|
||||
),
|
||||
timestamp: ru.timestamp,
|
||||
reserveShortInfo: {
|
||||
exchangeBaseUrl: reserve.exchangeBaseUrl,
|
||||
reserveCreationDetail,
|
||||
reservePub: reserve.reservePub,
|
||||
},
|
||||
reserveAwaitedAmount: Amounts.stringify(s.awaitedReserveAmount),
|
||||
reserveBalance: Amounts.stringify(s.computedReserveBalance),
|
||||
reserveUnclaimedAmount: Amounts.stringify(s.unclaimedReserveAmount),
|
||||
});
|
||||
});
|
||||
|
||||
tx.iter(Stores.tips).forEach((tip) => {
|
||||
if (tip.acceptedTimestamp) {
|
||||
history.push({
|
||||
type: HistoryEventType.TipAccepted,
|
||||
eventId: makeEventId(HistoryEventType.TipAccepted, tip.tipId),
|
||||
timestamp: tip.acceptedTimestamp,
|
||||
tipId: tip.tipId,
|
||||
tipAmountRaw: Amounts.stringify(tip.amount),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// tx.iter(Stores.refundEvents).forEachAsync(async (re) => {
|
||||
// const proposal = await tx.get(Stores.proposals, re.proposalId);
|
||||
// if (!proposal) {
|
||||
// return;
|
||||
// }
|
||||
// const purchase = await tx.get(Stores.purchases, re.proposalId);
|
||||
// if (!purchase) {
|
||||
// return;
|
||||
// }
|
||||
// const orderShortInfo = getOrderShortInfo(proposal);
|
||||
// if (!orderShortInfo) {
|
||||
// return;
|
||||
// }
|
||||
// const purchaseAmount = purchase.contractData.amount;
|
||||
// let amountRefundedRaw = Amounts.getZero(purchaseAmount.currency);
|
||||
// let amountRefundedInvalid = Amounts.getZero(purchaseAmount.currency);
|
||||
// let amountRefundedEffective = Amounts.getZero(purchaseAmount.currency);
|
||||
// Object.keys(purchase.refundsDone).forEach((x, i) => {
|
||||
// const r = purchase.refundsDone[x];
|
||||
// if (r.refundGroupId !== re.refundGroupId) {
|
||||
// return;
|
||||
// }
|
||||
// const refundAmount = Amounts.parseOrThrow(r.perm.refund_amount);
|
||||
// const refundFee = Amounts.parseOrThrow(r.perm.refund_fee);
|
||||
// amountRefundedRaw = Amounts.add(amountRefundedRaw, refundAmount)
|
||||
// .amount;
|
||||
// amountRefundedEffective = Amounts.add(
|
||||
// amountRefundedEffective,
|
||||
// refundAmount,
|
||||
// ).amount;
|
||||
// amountRefundedEffective = Amounts.sub(
|
||||
// amountRefundedEffective,
|
||||
// refundFee,
|
||||
// ).amount;
|
||||
// });
|
||||
// Object.keys(purchase.refundsFailed).forEach((x, i) => {
|
||||
// const r = purchase.refundsFailed[x];
|
||||
// if (r.refundGroupId !== re.refundGroupId) {
|
||||
// return;
|
||||
// }
|
||||
// const ra = Amounts.parseOrThrow(r.perm.refund_amount);
|
||||
// const refundFee = Amounts.parseOrThrow(r.perm.refund_fee);
|
||||
// amountRefundedRaw = Amounts.add(amountRefundedRaw, ra).amount;
|
||||
// amountRefundedInvalid = Amounts.add(amountRefundedInvalid, ra).amount;
|
||||
// amountRefundedEffective = Amounts.sub(
|
||||
// amountRefundedEffective,
|
||||
// refundFee,
|
||||
// ).amount;
|
||||
// });
|
||||
// history.push({
|
||||
// type: HistoryEventType.Refund,
|
||||
// eventId: makeEventId(HistoryEventType.Refund, re.refundGroupId),
|
||||
// refundGroupId: re.refundGroupId,
|
||||
// orderShortInfo,
|
||||
// timestamp: re.timestamp,
|
||||
// amountRefundedEffective: Amounts.stringify(amountRefundedEffective),
|
||||
// amountRefundedRaw: Amounts.stringify(amountRefundedRaw),
|
||||
// amountRefundedInvalid: Amounts.stringify(amountRefundedInvalid),
|
||||
// });
|
||||
// });
|
||||
|
||||
tx.iter(Stores.recoupGroups).forEach((rg) => {
|
||||
if (rg.timestampFinished) {
|
||||
let verboseDetails: any = undefined;
|
||||
if (historyQuery?.extraDebug) {
|
||||
verboseDetails = {
|
||||
oldAmountPerCoin: rg.oldAmountPerCoin.map(Amounts.stringify),
|
||||
};
|
||||
}
|
||||
|
||||
history.push({
|
||||
type: HistoryEventType.FundsRecouped,
|
||||
timestamp: rg.timestampFinished,
|
||||
eventId: makeEventId(
|
||||
HistoryEventType.FundsRecouped,
|
||||
rg.recoupGroupId,
|
||||
),
|
||||
numCoinsRecouped: rg.coinPubs.length,
|
||||
verboseDetails,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
history.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp));
|
||||
|
||||
return { history };
|
||||
}
|
@ -1,698 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2019 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/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Type and schema definitions for the wallet's history.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { RefreshReason } from "./walletTypes";
|
||||
import { WithdrawalSource } from "./dbTypes";
|
||||
import { Timestamp } from "../util/time";
|
||||
|
||||
/**
|
||||
* Type tags for the history event types.
|
||||
*/
|
||||
export const enum HistoryEventType {
|
||||
AuditorComplaintSent = "auditor-complained-sent",
|
||||
AuditorComplaintProcessed = "auditor-complaint-processed",
|
||||
AuditorTrustAdded = "auditor-trust-added",
|
||||
AuditorTrustRemoved = "auditor-trust-removed",
|
||||
ExchangeAdded = "exchange-added",
|
||||
ExchangeTermsAccepted = "exchange-terms-accepted",
|
||||
ExchangePolicyChanged = "exchange-policy-changed",
|
||||
ExchangeTrustAdded = "exchange-trust-added",
|
||||
ExchangeTrustRemoved = "exchange-trust-removed",
|
||||
ExchangeUpdated = "exchange-updated",
|
||||
FundsDepositedToSelf = "funds-deposited-to-self",
|
||||
FundsRecouped = "funds-recouped",
|
||||
OrderAccepted = "order-accepted",
|
||||
OrderRedirected = "order-redirected",
|
||||
OrderRefused = "order-refused",
|
||||
PaymentAborted = "payment-aborted",
|
||||
PaymentSent = "payment-sent",
|
||||
Refreshed = "refreshed",
|
||||
Refund = "refund",
|
||||
ReserveBalanceUpdated = "reserve-balance-updated",
|
||||
ReserveCreated = "reserve-created",
|
||||
TipAccepted = "tip-accepted",
|
||||
TipDeclined = "tip-declined",
|
||||
Withdrawn = "withdrawn",
|
||||
}
|
||||
|
||||
export const enum ReserveType {
|
||||
/**
|
||||
* Manually created.
|
||||
*/
|
||||
Manual = "manual",
|
||||
/**
|
||||
* Withdrawn from a bank that has "tight" Taler integration
|
||||
*/
|
||||
TalerBankWithdraw = "taler-bank-withdraw",
|
||||
}
|
||||
|
||||
/**
|
||||
* Short info about a reserve. Enough to display in a list view and
|
||||
* to query more information from the wallet.
|
||||
*/
|
||||
export interface ReserveShortInfo {
|
||||
/**
|
||||
* The exchange that the reserve will be at.
|
||||
*/
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Key to query more details
|
||||
*/
|
||||
reservePub: string;
|
||||
|
||||
/**
|
||||
* Detail about how the reserve has been created.
|
||||
*/
|
||||
reserveCreationDetail: ReserveCreationDetail;
|
||||
}
|
||||
|
||||
export type ReserveCreationDetail =
|
||||
| { type: ReserveType.Manual }
|
||||
| { type: ReserveType.TalerBankWithdraw; bankUrl: string };
|
||||
|
||||
export interface HistoryReserveCreatedEvent {
|
||||
type: HistoryEventType.ReserveCreated;
|
||||
|
||||
/**
|
||||
* Amount that the should appear in the reserve once its status
|
||||
* is requested from the exchange.
|
||||
*/
|
||||
expectedAmount: string;
|
||||
|
||||
/**
|
||||
* Condensed information about the reserve.
|
||||
*/
|
||||
reserveShortInfo: ReserveShortInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is emitted every time we ask the exchange for the status
|
||||
* of the reserve, and the status has changed.
|
||||
*/
|
||||
export interface HistoryReserveBalanceUpdatedEvent {
|
||||
type: HistoryEventType.ReserveBalanceUpdated;
|
||||
|
||||
/**
|
||||
* Point in time when the reserve was confirmed.
|
||||
*/
|
||||
timestamp: Timestamp;
|
||||
|
||||
/**
|
||||
* Condensed information about the reserve.
|
||||
*/
|
||||
reserveShortInfo: ReserveShortInfo;
|
||||
|
||||
/**
|
||||
* Amount currently left in the reserve.
|
||||
*/
|
||||
reserveBalance: string;
|
||||
|
||||
/**
|
||||
* Amount we still expect to be added to the reserve.
|
||||
*/
|
||||
reserveAwaitedAmount: string;
|
||||
|
||||
/**
|
||||
* Amount that hasn't been withdrawn yet.
|
||||
*/
|
||||
reserveUnclaimedAmount: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* History event to indicate that the user has accepted a tip.
|
||||
*/
|
||||
export interface HistoryTipAcceptedEvent {
|
||||
type: HistoryEventType.TipAccepted;
|
||||
|
||||
/**
|
||||
* Point in time when the tip has been accepted.
|
||||
*/
|
||||
timestamp: Timestamp;
|
||||
|
||||
/**
|
||||
* Unique identifier for the tip to query more information.
|
||||
*/
|
||||
tipId: string;
|
||||
|
||||
/**
|
||||
* Raw amount of the tip, without extra fees that apply.
|
||||
*/
|
||||
tipAmountRaw: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* History event to indicate that the user has accepted a tip.
|
||||
*/
|
||||
export interface HistoryTipDeclinedEvent {
|
||||
type: HistoryEventType.TipDeclined;
|
||||
|
||||
/**
|
||||
* Point in time when the tip has been declined.
|
||||
*/
|
||||
timestamp: Timestamp;
|
||||
|
||||
/**
|
||||
* Unique identifier for the tip to query more information.
|
||||
*/
|
||||
tipId: string;
|
||||
|
||||
/**
|
||||
* Raw amount of the tip, without extra fees that apply.
|
||||
*/
|
||||
tipAmountRaw: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The wallet has send a complaint (typically with cryptographic evidence of
|
||||
* something having gone wrong) to the auditor.
|
||||
*/
|
||||
export interface HistoryAuditorComplaintSentEvent {
|
||||
type: HistoryEventType.AuditorComplaintSent;
|
||||
|
||||
auditorComplaintId: string;
|
||||
|
||||
/* FIXME: add fields once this feature is implemented */
|
||||
}
|
||||
|
||||
/**
|
||||
* The wallet has received a response from the auditor to a complaint.
|
||||
*/
|
||||
export interface HistoryAuditorComplaintProcessedEvent {
|
||||
type: HistoryEventType.AuditorComplaintProcessed;
|
||||
|
||||
auditorComplaintId: string;
|
||||
|
||||
/* FIXME: add fields once this feature is implemented */
|
||||
}
|
||||
|
||||
/**
|
||||
* The wallet has added an auditor as a trusted auditor.
|
||||
*/
|
||||
export interface HistoryAuditorTrustAddedEvent {
|
||||
type: HistoryEventType.AuditorTrustAdded;
|
||||
|
||||
/**
|
||||
* Base URL of the auditor.
|
||||
*/
|
||||
auditorBaseUrl: string;
|
||||
|
||||
/**
|
||||
* If set to true, this auditor hasn't been added by the user,
|
||||
* but is part of the pre-set trusted auditors in the wallet.
|
||||
*/
|
||||
builtIn: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The wallet has added an auditor as a trusted auditor.
|
||||
*/
|
||||
export interface HistoryAuditorTrustRemovedEvent {
|
||||
type: HistoryEventType.AuditorTrustRemoved;
|
||||
|
||||
/**
|
||||
* Base URL of the auditor.
|
||||
*/
|
||||
auditorBaseUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An exchange has been added to the wallet. The wallet only
|
||||
* downloads information about the exchange, but does not necessarily
|
||||
* trust it yet.
|
||||
*/
|
||||
export interface HistoryExchangeAddedEvent {
|
||||
type: HistoryEventType.ExchangeAdded;
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Set to true if the exchange was pre-configured
|
||||
* by the wallet.
|
||||
*/
|
||||
builtIn: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* History event to indicate that the wallet now trusts
|
||||
* an exchange.
|
||||
*/
|
||||
export interface HistoryExchangeTrustAddedEvent {
|
||||
type: HistoryEventType.ExchangeTrustAdded;
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Set to true if the exchange was pre-configured
|
||||
* by the wallet.
|
||||
*/
|
||||
builtIn: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* History event to indicate that the wallet doesn't trust
|
||||
* an exchange anymore.
|
||||
*/
|
||||
export interface HistoryExchangeTrustRemovedEvent {
|
||||
type: HistoryEventType.ExchangeTrustRemoved;
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This event indicates that the user accepted the terms of service
|
||||
* of the exchange.
|
||||
*/
|
||||
export interface HistoryExchangeTermsAcceptedEvent {
|
||||
type: HistoryEventType.ExchangeTermsAccepted;
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Etag that the exchange sent with the terms of service.
|
||||
* Identifies the version of the terms of service.
|
||||
*/
|
||||
termsEtag: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The exchange has changed the terms of service or privacy
|
||||
* policy.
|
||||
*/
|
||||
export interface HistoryExchangePolicyChangedEvent {
|
||||
type: HistoryEventType.ExchangePolicyChanged;
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Etag that the exchange sent with the terms of service.
|
||||
* Identifies the version of the terms of service.
|
||||
*/
|
||||
termsEtag: string | undefined;
|
||||
|
||||
/**
|
||||
* Etag that the exchange sent with the privacy policy.
|
||||
* Identifies the version of the privacy policy.
|
||||
*/
|
||||
privacyPolicyEtag: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* This history event indicates that the exchange has updated
|
||||
* the /keys or /wire information. The event is only emitted if
|
||||
* this information changed since the last time the wallet checked it.
|
||||
*/
|
||||
export interface HistoryExchangeUpdatedEvent {
|
||||
type: HistoryEventType.ExchangeUpdated;
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* History event to indicate that the user sent back digital cash from
|
||||
* their wallet back to their own bank account (basically acting as a merchant).
|
||||
*/
|
||||
export interface HistoryFundsDepositedToSelfEvent {
|
||||
type: HistoryEventType.FundsDepositedToSelf;
|
||||
|
||||
/**
|
||||
* Amount that got sent back.
|
||||
*/
|
||||
amount: string;
|
||||
|
||||
/**
|
||||
* Account that received the funds.
|
||||
*/
|
||||
receiverPaytoUri: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* History event to indicate that the exchange marked
|
||||
* some denominations as "compromised", and the wallet has
|
||||
* converted funds in these denominations to new funds.
|
||||
*/
|
||||
export interface HistoryFundsRecoupedEvent {
|
||||
type: HistoryEventType.FundsRecouped;
|
||||
numCoinsRecouped: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Condensed information about an order, enough to display in a list view
|
||||
* and to query more details from the wallet for a detail view.
|
||||
*/
|
||||
export interface OrderShortInfo {
|
||||
/**
|
||||
* Wallet-internal identifier of the proposal.
|
||||
*/
|
||||
proposalId: string;
|
||||
|
||||
/**
|
||||
* Order ID, uniquely identifies the order within a merchant instance.
|
||||
*/
|
||||
orderId: string;
|
||||
|
||||
/**
|
||||
* Base URL of the merchant.
|
||||
*/
|
||||
merchantBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Amount that must be paid for the contract.
|
||||
*/
|
||||
amount: string;
|
||||
|
||||
/**
|
||||
* Summary of the proposal, given by the merchant.
|
||||
*/
|
||||
summary: string;
|
||||
|
||||
/**
|
||||
* URL of the fulfillment, given by the merchant.
|
||||
*/
|
||||
fulfillmentUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user has accepted purchasing something.
|
||||
*/
|
||||
export interface HistoryOrderAcceptedEvent {
|
||||
/**
|
||||
* Type tag.
|
||||
*/
|
||||
type: HistoryEventType.OrderAccepted;
|
||||
|
||||
/**
|
||||
* Condensed info about the order.
|
||||
*/
|
||||
orderShortInfo: OrderShortInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* The customer refused to pay.
|
||||
*/
|
||||
export interface HistoryOrderRefusedEvent {
|
||||
/**
|
||||
* Type tag.
|
||||
*/
|
||||
type: HistoryEventType.OrderRefused;
|
||||
|
||||
/**
|
||||
* Condensed info about the order.
|
||||
*/
|
||||
orderShortInfo: OrderShortInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* The wallet has claimed an order.
|
||||
*/
|
||||
export interface HistoryOrderRedirectedEvent {
|
||||
/**
|
||||
* Type tag.
|
||||
*/
|
||||
type: HistoryEventType.OrderRedirected;
|
||||
|
||||
/**
|
||||
* Condensed info about the new order that contains a
|
||||
* product (identified by the fulfillment URL) that we've already paid for.
|
||||
*/
|
||||
newOrderShortInfo: OrderShortInfo;
|
||||
|
||||
/**
|
||||
* Condensed info about the order that we already paid for.
|
||||
*/
|
||||
alreadyPaidOrderShortInfo: OrderShortInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user aborted a pending, partially submitted payment.
|
||||
*/
|
||||
export interface HistoryPaymentAbortedEvent {
|
||||
/**
|
||||
* Type tag.
|
||||
*/
|
||||
type: HistoryEventType.PaymentAborted;
|
||||
|
||||
/**
|
||||
* Condensed info about the order that we already paid for.
|
||||
*/
|
||||
orderShortInfo: OrderShortInfo;
|
||||
|
||||
/**
|
||||
* Amount that was lost due to refund and refreshing fees.
|
||||
*/
|
||||
amountLost: string;
|
||||
}
|
||||
|
||||
export interface VerbosePayCoinDetails {
|
||||
coins: {
|
||||
value: string;
|
||||
contribution: string;
|
||||
denomPub: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* History event to indicate that a payment has been (re-)submitted
|
||||
* to the merchant.
|
||||
*/
|
||||
export interface HistoryPaymentSent {
|
||||
/**
|
||||
* Type tag.
|
||||
*/
|
||||
type: HistoryEventType.PaymentSent;
|
||||
|
||||
/**
|
||||
* Condensed info about the order that we already paid for.
|
||||
*/
|
||||
orderShortInfo: OrderShortInfo;
|
||||
|
||||
/**
|
||||
* Set to true if the payment has been previously sent
|
||||
* to the merchant successfully, possibly with a different session ID.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
sessionId: string | undefined;
|
||||
|
||||
verboseDetails?: VerbosePayCoinDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* A refund has been applied.
|
||||
*/
|
||||
export interface HistoryRefunded {
|
||||
/**
|
||||
* Type tag.
|
||||
*/
|
||||
type: HistoryEventType.Refund;
|
||||
|
||||
timestamp: Timestamp;
|
||||
|
||||
orderShortInfo: OrderShortInfo;
|
||||
|
||||
/**
|
||||
* Unique identifier for this refund.
|
||||
* (Identifies multiple refund permissions that were obtained at once.)
|
||||
*/
|
||||
refundGroupId: string;
|
||||
|
||||
/**
|
||||
* Part of the refund that couldn't be applied because
|
||||
* the refund permissions were expired.
|
||||
*/
|
||||
amountRefundedInvalid: string;
|
||||
|
||||
/**
|
||||
* Amount that has been refunded by the merchant.
|
||||
*/
|
||||
amountRefundedRaw: string;
|
||||
|
||||
/**
|
||||
* Amount will be added to the wallet's balance after fees and refreshing.
|
||||
*/
|
||||
amountRefundedEffective: string;
|
||||
}
|
||||
|
||||
export interface VerboseRefreshDetails {
|
||||
outputCoins: {
|
||||
denomPub: string;
|
||||
value: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Event to indicate that a group of refresh sessions has completed.
|
||||
*/
|
||||
export interface HistoryRefreshedEvent {
|
||||
/**
|
||||
* Type tag.
|
||||
*/
|
||||
type: HistoryEventType.Refreshed;
|
||||
|
||||
/**
|
||||
* Amount that is now available again because it has
|
||||
* been refreshed.
|
||||
*/
|
||||
amountRefreshedEffective: string;
|
||||
|
||||
/**
|
||||
* Amount that we spent for refreshing.
|
||||
*/
|
||||
amountRefreshedRaw: string;
|
||||
|
||||
/**
|
||||
* Why was the refreshing done?
|
||||
*/
|
||||
refreshReason: RefreshReason;
|
||||
|
||||
numInputCoins: number;
|
||||
numRefreshedInputCoins: number;
|
||||
numOutputCoins: number;
|
||||
|
||||
/**
|
||||
* Identifier for a refresh group, contains one or
|
||||
* more refresh session IDs.
|
||||
*/
|
||||
refreshGroupId: string;
|
||||
|
||||
verboseDetails?: VerboseRefreshDetails;
|
||||
}
|
||||
|
||||
export interface VerboseWithdrawDetails {
|
||||
coins: {
|
||||
value: string;
|
||||
denomPub: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A withdrawal has completed.
|
||||
*/
|
||||
export interface HistoryWithdrawnEvent {
|
||||
type: HistoryEventType.Withdrawn;
|
||||
|
||||
/**
|
||||
* Exchange that was withdrawn from.
|
||||
*/
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Unique identifier for the withdrawal session, can be used to
|
||||
* query more detailed information from the wallet.
|
||||
*/
|
||||
withdrawalGroupId: string;
|
||||
|
||||
withdrawalSource: WithdrawalSource;
|
||||
|
||||
/**
|
||||
* Amount that has been subtracted from the reserve's balance
|
||||
* for this withdrawal.
|
||||
*/
|
||||
amountWithdrawnRaw: string;
|
||||
|
||||
/**
|
||||
* Amount that actually was added to the wallet's balance.
|
||||
*/
|
||||
amountWithdrawnEffective: string;
|
||||
|
||||
/**
|
||||
* Verbose details of the operations, only generated when requested.
|
||||
*/
|
||||
verboseDetails?: VerboseWithdrawDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common fields that all history events need to have.
|
||||
*/
|
||||
export interface HistoryEventBase {
|
||||
type: HistoryEventType;
|
||||
|
||||
/**
|
||||
* Main timestamp of the history event.
|
||||
*/
|
||||
timestamp: Timestamp;
|
||||
|
||||
/**
|
||||
* Opaque unique ID for the event, used as a starting point
|
||||
* for paginating history queries and for invoking actions
|
||||
* on the event (e.g. hiding it from the history).
|
||||
*/
|
||||
eventId: string;
|
||||
|
||||
/**
|
||||
* Extra details for debugging.
|
||||
*/
|
||||
verboseDetails?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Union of all history detail types, discriminated by their type.
|
||||
*/
|
||||
export type HistoryEvent = HistoryEventBase &
|
||||
(
|
||||
| HistoryAuditorComplaintSentEvent
|
||||
| HistoryAuditorComplaintProcessedEvent
|
||||
| HistoryAuditorTrustAddedEvent
|
||||
| HistoryAuditorTrustRemovedEvent
|
||||
| HistoryExchangeAddedEvent
|
||||
| HistoryExchangeTermsAcceptedEvent
|
||||
| HistoryExchangePolicyChangedEvent
|
||||
| HistoryExchangeTrustAddedEvent
|
||||
| HistoryExchangeTrustRemovedEvent
|
||||
| HistoryExchangeUpdatedEvent
|
||||
| HistoryFundsDepositedToSelfEvent
|
||||
| HistoryFundsRecoupedEvent
|
||||
| HistoryOrderAcceptedEvent
|
||||
| HistoryOrderRedirectedEvent
|
||||
| HistoryOrderRefusedEvent
|
||||
| HistoryPaymentAbortedEvent
|
||||
| HistoryPaymentSent
|
||||
| HistoryRefreshedEvent
|
||||
| HistoryRefunded
|
||||
| HistoryReserveBalanceUpdatedEvent
|
||||
| HistoryReserveCreatedEvent
|
||||
| HistoryTipAcceptedEvent
|
||||
| HistoryTipDeclinedEvent
|
||||
| HistoryWithdrawnEvent
|
||||
);
|
||||
|
||||
export interface HistoryQuery {
|
||||
/**
|
||||
* Output extra verbose details, intended for debugging
|
||||
* and not for end users.
|
||||
*/
|
||||
extraDebug?: boolean;
|
||||
}
|
@ -93,7 +93,6 @@ import { InternalWalletState } from "./operations/state";
|
||||
import { createReserve } from "./operations/reserves";
|
||||
import { processRefreshGroup, createRefreshGroup } from "./operations/refresh";
|
||||
import { processWithdrawGroup } from "./operations/withdraw";
|
||||
import { getHistory } from "./operations/history";
|
||||
import { getPendingOperations } from "./operations/pending";
|
||||
import { getBalances } from "./operations/balance";
|
||||
import { acceptTip, getTipStatus, processTip } from "./operations/tip";
|
||||
@ -106,7 +105,6 @@ import {
|
||||
PendingOperationType,
|
||||
} from "./types/pending";
|
||||
import { WalletNotification, NotificationType } from "./types/notifications";
|
||||
import { HistoryQuery, HistoryEvent } from "./types/history";
|
||||
import { processPurchaseQueryRefund, applyRefund } from "./operations/refund";
|
||||
import { durationMin, Duration } from "./util/time";
|
||||
import { processRecoupGroup } from "./operations/recoup";
|
||||
@ -558,9 +556,9 @@ export class Wallet {
|
||||
* Retrive the full event history for this wallet.
|
||||
*/
|
||||
async getHistory(
|
||||
historyQuery?: HistoryQuery,
|
||||
): Promise<{ history: HistoryEvent[] }> {
|
||||
return getHistory(this.ws, historyQuery);
|
||||
historyQuery?: any[],
|
||||
): Promise<{ history: any[] }> {
|
||||
return { history: [] };
|
||||
}
|
||||
|
||||
async getPendingOperations({ onlyDue = false } = {}): Promise<
|
||||
|
Loading…
Reference in New Issue
Block a user