summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/taler-harness/src/harness/harness.ts2
-rw-r--r--packages/taler-harness/src/harness/helpers.ts9
-rw-r--r--packages/taler-harness/src/integrationtests/test-deposit.ts30
-rw-r--r--packages/taler-util/src/notifications.ts14
-rw-r--r--packages/taler-util/src/wallet-types.ts1
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts91
-rw-r--r--packages/taler-wallet-webextension/src/cta/Deposit/test.ts5
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts4
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts37
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx4
10 files changed, 168 insertions, 29 deletions
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts
index 5733e776b..840149e7c 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -2097,7 +2097,7 @@ export interface WalletClientArgs {
export class WalletClient {
remoteWallet: RemoteWallet | undefined = undefined;
- waiter: WalletNotificationWaiter = makeNotificationWaiter();
+ private waiter: WalletNotificationWaiter = makeNotificationWaiter();
constructor(private args: WalletClientArgs) {}
diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts
index d203cc608..516312ed8 100644
--- a/packages/taler-harness/src/harness/helpers.ts
+++ b/packages/taler-harness/src/harness/helpers.ts
@@ -99,6 +99,8 @@ export interface EnvOptions {
/**
* Run a test case with a simple TESTKUDOS Taler environment, consisting
* of one exchange, one bank and one merchant.
+ *
+ * @deprecated use {@link createSimpleTestkudosEnvironmentV2} instead
*/
export async function createSimpleTestkudosEnvironment(
t: GlobalTestState,
@@ -505,6 +507,11 @@ export interface WithdrawViaBankResult {
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(
t: GlobalTestState,
p: {
@@ -550,6 +557,8 @@ export async function withdrawViaBankV2(
/**
* Withdraw balance.
+ *
+ * @deprecated use {@link withdrawViaBankV2 instead}
*/
export async function withdrawViaBank(
t: GlobalTestState,
diff --git a/packages/taler-harness/src/integrationtests/test-deposit.ts b/packages/taler-harness/src/integrationtests/test-deposit.ts
index 24dcbafc0..1b46daf5f 100644
--- a/packages/taler-harness/src/integrationtests/test-deposit.ts
+++ b/packages/taler-harness/src/integrationtests/test-deposit.ts
@@ -17,11 +17,12 @@
/**
* Imports.
*/
+import { NotificationType, TransactionState } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, getPayto } from "../harness/harness.js";
import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
+ createSimpleTestkudosEnvironmentV2,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
/**
@@ -30,16 +31,27 @@ import {
export async function runDepositTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, exchange, merchant } =
- await createSimpleTestkudosEnvironment(t);
+ const { walletClient, bank, exchange } =
+ await createSimpleTestkudosEnvironmentV2(t);
// 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,
{
amount: "TESTKUDOS:10",
@@ -47,9 +59,7 @@ export async function runDepositTest(t: GlobalTestState) {
},
);
- await wallet.runUntilDone();
-
- const transactions = await wallet.client.call(
+ const transactions = await walletClient.client.call(
WalletApiOperation.GetTransactions,
{},
);
diff --git a/packages/taler-util/src/notifications.ts b/packages/taler-util/src/notifications.ts
index 0d85c85e9..ff1017cd1 100644
--- a/packages/taler-util/src/notifications.ts
+++ b/packages/taler-util/src/notifications.ts
@@ -22,6 +22,7 @@
/**
* Imports.
*/
+import { TransactionState, TransactionSubstate } from "./transactions-types.js";
import { TalerErrorDetail } from "./wallet-types.js";
export enum NotificationType {
@@ -67,6 +68,16 @@ export enum NotificationType {
WithdrawalGroupReserveReady = "withdrawal-group-reserve-ready",
PeerPullCreditReady = "peer-pull-credit-ready",
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 {
@@ -327,4 +338,5 @@ export type WalletNotification =
| KycRequestedNotification
| WithdrawalGroupBankConfirmed
| WithdrawalGroupReserveReadyNotification
- | PeerPullCreditReadyNotification;
+ | PeerPullCreditReadyNotification
+ | TransactionStateTransitionNotification;
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index dea6bd361..c75ca7fdd 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -1722,6 +1722,7 @@ export const codecForPrepareDepositRequest = (): Codec<PrepareDepositRequest> =>
export interface PrepareDepositResponse {
totalDepositCost: AmountString;
effectiveDepositAmount: AmountString;
+ fees: DepositGroupFees;
}
export const codecForCreateDepositGroupRequest =
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index 660fb8816..9abec89bf 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -57,12 +57,13 @@ import {
WireFee,
} from "@gnu-taler/taler-util";
import {
+ DenominationRecord,
DepositGroupRecord,
OperationStatus,
TransactionStatus,
} from "../db.js";
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 { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { OperationAttemptResult } from "../util/retries.js";
@@ -556,9 +557,17 @@ export async function prepareDepositGroup(
payCoinSel.coinSel,
);
+ const fees = await getTotalFeesForDepositAmount(
+ ws,
+ p.targetType,
+ amount,
+ payCoinSel.coinSel,
+ );
+
return {
totalDepositCost: Amounts.stringify(totalDepositCost),
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;
}
+
+/**
+ * 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,
+ ),
+ };
+}
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
index b9fbc3638..a7d24eeb8 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
@@ -74,6 +74,11 @@ describe("Deposit CTA states", () => {
{
effectiveDepositAmount: "EUR:1",
totalDepositCost: "EUR:1.2",
+ fees: {
+ coin: "EUR:0",
+ refresh: "EUR:0.2",
+ wire: "EUR:0",
+ },
},
);
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index b744b80e5..42a3ba847 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -151,7 +151,7 @@ export function useComponentState({
// eslint-disable-next-line react-hooks/rules-of-hooks
const hook = useAsyncAsHook(async () => {
- const fee = await api.wallet.call(WalletApiOperation.GetFeeForDeposit, {
+ const fee = await api.wallet.call(WalletApiOperation.PrepareDeposit, {
amount: amountStr,
depositPaytoUri,
});
@@ -181,7 +181,7 @@ export function useComponentState({
const totalFee =
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);
const totalToDeposit =
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 1489e2bb9..a06b1ae75 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -23,6 +23,7 @@ import {
Amounts,
DepositGroupFees,
parsePaytoUri,
+ PrepareDepositResponse,
ScopeType,
stringifyPaytoUri,
} from "@gnu-taler/taler-util";
@@ -36,16 +37,24 @@ import { useComponentState } from "./state.js";
const currency = "EUR";
const amount = `${currency}:0`;
-const withoutFee = (): DepositGroupFees => ({
- coin: Amounts.stringify(`${currency}:0`),
- wire: Amounts.stringify(`${currency}:0`),
- refresh: Amounts.stringify(`${currency}:0`),
+const withoutFee = (): PrepareDepositResponse => ({
+ effectiveDepositAmount: `${currency}:5`,
+ totalDepositCost: `${currency}:5`,
+ fees: {
+ coin: Amounts.stringify(`${currency}:0`),
+ wire: Amounts.stringify(`${currency}:0`),
+ refresh: Amounts.stringify(`${currency}:0`),
+ },
});
-const withSomeFee = (): DepositGroupFees => ({
- coin: Amounts.stringify(`${currency}:1`),
- wire: Amounts.stringify(`${currency}:1`),
- refresh: Amounts.stringify(`${currency}:1`),
+const withSomeFee = (): PrepareDepositResponse => ({
+ effectiveDepositAmount: `${currency}:5`,
+ totalDepositCost: `${currency}:5`,
+ fees: {
+ coin: Amounts.stringify(`${currency}:1`),
+ wire: Amounts.stringify(`${currency}:1`),
+ refresh: Amounts.stringify(`${currency}:1`),
+ },
});
describe("DepositPage states", () => {
@@ -182,7 +191,7 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
- WalletApiOperation.GetFeeForDeposit,
+ WalletApiOperation.PrepareDeposit,
undefined,
withoutFee(),
);
@@ -241,13 +250,13 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
- WalletApiOperation.GetFeeForDeposit,
+ WalletApiOperation.PrepareDeposit,
undefined,
withoutFee(),
);
handler.addWalletCallResponse(
- WalletApiOperation.GetFeeForDeposit,
+ WalletApiOperation.PrepareDeposit,
undefined,
withoutFee(),
);
@@ -330,17 +339,17 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
- WalletApiOperation.GetFeeForDeposit,
+ WalletApiOperation.PrepareDeposit,
undefined,
withoutFee(),
);
handler.addWalletCallResponse(
- WalletApiOperation.GetFeeForDeposit,
+ WalletApiOperation.PrepareDeposit,
undefined,
withSomeFee(),
);
handler.addWalletCallResponse(
- WalletApiOperation.GetFeeForDeposit,
+ WalletApiOperation.PrepareDeposit,
undefined,
withSomeFee(),
);
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
index d338b77f5..bf59573ec 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
@@ -34,6 +34,8 @@ import {
TransactionPeerPushDebit,
TransactionRefresh,
TransactionRefund,
+ TransactionState,
+ TransactionSubstate,
TransactionTip,
TransactionType,
TransactionWithdrawal,
@@ -68,6 +70,8 @@ const commonTransaction = {
transactionId: "txn:deposit:12",
frozen: undefined as any as boolean, //deprecated
type: TransactionType.Deposit,
+ txState: TransactionState.Unknown,
+ txSubstate: TransactionSubstate.None,
} as TransactionCommon;
import merchantIcon from "../../static-dev/merchant-icon.jpeg";