sunset history API (we have the transaction list now)

This commit is contained in:
Florian Dold 2020-07-23 17:38:10 +05:30
parent d88829cfa8
commit 60891b502a
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 3 additions and 1215 deletions

View File

@ -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 };
}

View File

@ -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;
}

View File

@ -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<