-re-add missing fields, fix types

This commit is contained in:
Florian Dold 2023-04-06 12:47:34 +02:00
parent 3cf6d15eae
commit 43ae414a55
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
10 changed files with 168 additions and 29 deletions
packages
taler-harness/src
taler-util/src
taler-wallet-core/src/operations
taler-wallet-webextension/src

View File

@ -2097,7 +2097,7 @@ export interface WalletClientArgs {
export class WalletClient { export class WalletClient {
remoteWallet: RemoteWallet | undefined = undefined; remoteWallet: RemoteWallet | undefined = undefined;
waiter: WalletNotificationWaiter = makeNotificationWaiter(); private waiter: WalletNotificationWaiter = makeNotificationWaiter();
constructor(private args: WalletClientArgs) {} constructor(private args: WalletClientArgs) {}

View File

@ -99,6 +99,8 @@ export interface EnvOptions {
/** /**
* Run a test case with a simple TESTKUDOS Taler environment, consisting * Run a test case with a simple TESTKUDOS Taler environment, consisting
* of one exchange, one bank and one merchant. * of one exchange, one bank and one merchant.
*
* @deprecated use {@link createSimpleTestkudosEnvironmentV2} instead
*/ */
export async function createSimpleTestkudosEnvironment( export async function createSimpleTestkudosEnvironment(
t: GlobalTestState, t: GlobalTestState,
@ -505,6 +507,11 @@ export interface WithdrawViaBankResult {
withdrawalFinishedCond: Promise<WithdrawalGroupFinishedNotification>; withdrawalFinishedCond: Promise<WithdrawalGroupFinishedNotification>;
} }
/**
* Withdraw via a bank with the testing API enabled.
* Uses the new notification-based mechanism to wait for the
* operation to finish.
*/
export async function withdrawViaBankV2( export async function withdrawViaBankV2(
t: GlobalTestState, t: GlobalTestState,
p: { p: {
@ -550,6 +557,8 @@ export async function withdrawViaBankV2(
/** /**
* Withdraw balance. * Withdraw balance.
*
* @deprecated use {@link withdrawViaBankV2 instead}
*/ */
export async function withdrawViaBank( export async function withdrawViaBank(
t: GlobalTestState, t: GlobalTestState,

View File

@ -17,11 +17,12 @@
/** /**
* Imports. * Imports.
*/ */
import { NotificationType, TransactionState } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, getPayto } from "../harness/harness.js"; import { GlobalTestState, getPayto } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
/** /**
@ -30,16 +31,27 @@ import {
export async function runDepositTest(t: GlobalTestState) { export async function runDepositTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); const withdrawalResult = await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
await wallet.runUntilDone(); await withdrawalResult.withdrawalFinishedCond;
const { depositGroupId } = await wallet.client.call( const depositDone = await walletClient.waitForNotificationCond(
(n) =>
n.type == NotificationType.TransactionStateTransition &&
n.newTxState == TransactionState.Done,
);
const depositGroupResult = await walletClient.client.call(
WalletApiOperation.CreateDepositGroup, WalletApiOperation.CreateDepositGroup,
{ {
amount: "TESTKUDOS:10", amount: "TESTKUDOS:10",
@ -47,9 +59,7 @@ export async function runDepositTest(t: GlobalTestState) {
}, },
); );
await wallet.runUntilDone(); const transactions = await walletClient.client.call(
const transactions = await wallet.client.call(
WalletApiOperation.GetTransactions, WalletApiOperation.GetTransactions,
{}, {},
); );

View File

@ -22,6 +22,7 @@
/** /**
* Imports. * Imports.
*/ */
import { TransactionState, TransactionSubstate } from "./transactions-types.js";
import { TalerErrorDetail } from "./wallet-types.js"; import { TalerErrorDetail } from "./wallet-types.js";
export enum NotificationType { export enum NotificationType {
@ -67,6 +68,16 @@ export enum NotificationType {
WithdrawalGroupReserveReady = "withdrawal-group-reserve-ready", WithdrawalGroupReserveReady = "withdrawal-group-reserve-ready",
PeerPullCreditReady = "peer-pull-credit-ready", PeerPullCreditReady = "peer-pull-credit-ready",
DepositOperationError = "deposit-operation-error", DepositOperationError = "deposit-operation-error",
TransactionStateTransition = "transaction-state-transition",
}
export interface TransactionStateTransitionNotification {
type: NotificationType.TransactionStateTransition;
transactionId: string;
oldTxState: TransactionState;
oldTxSubstate: TransactionSubstate;
newTxState: TransactionState;
newTxSubstate: TransactionSubstate;
} }
export interface ProposalAcceptedNotification { export interface ProposalAcceptedNotification {
@ -327,4 +338,5 @@ export type WalletNotification =
| KycRequestedNotification | KycRequestedNotification
| WithdrawalGroupBankConfirmed | WithdrawalGroupBankConfirmed
| WithdrawalGroupReserveReadyNotification | WithdrawalGroupReserveReadyNotification
| PeerPullCreditReadyNotification; | PeerPullCreditReadyNotification
| TransactionStateTransitionNotification;

View File

@ -1722,6 +1722,7 @@ export const codecForPrepareDepositRequest = (): Codec<PrepareDepositRequest> =>
export interface PrepareDepositResponse { export interface PrepareDepositResponse {
totalDepositCost: AmountString; totalDepositCost: AmountString;
effectiveDepositAmount: AmountString; effectiveDepositAmount: AmountString;
fees: DepositGroupFees;
} }
export const codecForCreateDepositGroupRequest = export const codecForCreateDepositGroupRequest =

View File

@ -57,12 +57,13 @@ import {
WireFee, WireFee,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
DenominationRecord,
DepositGroupRecord, DepositGroupRecord,
OperationStatus, OperationStatus,
TransactionStatus, TransactionStatus,
} from "../db.js"; } from "../db.js";
import { TalerError } from "@gnu-taler/taler-util"; import { TalerError } from "@gnu-taler/taler-util";
import { KycPendingInfo, KycUserType } from "../index.js"; import { getTotalRefreshCost, KycPendingInfo, KycUserType } from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { OperationAttemptResult } from "../util/retries.js"; import { OperationAttemptResult } from "../util/retries.js";
@ -556,9 +557,17 @@ export async function prepareDepositGroup(
payCoinSel.coinSel, payCoinSel.coinSel,
); );
const fees = await getTotalFeesForDepositAmount(
ws,
p.targetType,
amount,
payCoinSel.coinSel,
);
return { return {
totalDepositCost: Amounts.stringify(totalDepositCost), totalDepositCost: Amounts.stringify(totalDepositCost),
effectiveDepositAmount: Amounts.stringify(effectiveDepositAmount), effectiveDepositAmount: Amounts.stringify(effectiveDepositAmount),
fees,
}; };
} }
@ -774,3 +783,83 @@ export async function getCounterpartyEffectiveDepositAmount(
}); });
return Amounts.sub(Amounts.sum(amt).amount, Amounts.sum(fees).amount).amount; return Amounts.sub(Amounts.sum(amt).amount, Amounts.sum(fees).amount).amount;
} }
/**
* Get the fee amount that will be charged when trying to deposit the
* specified amount using the selected coins and the wire method.
*/
export async function getTotalFeesForDepositAmount(
ws: InternalWalletState,
wireType: string,
total: AmountJson,
pcs: PayCoinSelection,
): Promise<DepositGroupFees> {
const wireFee: AmountJson[] = [];
const coinFee: AmountJson[] = [];
const refreshFee: AmountJson[] = [];
const exchangeSet: Set<string> = new Set();
await ws.db
.mktx((x) => [x.coins, x.denominations, x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
for (let i = 0; i < pcs.coinPubs.length; i++) {
const coin = await tx.coins.get(pcs.coinPubs[i]);
if (!coin) {
throw Error("can't calculate deposit amount, coin not found");
}
const denom = await ws.getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
coin.denomPubHash,
);
if (!denom) {
throw Error("can't find denomination to calculate deposit amount");
}
coinFee.push(Amounts.parseOrThrow(denom.feeDeposit));
exchangeSet.add(coin.exchangeBaseUrl);
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
.iter(coin.exchangeBaseUrl)
.filter((x) =>
Amounts.isSameCurrency(
DenominationRecord.getValue(x),
pcs.coinContributions[i],
),
);
const amountLeft = Amounts.sub(
denom.value,
pcs.coinContributions[i],
).amount;
const refreshCost = getTotalRefreshCost(allDenoms, denom, amountLeft);
refreshFee.push(refreshCost);
}
for (const exchangeUrl of exchangeSet.values()) {
const exchangeDetails = await getExchangeDetails(tx, exchangeUrl);
if (!exchangeDetails) {
continue;
}
const fee = exchangeDetails.wireInfo.feesForType[wireType]?.find(
(x) => {
return AbsoluteTime.isBetween(
AbsoluteTime.now(),
AbsoluteTime.fromTimestamp(x.startStamp),
AbsoluteTime.fromTimestamp(x.endStamp),
);
},
)?.wireFee;
if (fee) {
wireFee.push(Amounts.parseOrThrow(fee));
}
}
});
return {
coin: Amounts.stringify(Amounts.sumOrZero(total.currency, coinFee).amount),
wire: Amounts.stringify(Amounts.sumOrZero(total.currency, wireFee).amount),
refresh: Amounts.stringify(
Amounts.sumOrZero(total.currency, refreshFee).amount,
),
};
}

View File

@ -74,6 +74,11 @@ describe("Deposit CTA states", () => {
{ {
effectiveDepositAmount: "EUR:1", effectiveDepositAmount: "EUR:1",
totalDepositCost: "EUR:1.2", totalDepositCost: "EUR:1.2",
fees: {
coin: "EUR:0",
refresh: "EUR:0.2",
wire: "EUR:0",
},
}, },
); );

View File

@ -151,7 +151,7 @@ export function useComponentState({
// eslint-disable-next-line react-hooks/rules-of-hooks // eslint-disable-next-line react-hooks/rules-of-hooks
const hook = useAsyncAsHook(async () => { const hook = useAsyncAsHook(async () => {
const fee = await api.wallet.call(WalletApiOperation.GetFeeForDeposit, { const fee = await api.wallet.call(WalletApiOperation.PrepareDeposit, {
amount: amountStr, amount: amountStr,
depositPaytoUri, depositPaytoUri,
}); });
@ -181,7 +181,7 @@ export function useComponentState({
const totalFee = const totalFee =
fee !== undefined fee !== undefined
? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount ? Amounts.sum([fee.fees.wire, fee.fees.coin, fee.fees.refresh]).amount
: Amounts.zeroOfCurrency(currency); : Amounts.zeroOfCurrency(currency);
const totalToDeposit = const totalToDeposit =

View File

@ -23,6 +23,7 @@ import {
Amounts, Amounts,
DepositGroupFees, DepositGroupFees,
parsePaytoUri, parsePaytoUri,
PrepareDepositResponse,
ScopeType, ScopeType,
stringifyPaytoUri, stringifyPaytoUri,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
@ -36,16 +37,24 @@ import { useComponentState } from "./state.js";
const currency = "EUR"; const currency = "EUR";
const amount = `${currency}:0`; const amount = `${currency}:0`;
const withoutFee = (): DepositGroupFees => ({ const withoutFee = (): PrepareDepositResponse => ({
effectiveDepositAmount: `${currency}:5`,
totalDepositCost: `${currency}:5`,
fees: {
coin: Amounts.stringify(`${currency}:0`), coin: Amounts.stringify(`${currency}:0`),
wire: Amounts.stringify(`${currency}:0`), wire: Amounts.stringify(`${currency}:0`),
refresh: Amounts.stringify(`${currency}:0`), refresh: Amounts.stringify(`${currency}:0`),
},
}); });
const withSomeFee = (): DepositGroupFees => ({ const withSomeFee = (): PrepareDepositResponse => ({
effectiveDepositAmount: `${currency}:5`,
totalDepositCost: `${currency}:5`,
fees: {
coin: Amounts.stringify(`${currency}:1`), coin: Amounts.stringify(`${currency}:1`),
wire: Amounts.stringify(`${currency}:1`), wire: Amounts.stringify(`${currency}:1`),
refresh: Amounts.stringify(`${currency}:1`), refresh: Amounts.stringify(`${currency}:1`),
},
}); });
describe("DepositPage states", () => { describe("DepositPage states", () => {
@ -182,7 +191,7 @@ describe("DepositPage states", () => {
}, },
); );
handler.addWalletCallResponse( handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit, WalletApiOperation.PrepareDeposit,
undefined, undefined,
withoutFee(), withoutFee(),
); );
@ -241,13 +250,13 @@ describe("DepositPage states", () => {
}, },
); );
handler.addWalletCallResponse( handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit, WalletApiOperation.PrepareDeposit,
undefined, undefined,
withoutFee(), withoutFee(),
); );
handler.addWalletCallResponse( handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit, WalletApiOperation.PrepareDeposit,
undefined, undefined,
withoutFee(), withoutFee(),
); );
@ -330,17 +339,17 @@ describe("DepositPage states", () => {
}, },
); );
handler.addWalletCallResponse( handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit, WalletApiOperation.PrepareDeposit,
undefined, undefined,
withoutFee(), withoutFee(),
); );
handler.addWalletCallResponse( handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit, WalletApiOperation.PrepareDeposit,
undefined, undefined,
withSomeFee(), withSomeFee(),
); );
handler.addWalletCallResponse( handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit, WalletApiOperation.PrepareDeposit,
undefined, undefined,
withSomeFee(), withSomeFee(),
); );

View File

@ -34,6 +34,8 @@ import {
TransactionPeerPushDebit, TransactionPeerPushDebit,
TransactionRefresh, TransactionRefresh,
TransactionRefund, TransactionRefund,
TransactionState,
TransactionSubstate,
TransactionTip, TransactionTip,
TransactionType, TransactionType,
TransactionWithdrawal, TransactionWithdrawal,
@ -68,6 +70,8 @@ const commonTransaction = {
transactionId: "txn:deposit:12", transactionId: "txn:deposit:12",
frozen: undefined as any as boolean, //deprecated frozen: undefined as any as boolean, //deprecated
type: TransactionType.Deposit, type: TransactionType.Deposit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.None,
} as TransactionCommon; } as TransactionCommon;
import merchantIcon from "../../static-dev/merchant-icon.jpeg"; import merchantIcon from "../../static-dev/merchant-icon.jpeg";