diff options
Diffstat (limited to 'packages/taler-wallet-core')
| -rw-r--r-- | packages/taler-wallet-core/src/db.ts | 14 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/backup/import.ts | 1 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/pay.ts | 4 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/recoup.ts | 48 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/refresh.ts | 3 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/reserves.ts | 87 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/tip.ts | 1 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/withdraw.ts | 32 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/util/coinSelection.ts | 2 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 126 | 
10 files changed, 194 insertions, 124 deletions
| diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index ac28d0979..2e76ab523 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -597,9 +597,6 @@ export interface PlanchetRecord {    denomPubHash: string; -  // FIXME: maybe too redundant? -  denomPub: DenominationPubKey; -    blindingKey: string;    withdrawSig: string; @@ -607,10 +604,6 @@ export interface PlanchetRecord {    coinEv: CoinEnvelope;    coinEvHash: string; - -  coinValue: AmountJson; - -  isFromTip: boolean;  }  /** @@ -686,11 +679,6 @@ export interface CoinRecord {    coinPriv: string;    /** -   * Key used by the exchange used to sign the coin. -   */ -  denomPub: DenominationPubKey; - -  /**     * Hash of the public key that signs the coin.     */    denomPubHash: string; @@ -1378,6 +1366,8 @@ export interface WithdrawalGroupRecord {    /**     * When was the withdrawal operation completed? +   * +   * FIXME: We should probably drop this and introduce an OperationStatus field.     */    timestampFinish?: Timestamp; diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 84acfb16c..35b62c2e4 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -419,7 +419,6 @@ export async function importBackup(                  coinPub: compCoin.coinPub,                  suspended: false,                  exchangeBaseUrl: backupExchangeDetails.base_url, -                denomPub: backupDenomination.denom_pub,                  denomPubHash,                  status: backupCoin.fresh                    ? CoinStatus.Fresh diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts index 97d87e5cc..6001cac4f 100644 --- a/packages/taler-wallet-core/src/operations/pay.ts +++ b/packages/taler-wallet-core/src/operations/pay.ts @@ -315,7 +315,7 @@ export async function getCandidatePayCoins(            candidateCoins.push({              availableAmount: coin.currentAmount,              coinPub: coin.coinPub, -            denomPub: coin.denomPub, +            denomPub: denom.denomPub,              feeDeposit: denom.feeDeposit,              exchangeBaseUrl: denom.exchangeBaseUrl,            }); @@ -1397,7 +1397,7 @@ export async function generateDepositPermissions(        coinPub: coin.coinPub,        contractTermsHash: contractData.contractTermsHash,        denomPubHash: coin.denomPubHash, -      denomKeyType: coin.denomPub.cipher, +      denomKeyType: denom.denomPub.cipher,        denomSig: coin.denomSig,        exchangeBaseUrl: coin.exchangeBaseUrl,        feeDeposit: denom.feeDeposit, diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts index afca923bd..23d14f212 100644 --- a/packages/taler-wallet-core/src/operations/recoup.ts +++ b/packages/taler-wallet-core/src/operations/recoup.ts @@ -164,18 +164,34 @@ async function recoupWithdrawCoin(    cs: WithdrawCoinSource,  ): Promise<void> {    const reservePub = cs.reservePub; -  const reserve = await ws.db +  const d = await ws.db      .mktx((x) => ({        reserves: x.reserves, +      denominations: x.denominations,      }))      .runReadOnly(async (tx) => { -      return tx.reserves.get(reservePub); +      const reserve = await tx.reserves.get(reservePub); +      if (!reserve) { +        return; +      } +      const denomInfo = await ws.getDenomInfo( +        ws, +        tx, +        reserve.exchangeBaseUrl, +        coin.denomPubHash, +      ); +      if (!denomInfo) { +        return; +      } +      return { reserve, denomInfo };      }); -  if (!reserve) { +  if (!d) {      // FIXME:  We should at least emit some pending operation / warning for this?      return;    } +  const { reserve, denomInfo } = d; +    ws.notify({      type: NotificationType.RecoupStarted,    }); @@ -184,7 +200,7 @@ async function recoupWithdrawCoin(      blindingKey: coin.blindingKey,      coinPriv: coin.coinPriv,      coinPub: coin.coinPub, -    denomPub: coin.denomPub, +    denomPub: denomInfo.denomPub,      denomPubHash: coin.denomPubHash,      denomSig: coin.denomSig,    }); @@ -253,6 +269,28 @@ async function recoupRefreshCoin(    coin: CoinRecord,    cs: RefreshCoinSource,  ): Promise<void> { +  const d = await ws.db +    .mktx((x) => ({ +      coins: x.coins, +      denominations: x.denominations, +    })) +    .runReadOnly(async (tx) => { +      const denomInfo = await ws.getDenomInfo( +        ws, +        tx, +        coin.exchangeBaseUrl, +        coin.denomPubHash, +      ); +      if (!denomInfo) { +        return; +      } +      return { denomInfo }; +    }); +  if (!d) { +    // FIXME:  We should at least emit some pending operation / warning for this? +    return; +  } +    ws.notify({      type: NotificationType.RecoupStarted,    }); @@ -261,7 +299,7 @@ async function recoupRefreshCoin(      blindingKey: coin.blindingKey,      coinPriv: coin.coinPriv,      coinPub: coin.coinPub, -    denomPub: coin.denomPub, +    denomPub: d.denomInfo.denomPub,      denomPubHash: coin.denomPubHash,      denomSig: coin.denomSig,    }); diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index ba4cb697d..550119de1 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -395,7 +395,7 @@ async function refreshMelt(      oldCoin.exchangeBaseUrl,    );    let meltReqBody: any; -  if (oldCoin.denomPub.cipher === DenomKeyType.Rsa) { +  if (oldDenom.denomPub.cipher === DenomKeyType.Rsa) {      meltReqBody = {        coin_pub: oldCoin.coinPub,        confirm_sig: derived.confirmSig, @@ -671,7 +671,6 @@ async function refreshReveal(          coinPriv: pc.coinPriv,          coinPub: pc.coinPub,          currentAmount: denom.value, -        denomPub: denom.denomPub,          denomPubHash: denom.denomPubHash,          denomSig: {            cipher: DenomKeyType.Rsa, diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index 7011b2f26..a16d3ec31 100644 --- a/packages/taler-wallet-core/src/operations/reserves.ts +++ b/packages/taler-wallet-core/src/operations/reserves.ts @@ -587,8 +587,8 @@ async function updateReserve(    logger.trace(`got reserve status ${j2s(result.response)}`);    const reserveInfo = result.response; -  const balance = Amounts.parseOrThrow(reserveInfo.balance); -  const currency = balance.currency; +  const reserveBalance = Amounts.parseOrThrow(reserveInfo.balance); +  const currency = reserveBalance.currency;    await updateWithdrawalDenoms(ws, reserve.exchangeBaseUrl);    const denoms = await getCandidateWithdrawalDenoms( @@ -598,73 +598,50 @@ async function updateReserve(    const newWithdrawalGroup = await ws.db      .mktx((x) => ({ -      coins: x.coins,        planchets: x.planchets,        withdrawalGroups: x.withdrawalGroups,        reserves: x.reserves, +      denominations: x.denominations,      }))      .runReadWrite(async (tx) => {        const newReserve = await tx.reserves.get(reserve.reservePub);        if (!newReserve) {          return;        } -      let amountReservePlus = Amounts.getZero(currency); +      let amountReservePlus = reserveBalance;        let amountReserveMinus = Amounts.getZero(currency); -      // Subtract withdrawal groups for this reserve from the available amount. +      // Subtract amount allocated in unfinished withdrawal groups +      // for this reserve from the available amount.        await tx.withdrawalGroups.indexes.byReservePub          .iter(reservePub) -        .forEach((wg) => { -          const cost = wg.denomsSel.totalWithdrawCost; -          amountReserveMinus = Amounts.add(amountReserveMinus, cost).amount; -        }); - -      for (const entry of reserveInfo.history) { -        switch (entry.type) { -          case ReserveTransactionType.Credit: -            amountReservePlus = Amounts.add( -              amountReservePlus, -              Amounts.parseOrThrow(entry.amount), -            ).amount; -            break; -          case ReserveTransactionType.Recoup: -            amountReservePlus = Amounts.add( -              amountReservePlus, -              Amounts.parseOrThrow(entry.amount), -            ).amount; -            break; -          case ReserveTransactionType.Closing: -            amountReserveMinus = Amounts.add( -              amountReserveMinus, -              Amounts.parseOrThrow(entry.amount), -            ).amount; -            break; -          case ReserveTransactionType.Withdraw: { -            // Now we check if the withdrawal transaction -            // is part of any withdrawal known to this wallet. -            const planchet = await tx.planchets.indexes.byCoinEvHash.get( -              entry.h_coin_envelope, -            ); -            if (planchet) { -              // Amount is already accounted in some withdrawal session -              break; -            } -            const coin = await tx.coins.indexes.byCoinEvHash.get( -              entry.h_coin_envelope, -            ); -            if (coin) { -              // Amount is already accounted in some withdrawal session -              break; -            } -            // Amount has been claimed by some withdrawal we don't know about -            amountReserveMinus = Amounts.add( -              amountReserveMinus, -              Amounts.parseOrThrow(entry.amount), -            ).amount; -            break; +        .forEachAsync(async (wg) => { +          if (wg.timestampFinish) { +            return;            } -        } -      } +          await tx.planchets.indexes.byGroup +            .iter(wg.withdrawalGroupId) +            .forEachAsync(async (pr) => { +              if (pr.withdrawalDone) { +                return; +              } +              const denomInfo = await ws.getDenomInfo( +                ws, +                tx, +                wg.exchangeBaseUrl, +                pr.denomPubHash, +              ); +              if (!denomInfo) { +                logger.error(`no denom info found for ${pr.denomPubHash}`); +                return; +              } +              amountReserveMinus = Amounts.add( +                amountReserveMinus, +                denomInfo.value, +                denomInfo.feeWithdraw, +              ).amount; +            }); +        });        const remainingAmount = Amounts.sub(          amountReservePlus, diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index cc2d71ef4..a2a4e6f49 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -374,7 +374,6 @@ async function processTipImpl(          walletTipId: walletTipId,        },        currentAmount: denom.value, -      denomPub: denom.denomPub,        denomPubHash: denom.denomPubHash,        denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa },        exchangeBaseUrl: tipRecord.exchangeBaseUrl, diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 731e9b3aa..ae3763a02 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -418,10 +418,7 @@ async function processPlanchetGenerate(      coinIdx,      coinPriv: r.coinPriv,      coinPub: r.coinPub, -    coinValue: r.coinValue, -    denomPub: r.denomPub,      denomPubHash: r.denomPubHash, -    isFromTip: false,      reservePub: r.reservePub,      withdrawalDone: false,      withdrawSig: r.withdrawSig, @@ -557,6 +554,7 @@ async function processPlanchetVerifyAndStoreCoin(      .mktx((x) => ({        withdrawalGroups: x.withdrawalGroups,        planchets: x.planchets, +      denominations: x.denominations,      }))      .runReadOnly(async (tx) => {        let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ @@ -570,16 +568,29 @@ async function processPlanchetVerifyAndStoreCoin(          logger.warn("processPlanchet: planchet already withdrawn");          return;        } -      return { planchet, exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl }; +      const denomInfo = await ws.getDenomInfo( +        ws, +        tx, +        withdrawalGroup.exchangeBaseUrl, +        planchet.denomPubHash, +      ); +      if (!denomInfo) { +        return; +      } +      return { +        planchet, +        denomInfo, +        exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl, +      };      });    if (!d) {      return;    } -  const { planchet, exchangeBaseUrl } = d; +  const { planchet, denomInfo } = d; -  const planchetDenomPub = planchet.denomPub; +  const planchetDenomPub = denomInfo.denomPub;    if (planchetDenomPub.cipher !== DenomKeyType.Rsa) {      throw Error(`cipher (${planchetDenomPub.cipher}) not supported`);    } @@ -623,9 +634,9 @@ async function processPlanchetVerifyAndStoreCoin(    }    let denomSig: UnblindedSignature; -  if (planchet.denomPub.cipher === DenomKeyType.Rsa) { +  if (planchetDenomPub.cipher === DenomKeyType.Rsa) {      denomSig = { -      cipher: planchet.denomPub.cipher, +      cipher: planchetDenomPub.cipher,        rsa_signature: denomSigRsa,      };    } else { @@ -636,12 +647,11 @@ async function processPlanchetVerifyAndStoreCoin(      blindingKey: planchet.blindingKey,      coinPriv: planchet.coinPriv,      coinPub: planchet.coinPub, -    currentAmount: planchet.coinValue, -    denomPub: planchet.denomPub, +    currentAmount: denomInfo.value,      denomPubHash: planchet.denomPubHash,      denomSig,      coinEvHash: planchet.coinEvHash, -    exchangeBaseUrl: exchangeBaseUrl, +    exchangeBaseUrl: d.exchangeBaseUrl,      status: CoinStatus.Fresh,      coinSource: {        type: CoinSourceType.Withdraw, diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index 4f8a01d19..e19b58774 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -77,6 +77,8 @@ export interface AvailableCoinInfo {    /**     * Coin's denomination public key. +   *  +   * FIXME: We should only need the denomPubHash here, if at all.     */    denomPub: DenominationPubKey; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index ac0def3c1..329417562 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -24,23 +24,62 @@   */  import {    AcceptManualWithdrawalResult, -  AcceptWithdrawalResponse, AmountJson, Amounts, BalancesResponse, codecForAbortPayWithRefundRequest, +  AcceptWithdrawalResponse, +  AmountJson, +  Amounts, +  BalancesResponse, +  codecForAbortPayWithRefundRequest,    codecForAcceptBankIntegratedWithdrawalRequest,    codecForAcceptExchangeTosRequest,    codecForAcceptManualWithdrawalRequet,    codecForAcceptTipRequest, -  codecForAddExchangeRequest, codecForAny, codecForApplyRefundRequest, +  codecForAddExchangeRequest, +  codecForAny, +  codecForApplyRefundRequest,    codecForConfirmPayRequest, -  codecForCreateDepositGroupRequest, codecForDeleteTransactionRequest, codecForForceRefreshRequest, -  codecForGetExchangeTosRequest, codecForGetExchangeWithdrawalInfo, codecForGetFeeForDeposit, codecForGetWithdrawalDetailsForAmountRequest, -  codecForGetWithdrawalDetailsForUri, codecForImportDbRequest, codecForIntegrationTestArgs, codecForListKnownBankAccounts, codecForPreparePayRequest, -  codecForPrepareTipRequest, codecForRetryTransactionRequest, codecForSetCoinSuspendedRequest, codecForSetWalletDeviceIdRequest, codecForTestPayArgs, -  codecForTrackDepositGroupRequest, codecForTransactionsRequest, codecForWithdrawFakebankRequest, codecForWithdrawTestBalance, CoinDumpJson, CoreApiResponse, durationFromSpec, -  durationMin, ExchangeListItem, -  ExchangesListRespose, getDurationRemaining, GetExchangeTosResult, isTimestampExpired, -  j2s, KnownBankAccounts, Logger, ManualWithdrawalDetails, NotificationType, parsePaytoUri, PaytoUri, RefreshReason, TalerErrorCode, +  codecForCreateDepositGroupRequest, +  codecForDeleteTransactionRequest, +  codecForForceRefreshRequest, +  codecForGetExchangeTosRequest, +  codecForGetExchangeWithdrawalInfo, +  codecForGetFeeForDeposit, +  codecForGetWithdrawalDetailsForAmountRequest, +  codecForGetWithdrawalDetailsForUri, +  codecForImportDbRequest, +  codecForIntegrationTestArgs, +  codecForListKnownBankAccounts, +  codecForPreparePayRequest, +  codecForPrepareTipRequest, +  codecForRetryTransactionRequest, +  codecForSetCoinSuspendedRequest, +  codecForSetWalletDeviceIdRequest, +  codecForTestPayArgs, +  codecForTrackDepositGroupRequest, +  codecForTransactionsRequest, +  codecForWithdrawFakebankRequest, +  codecForWithdrawTestBalance, +  CoinDumpJson, +  CoreApiResponse, +  durationFromSpec, +  durationMin, +  ExchangeListItem, +  ExchangesListRespose, +  getDurationRemaining, +  GetExchangeTosResult, +  isTimestampExpired, +  j2s, +  KnownBankAccounts, +  Logger, +  ManualWithdrawalDetails, +  NotificationType, +  parsePaytoUri, +  PaytoUri, +  RefreshReason, +  TalerErrorCode,    Timestamp, -  timestampMin, URL, WalletNotification +  timestampMin, +  URL, +  WalletNotification,  } from "@gnu-taler/taler-util";  import {    DenomInfo, @@ -50,7 +89,7 @@ import {    MerchantOperations,    NotificationListener,    RecoupOperations, -  ReserveOperations +  ReserveOperations,  } from "./common.js";  import { CryptoApi, CryptoWorkerFactory } from "./crypto/workers/cryptoApi.js";  import { @@ -59,12 +98,12 @@ import {    exportDb,    importDb,    ReserveRecordStatus, -  WalletStoresV1 +  WalletStoresV1,  } from "./db.js";  import {    makeErrorDetails,    OperationFailedAndReportedError, -  OperationFailedError +  OperationFailedError,  } from "./errors.js";  import { exportBackup } from "./operations/backup/export.js";  import { @@ -77,7 +116,7 @@ import {    loadBackupRecovery,    processBackupForProvider,    removeBackupProvider, -  runBackupCycle +  runBackupCycle,  } from "./operations/backup/index.js";  import { setWalletDeviceId } from "./operations/backup/state.js";  import { getBalances } from "./operations/balance.js"; @@ -85,7 +124,7 @@ import {    createDepositGroup,    getFeeForDeposit,    processDepositGroup, -  trackDepositGroup +  trackDepositGroup,  } from "./operations/deposits.js";  import {    acceptExchangeTermsOfService, @@ -94,62 +133,64 @@ import {    getExchangeRequestTimeout,    getExchangeTrust,    updateExchangeFromUrl, -  updateExchangeTermsOfService +  updateExchangeTermsOfService,  } from "./operations/exchanges.js";  import { getMerchantInfo } from "./operations/merchants.js";  import {    confirmPay,    preparePayForUri,    processDownloadProposal, -  processPurchasePay +  processPurchasePay,  } from "./operations/pay.js";  import { getPendingOperations } from "./operations/pending.js";  import { createRecoupGroup, processRecoupGroup } from "./operations/recoup.js";  import {    autoRefresh,    createRefreshGroup, -  processRefreshGroup +  processRefreshGroup,  } from "./operations/refresh.js";  import {    abortFailedPayWithRefund,    applyRefund, -  processPurchaseQueryRefund +  processPurchaseQueryRefund,  } from "./operations/refund.js";  import {    createReserve,    createTalerWithdrawReserve,    getFundingPaytoUris, -  processReserve +  processReserve,  } from "./operations/reserves.js";  import {    runIntegrationTest,    testPay, -  withdrawTestBalance +  withdrawTestBalance,  } from "./operations/testing.js";  import { acceptTip, prepareTip, processTip } from "./operations/tip.js";  import {    deleteTransaction,    getTransactions, -  retryTransaction +  retryTransaction,  } from "./operations/transactions.js";  import {    getExchangeWithdrawalInfo,    getWithdrawalDetailsForUri, -  processWithdrawGroup +  processWithdrawGroup,  } from "./operations/withdraw.js";  import { -  PendingOperationsResponse, PendingTaskInfo, PendingTaskType +  PendingOperationsResponse, +  PendingTaskInfo, +  PendingTaskType,  } from "./pending-types.js";  import { assertUnreachable } from "./util/assertUnreachable.js";  import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";  import {    HttpRequestLibrary, -  readSuccessResponseJsonOrThrow +  readSuccessResponseJsonOrThrow,  } from "./util/http.js";  import {    AsyncCondition,    OpenedPromise, -  openPromise +  openPromise,  } from "./util/promiseUtils.js";  import { DbAccess, GetReadWriteAccess } from "./util/query.js";  import { TimerGroup } from "./util/timer.js"; @@ -455,7 +496,10 @@ async function getExchangeTos(    ) {      throw Error("exchange is in invalid state");    } -  if (acceptedFormat && acceptedFormat.findIndex(f => f === contentType) !== -1) { +  if ( +    acceptedFormat && +    acceptedFormat.findIndex((f) => f === contentType) !== -1 +  ) {      return {        acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag,        currentEtag, @@ -464,7 +508,12 @@ async function getExchangeTos(      };    } -  const tosDownload = await downloadTosFromAcceptedFormat(ws, exchangeBaseUrl, getExchangeRequestTimeout(), acceptedFormat); +  const tosDownload = await downloadTosFromAcceptedFormat( +    ws, +    exchangeBaseUrl, +    getExchangeRequestTimeout(), +    acceptedFormat, +  );    if (tosDownload.tosContentType === contentType) {      return { @@ -474,7 +523,7 @@ async function getExchangeTos(        contentType,      };    } -  await updateExchangeTermsOfService(ws, exchangeBaseUrl, tosDownload) +  await updateExchangeTermsOfService(ws, exchangeBaseUrl, tosDownload);    return {      acceptedEtag: exchangeDetails.termsOfServiceAcceptedEtag, @@ -482,7 +531,6 @@ async function getExchangeTos(      content: tosDownload.tosText,      contentType: tosDownload.tosContentType,    }; -  }  async function listKnownBankAccounts( @@ -641,9 +689,15 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> {            }            withdrawalReservePub = ws.reservePub;          } +        const denomInfo = await ws.getDenomInfo( +          ws, +          tx, +          c.exchangeBaseUrl, +          c.denomPubHash, +        );          coinsJson.coins.push({            coin_pub: c.coinPub, -          denom_pub: c.denomPub, +          denom_pub: denomInfo?.denomPub!,            denom_pub_hash: c.denomPubHash,            denom_value: Amounts.stringify(denom.value),            exchange_base_url: c.exchangeBaseUrl, @@ -1030,7 +1084,7 @@ export async function handleCoreApiRequest(        try {          logger.error("Caught unexpected exception:");          logger.error(e.stack); -      } catch (e) { } +      } catch (e) {}        return {          type: "error",          operation, @@ -1236,7 +1290,10 @@ class InternalWalletStateImpl implements InternalWalletState {     * Run an async function after acquiring a list of locks, identified     * by string tokens.     */ -  async runSequentialized<T>(tokens: string[], f: () => Promise<T>): Promise<T> { +  async runSequentialized<T>( +    tokens: string[], +    f: () => Promise<T>, +  ): Promise<T> {      // Make sure locks are always acquired in the same order      tokens = [...tokens].sort(); @@ -1269,4 +1326,3 @@ class InternalWalletStateImpl implements InternalWalletState {      }    }  } - | 
