taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

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:
Mpackages/taler-wallet-core/src/exchanges.ts | 3+++
Mpackages/taler-wallet-core/src/pay-peer-common.ts | 9+++++++++
Mpackages/taler-wallet-core/src/pay-peer-push-debit.ts | 4++++
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 {