commit 40678e819eb909d6856da8a3d07942c44ff1d33c
parent 57fe4ca6d3758fcc85fd579e0c558ff4c6b105de
Author: Florian Dold <florian@dold.me>
Date: Mon, 22 Dec 2025 13:06:58 +0100
wallet-core: fix wrong estimate of peer-push-debit fees
The wallet lazily validates denominations. In the request for computing
the effective total cost of a P2P push payment, we did not properly
ensure that denominations which might be used in the refresh step are
validated. Thus wallet-core would sometimes over-estimate the fees by
assuming a 100 percent refresh fee due to missing validated
denominations for withdrawal.
Diffstat:
3 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
@@ -1422,6 +1422,9 @@ export async function fetchFreshExchange(
},
);
+ // FIXME: We should only transition here when
+ // the update is forced or necessary!
+
await wex.taskScheduler.ensureRunning();
await startUpdateExchangeEntry(wex, baseUrl, {
diff --git a/packages/taler-wallet-core/src/pay-peer-common.ts b/packages/taler-wallet-core/src/pay-peer-common.ts
@@ -44,6 +44,7 @@ import {
import { getTotalRefreshCost } from "./refresh.js";
import { BalanceEffect, applyNotifyTransition } from "./transactions.js";
import { WalletExecutionContext, getDenomInfo } from "./wallet.js";
+import { updateWithdrawalDenomsForExchange } from "./withdraw.js";
/**
* Get information about the coin selected for signatures.
@@ -121,6 +122,14 @@ export async function getTotalPeerPaymentCost(
wex: WalletExecutionContext,
pcs: SelectedProspectiveCoin[],
): Promise<AmountJson> {
+ // We first need to make sure we have already validated
+ // potential target denomination for the refresh.
+ // This must happen *before* we start the transaction.
+ if (pcs.length > 0) {
+ // P2P payments currently support only one exchange.
+ const exchangeBaseUrl = pcs[0].exchangeBaseUrl;
+ await updateWithdrawalDenomsForExchange(wex, exchangeBaseUrl);
+ }
return wex.db.runReadOnlyTx(
{ storeNames: ["coins", "denominations"] },
async (tx) => {
diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
@@ -81,6 +81,7 @@ import {
timestampProtocolToDb,
} from "./db.js";
import {
+ fetchFreshExchange,
getPreferredExchangeForCurrency,
getScopeForAllExchanges,
} from "./exchanges.js";
@@ -441,6 +442,9 @@ async function internalCheckPeerPushDebit(
assertUnreachable(coinSelRes);
}
logger.trace(`selected peer coins (len=${coins.length})`);
+ // We might need to do a refresh later, so it's important
+ // that we have up-to-date info about the exchange.
+ await fetchFreshExchange(wex, coinSelRes.result.exchangeBaseUrl);
const totalAmount = await getTotalPeerPaymentCost(wex, coins);
logger.trace("computed total peer payment cost");
return {