gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (4d70391f3 -> bd88dcebb)


From: gnunet
Subject: [taler-wallet-core] branch master updated (4d70391f3 -> bd88dcebb)
Date: Sat, 15 Oct 2022 12:59:29 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a change to branch master
in repository wallet-core.

    from 4d70391f3 wallet-core: address another DB FIXME
     new e075134ff wallet-core: simplify coin record
     new bd88dcebb wallet-core: simplify exchanges list response

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/taler-util/src/backup-types.ts            | 11 ++-
 packages/taler-util/src/taler-types.ts             | 54 -------------
 packages/taler-util/src/wallet-types.ts            | 94 ++++++++++++++++++++--
 packages/taler-wallet-cli/src/index.ts             |  4 +-
 .../integrationtests/test-refund-incremental.ts    |  2 +-
 .../src/integrationtests/test-wallettesting.ts     |  9 ++-
 packages/taler-wallet-core/src/db.ts               | 43 +---------
 .../taler-wallet-core/src/internal-wallet-state.ts |  4 +-
 .../src/operations/backup/export.ts                | 10 ++-
 .../src/operations/backup/import.ts                |  6 +-
 .../taler-wallet-core/src/operations/balance.ts    | 25 ++++--
 .../taler-wallet-core/src/operations/common.ts     | 39 +++++++--
 .../src/operations/pay-merchant.ts                 | 46 +++++------
 .../taler-wallet-core/src/operations/pay-peer.ts   |  8 +-
 .../taler-wallet-core/src/operations/recoup.ts     | 42 +++++-----
 .../taler-wallet-core/src/operations/refresh.ts    | 24 +++---
 packages/taler-wallet-core/src/operations/tip.ts   |  5 +-
 .../src/operations/transactions.ts                 |  8 +-
 .../taler-wallet-core/src/operations/withdraw.ts   | 30 +++----
 packages/taler-wallet-core/src/wallet.ts           | 39 +++++++--
 .../src/components/TermsOfService/utils.ts         | 27 +------
 .../src/components/TermsOfService/views.tsx        |  9 ++-
 .../src/cta/Withdraw/state.ts                      | 13 +--
 .../src/cta/Withdraw/test.ts                       |  3 +
 .../src/cta/Withdraw/views.tsx                     |  4 +-
 .../src/wallet/DeveloperPage.tsx                   | 17 ++--
 .../src/wallet/Settings.tsx                        | 22 +++--
 27 files changed, 314 insertions(+), 284 deletions(-)

diff --git a/packages/taler-util/src/backup-types.ts 
b/packages/taler-util/src/backup-types.ts
index 6c7b203b5..5d53f178e 100644
--- a/packages/taler-util/src/backup-types.ts
+++ b/packages/taler-util/src/backup-types.ts
@@ -475,6 +475,7 @@ export interface BackupRecoupGroup {
 
   timestamp_finish?: TalerProtocolTimestamp;
   finish_clock?: TalerProtocolTimestamp;
+  // FIXME: Use some enum here!
   finish_is_failure?: boolean;
 
   /**
@@ -483,7 +484,6 @@ export interface BackupRecoupGroup {
   coins: {
     coin_pub: string;
     recoup_finished: boolean;
-    old_amount: BackupAmountString;
   }[];
 }
 
@@ -582,9 +582,14 @@ export interface BackupCoin {
   denom_sig: UnblindedSignature;
 
   /**
-   * Amount that's left on the coin.
+   * Information about where and how the coin was spent.
    */
-  current_amount: BackupAmountString;
+  spend_allocation:
+    | {
+        id: string;
+        amount: BackupAmountString;
+      }
+    | undefined;
 
   /**
    * Blinding key used when withdrawing the coin.
diff --git a/packages/taler-util/src/taler-types.ts 
b/packages/taler-util/src/taler-types.ts
index de88fef69..71ceb7939 100644
--- a/packages/taler-util/src/taler-types.ts
+++ b/packages/taler-util/src/taler-types.ts
@@ -968,60 +968,6 @@ export class WithdrawBatchResponse {
   ev_sigs: WithdrawResponse[];
 }
 
-/**
- * Easy to process format for the public data of coins
- * managed by the wallet.
- */
-export interface CoinDumpJson {
-  coins: Array<{
-    /**
-     * The coin's denomination's public key.
-     */
-    denom_pub: DenominationPubKey;
-    /**
-     * Hash of denom_pub.
-     */
-    denom_pub_hash: string;
-    /**
-     * Value of the denomination (without any fees).
-     */
-    denom_value: string;
-    /**
-     * Public key of the coin.
-     */
-    coin_pub: string;
-    /**
-     * Base URL of the exchange for the coin.
-     */
-    exchange_base_url: string;
-    /**
-     * Remaining value on the coin, to the knowledge of
-     * the wallet.
-     */
-    remaining_value: string;
-    /**
-     * Public key of the parent coin.
-     * Only present if this coin was obtained via refreshing.
-     */
-    refresh_parent_coin_pub: string | undefined;
-    /**
-     * Public key of the reserve for this coin.
-     * Only present if this coin was obtained via refreshing.
-     */
-    withdrawal_reserve_pub: string | undefined;
-    /**
-     * Is the coin suspended?
-     * Suspended coins are not considered for payments.
-     */
-    coin_suspended: boolean;
-
-    /**
-     * Information about the age restriction
-     */
-    ageCommitmentProof: AgeCommitmentProof | undefined;
-  }>;
-}
-
 export interface MerchantPayResponse {
   sig: string;
 }
diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index d4de53972..aba1b1185 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -63,7 +63,10 @@ import {
   ExchangeAuditor,
   UnblindedSignature,
 } from "./taler-types.js";
-import { OrderShortInfo, codecForOrderShortInfo } from 
"./transactions-types.js";
+import {
+  OrderShortInfo,
+  codecForOrderShortInfo,
+} from "./transactions-types.js";
 import { BackupRecovery } from "./backup-types.js";
 import { PaytoUri } from "./payto.js";
 import { TalerErrorCode } from "./taler-error-codes.js";
@@ -141,6 +144,77 @@ export function mkAmount(
   return { value, fraction, currency };
 }
 
+/**
+ * Status of a coin.
+ */
+export enum CoinStatus {
+  /**
+   * Withdrawn and never shown to anybody.
+   */
+  Fresh = "fresh",
+
+  /**
+   * Fresh, but currently marked as "suspended", thus won't be used
+   * for spending.  Used for testing.
+   */
+  FreshSuspended = "fresh-suspended",
+
+  /**
+   * A coin that has been spent and refreshed.
+   */
+  Dormant = "dormant",
+}
+
+/**
+ * Easy to process format for the public data of coins
+ * managed by the wallet.
+ */
+export interface CoinDumpJson {
+  coins: Array<{
+    /**
+     * The coin's denomination's public key.
+     */
+    denom_pub: DenominationPubKey;
+    /**
+     * Hash of denom_pub.
+     */
+    denom_pub_hash: string;
+    /**
+     * Value of the denomination (without any fees).
+     */
+    denom_value: string;
+    /**
+     * Public key of the coin.
+     */
+    coin_pub: string;
+    /**
+     * Base URL of the exchange for the coin.
+     */
+    exchange_base_url: string;
+    /**
+     * Public key of the parent coin.
+     * Only present if this coin was obtained via refreshing.
+     */
+    refresh_parent_coin_pub: string | undefined;
+    /**
+     * Public key of the reserve for this coin.
+     * Only present if this coin was obtained via refreshing.
+     */
+    withdrawal_reserve_pub: string | undefined;
+    coin_status: CoinStatus;
+    spend_allocation:
+      | {
+          id: string;
+          amount: string;
+        }
+      | undefined;
+    /**
+     * Information about the age restriction
+     */
+    ageCommitmentProof: AgeCommitmentProof | undefined;
+  }>;
+}
+
 export enum ConfirmPayResultType {
   Done = "done",
   Pending = "pending",
@@ -568,10 +642,11 @@ export enum RefreshReason {
 }
 
 /**
- * Wrapper for coin public keys.
+ * Request to refresh a single coin.
  */
-export interface CoinPublicKey {
+export interface CoinRefreshRequest {
   readonly coinPub: string;
+  readonly amount: AmountJson;
 }
 
 /**
@@ -825,11 +900,18 @@ export interface ExchangeFullDetails {
   globalFees: FeeDescription[];
 }
 
+export enum ExchangeTosStatus {
+  New = "new",
+  Accepted = "accepted",
+  Changed = "changed",
+  NotFound = "not-found",
+}
+
 export interface ExchangeListItem {
   exchangeBaseUrl: string;
   currency: string;
   paytoUris: string[];
-  tos: ExchangeTosStatusDetails;
+  tosStatus: ExchangeTosStatus;
 }
 
 const codecForAuditorDenomSig = (): Codec<AuditorDenomSig> =>
@@ -901,7 +983,7 @@ export const codecForExchangeListItem = (): 
Codec<ExchangeListItem> =>
     .property("currency", codecForString())
     .property("exchangeBaseUrl", codecForString())
     .property("paytoUris", codecForList(codecForString()))
-    .property("tos", codecForExchangeTos())
+    .property("tosStatus", codecForAny())
     .build("ExchangeListItem");
 
 export const codecForExchangesListResponse = (): Codec<ExchangesListResponse> 
=>
@@ -1071,6 +1153,8 @@ export interface GetExchangeTosResult {
    * Accepted content type
    */
   contentType: string;
+
+  tosStatus: ExchangeTosStatus;
 }
 
 export interface TestPayArgs {
diff --git a/packages/taler-wallet-cli/src/index.ts 
b/packages/taler-wallet-cli/src/index.ts
index 941a2f28f..b3abbac6f 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -1105,9 +1105,7 @@ advancedCli
         console.log(`coin ${coin.coin_pub}`);
         console.log(` exchange ${coin.exchange_base_url}`);
         console.log(` denomPubHash ${coin.denom_pub_hash}`);
-        console.log(
-          ` remaining amount ${Amounts.stringify(coin.remaining_value)}`,
-        );
+        console.log(` status ${coin.coin_status}`);
       }
     });
   });
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts
index e4e96a180..8d1f6e873 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts
@@ -193,7 +193,7 @@ export async function runRefundIncrementalTest(t: 
GlobalTestState) {
         .map((x) => x.amountEffective),
     ).amount;
 
-    t.assertAmountEquals("TESTKUDOS:8.33", effective);
+    t.assertAmountEquals("TESTKUDOS:8.59", effective);
   }
 
   await t.shutdown();
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts
index c16a85f19..03c446db3 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts
@@ -22,7 +22,7 @@
 /**
  * Imports.
  */
-import { Amounts } from "@gnu-taler/taler-util";
+import { Amounts, CoinStatus } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
 import {
@@ -32,7 +32,7 @@ import {
   MerchantService,
   setupDb,
   WalletCli,
-  getPayto
+  getPayto,
 } from "../harness/harness.js";
 import { SimpleTestEnvironment } from "../harness/helpers.js";
 
@@ -184,7 +184,10 @@ export async function runWallettestingTest(t: 
GlobalTestState) {
   let susp: string | undefined;
   {
     for (const c of coinDump.coins) {
-      if (0 === Amounts.cmp(c.remaining_value, "TESTKUDOS:8")) {
+      if (
+        c.coin_status === CoinStatus.Fresh &&
+        0 === Amounts.cmp(c.denom_value, "TESTKUDOS:8")
+      ) {
         susp = c.coin_pub;
       }
     }
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index c301ee457..b785efed8 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -49,6 +49,8 @@ import {
   ExchangeGlobalFees,
   DenomSelectionState,
   TransactionIdStr,
+  CoinRefreshRequest,
+  CoinStatus,
 } from "@gnu-taler/taler-util";
 import { RetryInfo, RetryTags } from "./util/retries.js";
 import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
@@ -603,27 +605,6 @@ export interface PlanchetRecord {
   ageCommitmentProof?: AgeCommitmentProof;
 }
 
-/**
- * Status of a coin.
- */
-export enum CoinStatus {
-  /**
-   * Withdrawn and never shown to anybody.
-   */
-  Fresh = "fresh",
-
-  /**
-   * Fresh, but currently marked as "suspended", thus won't be used
-   * for spending.  Used for testing.
-   */
-  FreshSuspended = "fresh-suspended",
-
-  /**
-   * A coin that has been spent and refreshed.
-   */
-  Dormant = "dormant",
-}
-
 export enum CoinSourceType {
   Withdraw = "withdraw",
   Refresh = "refresh",
@@ -692,14 +673,6 @@ export interface CoinRecord {
    */
   denomSig: UnblindedSignature;
 
-  /**
-   * Amount that's left on the coin.
-   *
-   * FIXME: This is pretty redundant with "allocation" and "status".
-   * Do we really need this?
-   */
-  currentAmount: AmountJson;
-
   /**
    * Base URL that identifies the exchange from which we got the
    * coin.
@@ -732,7 +705,7 @@ export interface CoinRecord {
    * - Diagnostics
    * - Idempotency of applying a coin selection (e.g. after re-selection)
    */
-  allocation: CoinAllocation | undefined;
+  spendAllocation: CoinAllocation | undefined;
 
   /**
    * Maximum age of purchases that can be made with this coin.
@@ -1461,18 +1434,11 @@ export interface RecoupGroupRecord {
    */
   recoupFinishedPerCoin: boolean[];
 
-  /**
-   * We store old amount (i.e. before recoup) of recouped coins here,
-   * as the balance of a recouped coin is set to zero when the
-   * recoup group is created.
-   */
-  oldAmountPerCoin: AmountJson[];
-
   /**
    * Public keys of coins that should be scheduled for refreshing
    * after all individual recoups are done.
    */
-  scheduleRefreshCoins: string[];
+  scheduleRefreshCoins: CoinRefreshRequest[];
 }
 
 export enum BackupProviderStateTag {
@@ -1875,7 +1841,6 @@ export const WalletStoresV1 = {
     "exchangeTos",
     describeContents<ExchangeTosRecord>({
       keyPath: ["exchangeBaseUrl", "etag"],
-      autoIncrement: true,
     }),
     {},
   ),
diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts 
b/packages/taler-wallet-core/src/internal-wallet-state.ts
index bc956bd17..ebb9cdb9b 100644
--- a/packages/taler-wallet-core/src/internal-wallet-state.ts
+++ b/packages/taler-wallet-core/src/internal-wallet-state.ts
@@ -38,7 +38,7 @@ import {
   CancellationToken,
   DenominationInfo,
   RefreshGroupId,
-  CoinPublicKey,
+  CoinRefreshRequest,
   RefreshReason,
 } from "@gnu-taler/taler-util";
 import { CryptoDispatcher } from "./crypto/workers/cryptoDispatcher.js";
@@ -86,7 +86,7 @@ export interface RefreshOperations {
       refreshGroups: typeof WalletStoresV1.refreshGroups;
       coinAvailability: typeof WalletStoresV1.coinAvailability;
     }>,
-    oldCoinPubs: CoinPublicKey[],
+    oldCoinPubs: CoinRefreshRequest[],
     reason: RefreshReason,
   ): Promise<RefreshGroupId>;
 }
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts 
b/packages/taler-wallet-core/src/operations/backup/export.ts
index 30e61e382..1472b1b90 100644
--- a/packages/taler-wallet-core/src/operations/backup/export.ts
+++ b/packages/taler-wallet-core/src/operations/backup/export.ts
@@ -54,6 +54,7 @@ import {
   BACKUP_VERSION_MINOR,
   canonicalizeBaseUrl,
   canonicalJson,
+  CoinStatus,
   encodeCrock,
   getRandomBytes,
   hash,
@@ -63,7 +64,6 @@ import {
 } from "@gnu-taler/taler-util";
 import {
   CoinSourceType,
-  CoinStatus,
   ConfigRecordKey,
   DenominationRecord,
   PurchaseStatus,
@@ -206,7 +206,6 @@ export async function exportBackup(
           coins: recoupGroup.coinPubs.map((x, i) => ({
             coin_pub: x,
             recoup_finished: recoupGroup.recoupFinishedPerCoin[i],
-            old_amount: Amounts.stringify(recoupGroup.oldAmountPerCoin[i]),
           })),
         });
       });
@@ -259,8 +258,13 @@ export async function exportBackup(
           blinding_key: coin.blindingKey,
           coin_priv: coin.coinPriv,
           coin_source: bcs,
-          current_amount: Amounts.stringify(coin.currentAmount),
           fresh: coin.status === CoinStatus.Fresh,
+          spend_allocation: coin.spendAllocation
+            ? {
+                amount: coin.spendAllocation.amount,
+                id: coin.spendAllocation.id,
+              }
+            : undefined,
           denom_sig: coin.denomSig,
         });
       });
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts 
b/packages/taler-wallet-core/src/operations/backup/import.ts
index 3bbb7d798..9c5eea9af 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -27,6 +27,7 @@ import {
   BackupRefundState,
   BackupWgType,
   codecForContractTerms,
+  CoinStatus,
   DenomKeyType,
   DenomSelectionState,
   j2s,
@@ -41,10 +42,8 @@ import {
   CoinRecord,
   CoinSource,
   CoinSourceType,
-  CoinStatus,
   DenominationRecord,
   DenominationVerificationStatus,
-  OperationStatus,
   ProposalDownloadInfo,
   PurchaseStatus,
   PurchasePayInfo,
@@ -272,7 +271,6 @@ export async function importCoin(
       blindingKey: backupCoin.blinding_key,
       coinEvHash: compCoin.coinEvHash,
       coinPriv: backupCoin.coin_priv,
-      currentAmount: Amounts.parseOrThrow(backupCoin.current_amount),
       denomSig: backupCoin.denom_sig,
       coinPub: compCoin.coinPub,
       exchangeBaseUrl,
@@ -284,7 +282,7 @@ export async function importCoin(
       // FIXME!
       ageCommitmentProof: undefined,
       // FIXME!
-      allocation: undefined,
+      spendAllocation: undefined,
     };
     if (coinRecord.status === CoinStatus.Fresh) {
       await makeCoinAvailable(ws, tx, coinRecord);
diff --git a/packages/taler-wallet-core/src/operations/balance.ts 
b/packages/taler-wallet-core/src/operations/balance.ts
index 44357fdf4..3db66b5d9 100644
--- a/packages/taler-wallet-core/src/operations/balance.ts
+++ b/packages/taler-wallet-core/src/operations/balance.ts
@@ -23,7 +23,7 @@ import {
   Amounts,
   Logger,
 } from "@gnu-taler/taler-util";
-import { CoinStatus, WalletStoresV1 } from "../db.js";
+import { WalletStoresV1 } from "../db.js";
 import { GetReadOnlyAccess } from "../util/query.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 
@@ -42,6 +42,7 @@ export async function getBalancesInsideTransaction(
   ws: InternalWalletState,
   tx: GetReadOnlyAccess<{
     coins: typeof WalletStoresV1.coins;
+    coinAvailability: typeof WalletStoresV1.coinAvailability;
     refreshGroups: typeof WalletStoresV1.refreshGroups;
     withdrawalGroups: typeof WalletStoresV1.withdrawalGroups;
   }>,
@@ -64,12 +65,14 @@ export async function getBalancesInsideTransaction(
     return balanceStore[currency];
   };
 
-  await tx.coins.iter().forEach((c) => {
-    // Only count fresh coins, as dormant coins will
-    // already be in a refresh session.
-    if (c.status === CoinStatus.Fresh) {
-      const b = initBalance(c.currentAmount.currency);
-      b.available = Amounts.add(b.available, c.currentAmount).amount;
+  await tx.coinAvailability.iter().forEach((ca) => {
+    const b = initBalance(ca.currency);
+    for (let i = 0; i < ca.freshCoinCount; i++) {
+      b.available = Amounts.add(b.available, {
+        currency: ca.currency,
+        fraction: ca.amountFrac,
+        value: ca.amountVal,
+      }).amount;
     }
   });
 
@@ -139,7 +142,13 @@ export async function getBalances(
   logger.trace("starting to compute balance");
 
   const wbal = await ws.db
-    .mktx((x) => [x.coins, x.refreshGroups, x.purchases, x.withdrawalGroups])
+    .mktx((x) => [
+      x.coins,
+      x.coinAvailability,
+      x.refreshGroups,
+      x.purchases,
+      x.withdrawalGroups,
+    ])
     .runReadOnly(async (tx) => {
       return getBalancesInsideTransaction(ws, tx);
     });
diff --git a/packages/taler-wallet-core/src/operations/common.ts 
b/packages/taler-wallet-core/src/operations/common.ts
index d17530c7f..ee7a1b46e 100644
--- a/packages/taler-wallet-core/src/operations/common.ts
+++ b/packages/taler-wallet-core/src/operations/common.ts
@@ -20,6 +20,9 @@
 import {
   AmountJson,
   Amounts,
+  CoinRefreshRequest,
+  CoinStatus,
+  ExchangeTosStatus,
   j2s,
   Logger,
   RefreshReason,
@@ -29,7 +32,7 @@ import {
   TransactionIdStr,
   TransactionType,
 } from "@gnu-taler/taler-util";
-import { WalletStoresV1, CoinStatus, CoinRecord } from "../db.js";
+import { WalletStoresV1, CoinRecord, ExchangeDetailsRecord } from "../db.js";
 import { makeErrorDetail, TalerError } from "../errors.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
@@ -103,11 +106,19 @@ export async function spendCoins(
   }>,
   csi: CoinsSpendInfo,
 ): Promise<void> {
+  let refreshCoinPubs: CoinRefreshRequest[] = [];
   for (let i = 0; i < csi.coinPubs.length; i++) {
     const coin = await tx.coins.get(csi.coinPubs[i]);
     if (!coin) {
       throw Error("coin allocated for payment doesn't exist anymore");
     }
+    const denom = await ws.getDenomInfo(
+      ws,
+      tx,
+      coin.exchangeBaseUrl,
+      coin.denomPubHash,
+    );
+    checkDbInvariant(!!denom);
     const coinAvailability = await tx.coinAvailability.get([
       coin.exchangeBaseUrl,
       coin.denomPubHash,
@@ -116,7 +127,7 @@ export async function spendCoins(
     checkDbInvariant(!!coinAvailability);
     const contrib = csi.contributions[i];
     if (coin.status !== CoinStatus.Fresh) {
-      const alloc = coin.allocation;
+      const alloc = coin.spendAllocation;
       if (!alloc) {
         continue;
       }
@@ -131,15 +142,18 @@ export async function spendCoins(
       continue;
     }
     coin.status = CoinStatus.Dormant;
-    coin.allocation = {
+    coin.spendAllocation = {
       id: csi.allocationId,
       amount: Amounts.stringify(contrib),
     };
-    const remaining = Amounts.sub(coin.currentAmount, contrib);
+    const remaining = Amounts.sub(denom.value, contrib);
     if (remaining.saturated) {
       throw Error("not enough remaining balance on coin for payment");
     }
-    coin.currentAmount = remaining.amount;
+    refreshCoinPubs.push({
+      amount: remaining.amount,
+      coinPub: coin.coinPub,
+    });
     checkDbInvariant(!!coinAvailability);
     if (coinAvailability.freshCoinCount === 0) {
       throw Error(
@@ -150,9 +164,6 @@ export async function spendCoins(
     await tx.coins.put(coin);
     await tx.coinAvailability.put(coinAvailability);
   }
-  const refreshCoinPubs = csi.coinPubs.map((x) => ({
-    coinPub: x,
-  }));
   await ws.refreshOps.createRefreshGroup(
     ws,
     tx,
@@ -297,3 +308,15 @@ export function makeTombstoneId(
 ): TombstoneIdStr {
   return `tmb:${type}:${args.map((x) => encodeURIComponent(x)).join(":")}`;
 }
+
+export function getExchangeTosStatus(
+  exchangeDetails: ExchangeDetailsRecord,
+): ExchangeTosStatus {
+  if (!exchangeDetails.tosAccepted) {
+    return ExchangeTosStatus.New;
+  }
+  if (exchangeDetails.tosAccepted?.etag == exchangeDetails.tosCurrentEtag) {
+    return ExchangeTosStatus.Accepted;
+  }
+  return ExchangeTosStatus.Changed;
+}
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts 
b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index 6b14b60c6..2b0ea1f96 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -40,7 +40,8 @@ import {
   codecForMerchantPayResponse,
   codecForProposal,
   CoinDepositPermission,
-  CoinPublicKey,
+  CoinRefreshRequest,
+  CoinStatus,
   ConfirmPayResult,
   ConfirmPayResultType,
   ContractTerms,
@@ -78,7 +79,6 @@ import {
   AllowedExchangeInfo,
   BackupProviderStateTag,
   CoinRecord,
-  CoinStatus,
   DenominationRecord,
   PurchaseRecord,
   PurchaseStatus,
@@ -2084,7 +2084,7 @@ async function applySuccessfulRefund(
     denominations: typeof WalletStoresV1.denominations;
   }>,
   p: PurchaseRecord,
-  refreshCoinsMap: Record<string, { coinPub: string }>,
+  refreshCoinsMap: Record<string, CoinRefreshRequest>,
   r: MerchantCoinRefundSuccessStatus,
 ): Promise<void> {
   // FIXME: check signature before storing it as valid!
@@ -2102,31 +2102,23 @@ async function applySuccessfulRefund(
   if (!denom) {
     throw Error("inconsistent database");
   }
-  refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub };
   const refundAmount = Amounts.parseOrThrow(r.refund_amount);
   const refundFee = denom.fees.feeRefund;
+  const amountLeft = Amounts.sub(refundAmount, refundFee).amount;
   coin.status = CoinStatus.Dormant;
-  coin.currentAmount = Amounts.add(coin.currentAmount, refundAmount).amount;
-  coin.currentAmount = Amounts.sub(coin.currentAmount, refundFee).amount;
-  logger.trace(`coin amount after is 
${Amounts.stringify(coin.currentAmount)}`);
   await tx.coins.put(coin);
 
   const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
     .iter(coin.exchangeBaseUrl)
     .toArray();
-
-  const amountLeft = Amounts.sub(
-    Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount))
-      .amount,
-    denom.fees.feeRefund,
-  ).amount;
-
   const totalRefreshCostBound = getTotalRefreshCost(
     allDenoms,
     DenominationRecord.toDenomInfo(denom),
     amountLeft,
   );
 
+  refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub, amount: amountLeft 
};
+
   p.refunds[refundKey] = {
     type: RefundState.Applied,
     obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
@@ -2167,9 +2159,9 @@ async function storePendingRefund(
     .iter(coin.exchangeBaseUrl)
     .toArray();
 
+  // Refunded amount after fees.
   const amountLeft = Amounts.sub(
-    Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount))
-      .amount,
+    Amounts.parseOrThrow(r.refund_amount),
     denom.fees.feeRefund,
   ).amount;
 
@@ -2197,7 +2189,7 @@ async function storeFailedRefund(
     denominations: typeof WalletStoresV1.denominations;
   }>,
   p: PurchaseRecord,
-  refreshCoinsMap: Record<string, { coinPub: string }>,
+  refreshCoinsMap: Record<string, CoinRefreshRequest>,
   r: MerchantCoinRefundFailureStatus,
 ): Promise<void> {
   const refundKey = getRefundKey(r);
@@ -2221,8 +2213,7 @@ async function storeFailedRefund(
     .toArray();
 
   const amountLeft = Amounts.sub(
-    Amounts.add(coin.currentAmount, Amounts.parseOrThrow(r.refund_amount))
-      .amount,
+    Amounts.parseOrThrow(r.refund_amount),
     denom.fees.feeRefund,
   ).amount;
 
@@ -2246,6 +2237,7 @@ async function storeFailedRefund(
   if (p.purchaseStatus === PurchaseStatus.AbortingWithRefund) {
     // Refund failed because the merchant didn't even try to deposit
     // the coin yet, so we try to refresh.
+    // FIXME: Is this case tested?!
     if (r.exchange_code === TalerErrorCode.EXCHANGE_REFUND_DEPOSIT_NOT_FOUND) {
       const coin = await tx.coins.get(r.coin_pub);
       if (!coin) {
@@ -2271,14 +2263,11 @@ async function storeFailedRefund(
           contrib = payCoinSelection.coinContributions[i];
         }
       }
-      if (contrib) {
-        coin.currentAmount = Amounts.add(coin.currentAmount, contrib).amount;
-        coin.currentAmount = Amounts.sub(
-          coin.currentAmount,
-          denom.fees.feeRefund,
-        ).amount;
-      }
-      refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub };
+      // FIXME: Is this case tested?!
+      refreshCoinsMap[coin.coinPub] = {
+        coinPub: coin.coinPub,
+        amount: amountLeft,
+      };
       await tx.coins.put(coin);
     }
   }
@@ -2308,7 +2297,7 @@ async function acceptRefunds(
         return;
       }
 
-      const refreshCoinsMap: Record<string, CoinPublicKey> = {};
+      const refreshCoinsMap: Record<string, CoinRefreshRequest> = {};
 
       for (const refundStatus of refunds) {
         const refundKey = getRefundKey(refundStatus);
@@ -2350,6 +2339,7 @@ async function acceptRefunds(
       }
 
       const refreshCoinsPubs = Object.values(refreshCoinsMap);
+      logger.info(`refreshCoinMap ${j2s(refreshCoinsMap)}`);
       if (refreshCoinsPubs.length > 0) {
         await createRefreshGroup(
           ws,
diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts 
b/packages/taler-wallet-core/src/operations/pay-peer.ts
index ffc49c24c..3b65fba6b 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer.ts
@@ -36,6 +36,7 @@ import {
   codecForAmountString,
   codecForAny,
   codecForExchangeGetContractResponse,
+  CoinStatus,
   constructPayPullUri,
   constructPayPushUri,
   ContractTermsUtil,
@@ -63,17 +64,16 @@ import {
   WalletAccountMergeFlags,
 } from "@gnu-taler/taler-util";
 import {
-  CoinStatus,
-  WithdrawalGroupStatus,
+  ReserveRecord,
   WalletStoresV1,
+  WithdrawalGroupStatus,
   WithdrawalRecordType,
-  ReserveRecord,
 } from "../db.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
+import { makeTransactionId, spendCoins } from "../operations/common.js";
 import { readSuccessResponseJsonOrThrow } from "../util/http.js";
 import { checkDbInvariant } from "../util/invariants.js";
 import { GetReadOnlyAccess } from "../util/query.js";
-import { spendCoins, makeTransactionId } from "../operations/common.js";
 import { updateExchangeFromUrl } from "./exchanges.js";
 import { internalCreateWithdrawalGroup } from "./withdraw.js";
 
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts 
b/packages/taler-wallet-core/src/operations/recoup.ts
index ff6bb4efc..d3bcde048 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -28,6 +28,7 @@ import {
   Amounts,
   codecForRecoupConfirmation,
   codecForReserveStatus,
+  CoinStatus,
   encodeCrock,
   getRandomBytes,
   j2s,
@@ -40,7 +41,6 @@ import {
 import {
   CoinRecord,
   CoinSourceType,
-  CoinStatus,
   RecoupGroupRecord,
   RefreshCoinSource,
   WalletStoresV1,
@@ -50,6 +50,7 @@ import {
 } from "../db.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { readSuccessResponseJsonOrThrow } from "../util/http.js";
+import { checkDbInvariant } from "../util/invariants.js";
 import { GetReadWriteAccess } from "../util/query.js";
 import {
   OperationAttemptResult,
@@ -180,8 +181,6 @@ async function recoupWithdrawCoin(
         return;
       }
       updatedCoin.status = CoinStatus.Dormant;
-      const currency = updatedCoin.currentAmount.currency;
-      updatedCoin.currentAmount = Amounts.getZero(currency);
       await tx.coins.put(updatedCoin);
       await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
     });
@@ -265,16 +264,25 @@ async function recoupRefreshCoin(
         logger.warn("refresh old coin for recoup not found");
         return;
       }
-      revokedCoin.status = CoinStatus.Dormant;
-      oldCoin.currentAmount = Amounts.add(
-        oldCoin.currentAmount,
-        recoupGroup.oldAmountPerCoin[coinIdx],
-      ).amount;
-      logger.trace(
-        "recoup: setting old coin amount to",
-        Amounts.stringify(oldCoin.currentAmount),
+      const oldCoinDenom = await ws.getDenomInfo(
+        ws,
+        tx,
+        oldCoin.exchangeBaseUrl,
+        oldCoin.denomPubHash,
       );
-      recoupGroup.scheduleRefreshCoins.push(oldCoin.coinPub);
+      const revokedCoinDenom = await ws.getDenomInfo(
+        ws,
+        tx,
+        revokedCoin.exchangeBaseUrl,
+        revokedCoin.denomPubHash,
+      );
+      checkDbInvariant(!!oldCoinDenom);
+      checkDbInvariant(!!revokedCoinDenom);
+      revokedCoin.status = CoinStatus.Dormant;
+      recoupGroup.scheduleRefreshCoins.push({
+        coinPub: oldCoin.coinPub,
+        amount: Amounts.sub(oldCoinDenom.value, revokedCoinDenom.value).amount,
+      });
       await tx.coins.put(revokedCoin);
       await tx.coins.put(oldCoin);
       await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
@@ -410,7 +418,7 @@ export async function processRecoupGroupHandler(
         const refreshGroupId = await createRefreshGroup(
           ws,
           tx,
-          rg2.scheduleRefreshCoins.map((x) => ({ coinPub: x })),
+          rg2.scheduleRefreshCoins,
           RefreshReason.Recoup,
         );
         processRefreshGroup(ws, refreshGroupId.refreshGroupId).catch((e) => {
@@ -442,8 +450,6 @@ export async function createRecoupGroup(
     timestampFinished: undefined,
     timestampStarted: TalerProtocolTimestamp.now(),
     recoupFinishedPerCoin: coinPubs.map(() => false),
-    // Will be populated later
-    oldAmountPerCoin: [],
     scheduleRefreshCoins: [],
   };
 
@@ -454,12 +460,6 @@ export async function createRecoupGroup(
       await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
       continue;
     }
-    if (Amounts.isZero(coin.currentAmount)) {
-      await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
-      continue;
-    }
-    recoupGroup.oldAmountPerCoin[coinIdx] = coin.currentAmount;
-    coin.currentAmount = Amounts.getZero(coin.currentAmount.currency);
     await tx.coins.put(coin);
   }
 
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index 83ab32f20..c7d2c320e 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -23,8 +23,9 @@ import {
   amountToPretty,
   codecForExchangeMeltResponse,
   codecForExchangeRevealResponse,
-  CoinPublicKey,
   CoinPublicKeyString,
+  CoinRefreshRequest,
+  CoinStatus,
   DenominationInfo,
   DenomKeyType,
   Duration,
@@ -55,9 +56,7 @@ import { CryptoApiStoppedError } from 
"../crypto/workers/cryptoDispatcher.js";
 import {
   CoinRecord,
   CoinSourceType,
-  CoinStatus,
   DenominationRecord,
-  OperationStatus,
   RefreshCoinStatus,
   RefreshGroupRecord,
   RefreshOperationStatus,
@@ -672,7 +671,6 @@ async function refreshReveal(
         blindingKey: pc.blindingKey,
         coinPriv: pc.coinPriv,
         coinPub: pc.coinPub,
-        currentAmount: ncd.value,
         denomPubHash: ncd.denomPubHash,
         denomSig,
         exchangeBaseUrl: oldCoin.exchangeBaseUrl,
@@ -684,7 +682,7 @@ async function refreshReveal(
         coinEvHash: pc.coinEvHash,
         maxAge: pc.maxAge,
         ageCommitmentProof: pc.ageCommitmentProof,
-        allocation: undefined,
+        spendAllocation: undefined,
       };
 
       coins.push(coin);
@@ -845,7 +843,7 @@ export async function createRefreshGroup(
     refreshGroups: typeof WalletStoresV1.refreshGroups;
     coinAvailability: typeof WalletStoresV1.coinAvailability;
   }>,
-  oldCoinPubs: CoinPublicKey[],
+  oldCoinPubs: CoinRefreshRequest[],
   reason: RefreshReason,
 ): Promise<RefreshGroupId> {
   const refreshGroupId = encodeCrock(getRandomBytes(32));
@@ -908,9 +906,8 @@ export async function createRefreshGroup(
       default:
         assertUnreachable(coin.status);
     }
-    const refreshAmount = coin.currentAmount;
+    const refreshAmount = ocp.amount;
     inputPerCoin.push(refreshAmount);
-    coin.currentAmount = Amounts.getZero(refreshAmount.currency);
     await tx.coins.put(coin);
     const denoms = await getDenoms(coin.exchangeBaseUrl);
     const cost = getTotalRefreshCost(denoms, denom, refreshAmount);
@@ -1008,7 +1005,7 @@ export async function autoRefresh(
       const coins = await tx.coins.indexes.byBaseUrl
         .iter(exchangeBaseUrl)
         .toArray();
-      const refreshCoins: CoinPublicKey[] = [];
+      const refreshCoins: CoinRefreshRequest[] = [];
       for (const coin of coins) {
         if (coin.status !== CoinStatus.Fresh) {
           continue;
@@ -1023,7 +1020,14 @@ export async function autoRefresh(
         }
         const executeThreshold = getAutoRefreshExecuteThreshold(denom);
         if (AbsoluteTime.isExpired(executeThreshold)) {
-          refreshCoins.push(coin);
+          refreshCoins.push({
+            coinPub: coin.coinPub,
+            amount: {
+              value: denom.amountVal,
+              fraction: denom.amountFrac,
+              currency: denom.currency,
+            },
+          });
         } else {
           const checkThreshold = getAutoRefreshCheckThreshold(denom);
           minCheckThreshold = AbsoluteTime.min(
diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index b74e1182a..f98d69e26 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -24,6 +24,7 @@ import {
   BlindedDenominationSignature,
   codecForMerchantTipResponseV2,
   codecForTipPickupGetResponse,
+  CoinStatus,
   DenomKeyType,
   encodeCrock,
   getRandomBytes,
@@ -41,7 +42,6 @@ import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js";
 import {
   CoinRecord,
   CoinSourceType,
-  CoinStatus,
   DenominationRecord,
   TipRecord,
 } from "../db.js";
@@ -311,7 +311,6 @@ export async function processTip(
         coinIndex: i,
         walletTipId: walletTipId,
       },
-      currentAmount: DenominationRecord.getValue(denom),
       denomPubHash: denom.denomPubHash,
       denomSig: { cipher: DenomKeyType.Rsa, rsa_signature: denomSigRsa.sig },
       exchangeBaseUrl: tipRecord.exchangeBaseUrl,
@@ -319,7 +318,7 @@ export async function processTip(
       coinEvHash: planchet.coinEvHash,
       maxAge: AgeRestriction.AGE_UNRESTRICTED,
       ageCommitmentProof: planchet.ageCommitmentProof,
-      allocation: undefined,
+      spendAllocation: undefined,
     });
   }
 
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index c7ff4161a..1e7f982bc 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -540,7 +540,6 @@ function buildTransactionForTip(
 
 /**
  * For a set of refund with the same executionTime.
- *
  */
 interface MergedRefundInfo {
   executionTime: TalerProtocolTimestamp;
@@ -556,7 +555,7 @@ function mergeRefundByExecutionTime(
   const refundByExecTime = rs.reduce((prev, refund) => {
     const key = `${refund.executionTime.t_s}`;
 
-    //refunds counts if applied
+    // refunds count if applied
     const effective =
       refund.type === RefundState.Applied
         ? Amounts.sub(
@@ -582,7 +581,10 @@ function mergeRefundByExecutionTime(
         v.amountAppliedEffective,
         effective,
       ).amount;
-      v.amountAppliedRaw = Amounts.add(v.amountAppliedRaw).amount;
+      v.amountAppliedRaw = Amounts.add(
+        v.amountAppliedRaw,
+        refund.refundAmount,
+      ).amount;
       v.firstTimestamp = TalerProtocolTimestamp.min(
         v.firstTimestamp,
         refund.obtainedTime,
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index a258c5d76..1520dfc0a 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -36,6 +36,7 @@ import {
   codecForWithdrawBatchResponse,
   codecForWithdrawOperationStatusResponse,
   codecForWithdrawResponse,
+  CoinStatus,
   DenomKeyType,
   DenomSelectionState,
   Duration,
@@ -57,7 +58,6 @@ import {
   TransactionType,
   UnblindedSignature,
   URL,
-  VersionMatchResult,
   WithdrawBatchResponse,
   WithdrawResponse,
   WithdrawUriInfoResponse,
@@ -66,10 +66,8 @@ import { EddsaKeypair } from 
"../crypto/cryptoImplementation.js";
 import {
   CoinRecord,
   CoinSourceType,
-  CoinStatus,
   DenominationRecord,
   DenominationVerificationStatus,
-  ExchangeTosRecord,
   PlanchetRecord,
   PlanchetStatus,
   WalletStoresV1,
@@ -85,6 +83,7 @@ import {
 } from "../errors.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import {
+  getExchangeTosStatus,
   makeCoinAvailable,
   runOperationWithErrorReporting,
 } from "../operations/common.js";
@@ -736,7 +735,6 @@ async function processPlanchetVerifyAndStoreCoin(
     blindingKey: planchet.blindingKey,
     coinPriv: planchet.coinPriv,
     coinPub: planchet.coinPub,
-    currentAmount: denomInfo.value,
     denomPubHash: planchet.denomPubHash,
     denomSig,
     coinEvHash: planchet.coinEvHash,
@@ -750,7 +748,7 @@ async function processPlanchetVerifyAndStoreCoin(
     },
     maxAge: planchet.maxAge,
     ageCommitmentProof: planchet.ageCommitmentProof,
-    allocation: undefined,
+    spendAllocation: undefined,
   };
 
   const planchetCoinPub = planchet.coinPub;
@@ -1362,26 +1360,20 @@ export async function getWithdrawalDetailsForUri(
     .runReadOnly(async (tx) => {
       const exchangeRecords = await tx.exchanges.iter().toArray();
       for (const r of exchangeRecords) {
-        const details = await ws.exchangeOps.getExchangeDetails(tx, r.baseUrl);
+        const exchangeDetails = await ws.exchangeOps.getExchangeDetails(tx, 
r.baseUrl);
         const denominations = await tx.denominations.indexes.byExchangeBaseUrl
           .iter(r.baseUrl)
           .toArray();
-        if (details && denominations) {
+        if (exchangeDetails && denominations) {
           const tosRecord = await tx.exchangeTos.get([
-            details.exchangeBaseUrl,
-            details.tosCurrentEtag,
+            exchangeDetails.exchangeBaseUrl,
+            exchangeDetails.tosCurrentEtag,
           ]);
           exchanges.push({
-            exchangeBaseUrl: details.exchangeBaseUrl,
-            currency: details.currency,
-            // FIXME: We probably don't want to include the full ToS here!
-            tos: {
-              acceptedVersion: details.tosAccepted?.etag,
-              currentVersion: details.tosCurrentEtag,
-              contentType: tosRecord?.termsOfServiceContentType ?? "",
-              content: tosRecord?.termsOfServiceText ?? "",
-            },
-            paytoUris: details.wireInfo.accounts.map((x) => x.payto_uri),
+            exchangeBaseUrl: exchangeDetails.exchangeBaseUrl,
+            currency: exchangeDetails.currency,
+            paytoUris: exchangeDetails.wireInfo.accounts.map((x) => 
x.payto_uri),
+            tosStatus: getExchangeTosStatus(exchangeDetails),
           });
         }
       }
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index ef7a745ab..3c7194059 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -95,6 +95,8 @@ import {
   WalletNotification,
   codecForSetDevModeRequest,
   ExchangeTosStatusDetails,
+  CoinRefreshRequest,
+  CoinStatus,
 } from "@gnu-taler/taler-util";
 import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
 import {
@@ -105,11 +107,9 @@ import { clearDatabase } from "./db-utils.js";
 import {
   AuditorTrustRecord,
   CoinSourceType,
-  CoinStatus,
   ConfigRecordKey,
   DenominationRecord,
   ExchangeDetailsRecord,
-  ExchangeTosRecord,
   exportDb,
   importDb,
   WalletStoresV1,
@@ -146,7 +146,7 @@ import {
 } from "./operations/backup/index.js";
 import { setWalletDeviceId } from "./operations/backup/state.js";
 import { getBalances } from "./operations/balance.js";
-import { runOperationWithErrorReporting } from "./operations/common.js";
+import { getExchangeTosStatus, runOperationWithErrorReporting } from 
"./operations/common.js";
 import {
   createDepositGroup,
   getFeeForDeposit,
@@ -503,6 +503,7 @@ async function getExchangeTos(
       currentEtag,
       content,
       contentType,
+      tosStatus: getExchangeTosStatus(exchangeDetails),
     };
   }
 
@@ -519,6 +520,7 @@ async function getExchangeTos(
       currentEtag,
       content,
       contentType,
+      tosStatus: getExchangeTosStatus(exchangeDetails),
     };
   }
 
@@ -529,6 +531,7 @@ async function getExchangeTos(
     currentEtag: tosDownload.tosEtag,
     content: tosDownload.tosText,
     contentType: tosDownload.tosContentType,
+    tosStatus: getExchangeTosStatus(exchangeDetails),
   };
 }
 
@@ -665,7 +668,7 @@ async function getExchanges(
         exchanges.push({
           exchangeBaseUrl: r.baseUrl,
           currency,
-          tos,
+          tosStatus: getExchangeTosStatus(exchangeDetails),
           paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri),
         });
       }
@@ -934,10 +937,15 @@ async function dumpCoins(ws: InternalWalletState): 
Promise<CoinDumpJson> {
           }),
           exchange_base_url: c.exchangeBaseUrl,
           refresh_parent_coin_pub: refreshParentCoinPub,
-          remaining_value: Amounts.stringify(c.currentAmount),
           withdrawal_reserve_pub: withdrawalReservePub,
-          coin_suspended: c.status === CoinStatus.FreshSuspended,
+          coin_status: c.status,
           ageCommitmentProof: c.ageCommitmentProof,
+          spend_allocation: c.spendAllocation
+            ? {
+                amount: c.spendAllocation.amount,
+                id: c.spendAllocation.id,
+              }
+            : undefined,
         });
       }
     });
@@ -1153,7 +1161,6 @@ async function dispatchRequestInternal(
     }
     case "forceRefresh": {
       const req = codecForForceRefreshRequest().decode(payload);
-      const coinPubs = req.coinPubList.map((x) => ({ coinPub: x }));
       const refreshGroupId = await ws.db
         .mktx((x) => [
           x.refreshGroups,
@@ -1162,6 +1169,24 @@ async function dispatchRequestInternal(
           x.coins,
         ])
         .runReadWrite(async (tx) => {
+          let coinPubs: CoinRefreshRequest[] = [];
+          for (const c of req.coinPubList) {
+            const coin = await tx.coins.get(c);
+            if (!coin) {
+              throw Error(`coin (pubkey ${c}) not found`);
+            }
+            const denom = await ws.getDenomInfo(
+              ws,
+              tx,
+              coin.exchangeBaseUrl,
+              coin.denomPubHash,
+            );
+            checkDbInvariant(!!denom);
+            coinPubs.push({
+              coinPub: c,
+              amount: denom?.value,
+            });
+          }
           return await createRefreshGroup(
             ws,
             tx,
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts 
b/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts
index 5766883ae..a106c3d85 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { GetExchangeTosResult } from "@gnu-taler/taler-util";
+import { ExchangeTosStatus, GetExchangeTosResult } from 
"@gnu-taler/taler-util";
 
 export function buildTermsOfServiceState(
   tos: GetExchangeTosResult,
@@ -24,26 +24,7 @@ export function buildTermsOfServiceState(
     tos.content,
   );
 
-  const status: TermsStatus = buildTermsOfServiceStatus(
-    tos.content,
-    tos.acceptedEtag,
-    tos.currentEtag,
-  );
-
-  return { content, status, version: tos.currentEtag };
-}
-export function buildTermsOfServiceStatus(
-  content: string | undefined,
-  acceptedVersion: string | undefined,
-  currentVersion: string | undefined,
-): TermsStatus {
-  return !content
-    ? "notfound"
-    : !acceptedVersion
-    ? "new"
-    : acceptedVersion !== currentVersion
-    ? "changed"
-    : "accepted";
+  return { content, status: tos.tosStatus, version: tos.currentEtag };
 }
 
 function parseTermsOfServiceContent(
@@ -91,12 +72,10 @@ function parseTermsOfServiceContent(
 
 export type TermsState = {
   content: TermsDocument | undefined;
-  status: TermsStatus;
+  status: ExchangeTosStatus;
   version: string;
 };
 
-type TermsStatus = "new" | "accepted" | "changed" | "notfound";
-
 export type TermsDocument =
   | TermsDocumentXml
   | TermsDocumentHtml
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx 
b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
index c7f8ccb78..a7e03fd01 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
@@ -29,6 +29,7 @@ import {
 import { ExchangeXmlTos } from "../../components/ExchangeToS.js";
 import { ToggleHandler } from "../../mui/handlers.js";
 import { Button } from "../../mui/Button.js";
+import { ExchangeTosStatus } from "@gnu-taler/taler-util";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
@@ -100,7 +101,7 @@ export function ShowButtonsNonAcceptedTosView({
   if (!ableToReviewTermsOfService) {
     return (
       <Fragment>
-        {terms.status === "notfound" && (
+        {terms.status === ExchangeTosStatus.NotFound && (
           <section style={{ justifyContent: "space-around", display: "flex" }}>
             <WarningText>
               <i18n.Translate>
@@ -115,7 +116,7 @@ export function ShowButtonsNonAcceptedTosView({
 
   return (
     <Fragment>
-      {terms.status === "notfound" && (
+      {terms.status === ExchangeTosStatus.NotFound && (
         <section style={{ justifyContent: "space-around", display: "flex" }}>
           <WarningText>
             <i18n.Translate>
@@ -163,7 +164,7 @@ export function ShowTosContentView({
 
   return (
     <Fragment>
-      {terms.status !== "notfound" && !terms.content && (
+      {terms.status !== ExchangeTosStatus.NotFound && !terms.content && (
         <section style={{ justifyContent: "space-around", display: "flex" }}>
           <WarningBox>
             <i18n.Translate>
@@ -204,7 +205,7 @@ export function ShowTosContentView({
           </LinkSuccess>
         </section>
       )}
-      {termsAccepted && terms.status !== "notfound" && (
+      {termsAccepted && terms.status !== ExchangeTosStatus.NotFound && (
         <section style={{ justifyContent: "space-around", display: "flex" }}>
           <CheckboxOutlined
             name="terms"
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index 53bac579e..f4aea9cd6 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -15,7 +15,12 @@
  */
 
 /* eslint-disable react-hooks/rules-of-hooks */
-import { AmountJson, Amounts, ExchangeListItem } from "@gnu-taler/taler-util";
+import {
+  AmountJson,
+  Amounts,
+  ExchangeListItem,
+  ExchangeTosStatus,
+} from "@gnu-taler/taler-util";
 import { TalerError } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
@@ -173,10 +178,8 @@ function exchangeSelectionState(
     const [ageRestricted, setAgeRestricted] = useState(0);
     const currentExchange = selectedExchange.selected;
     const tosNeedToBeAccepted =
-      !currentExchange.tos.acceptedVersion ||
-      currentExchange.tos.currentVersion !==
-        currentExchange.tos.acceptedVersion;
-
+      currentExchange.tosStatus == ExchangeTosStatus.New ||
+      currentExchange.tosStatus == ExchangeTosStatus.Changed;
     /**
      * With the exchange and amount, ask the wallet the information
      * about the withdrawal
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
index d86771208..2b0690800 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
@@ -22,6 +22,7 @@
 import {
   Amounts,
   ExchangeFullDetails,
+  ExchangeTosStatus,
   GetExchangeTosResult,
 } from "@gnu-taler/taler-util";
 import { expect } from "chai";
@@ -169,6 +170,7 @@ describe("Withdraw CTA states", () => {
               content: "just accept",
               acceptedEtag: "v1",
               currentEtag: "v1",
+              tosStatus: ExchangeTosStatus.Accepted,
             }),
           } as any,
         ),
@@ -254,6 +256,7 @@ describe("Withdraw CTA states", () => {
               content: "just accept",
               acceptedEtag: "v1",
               currentEtag: "v2",
+              tosStatus: ExchangeTosStatus.Changed,
             }),
             setExchangeTosAccepted: async () => ({}),
           } as any,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
index 468d22d54..5c35151c8 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
@@ -37,6 +37,7 @@ import editIcon from "../../svg/edit_24px.svg";
 import { ExchangeDetails, WithdrawDetails } from "../../wallet/Transaction.js";
 import { TermsOfService } from "../../components/TermsOfService/index.js";
 import { State } from "./index.js";
+import { ExchangeTosStatus } from "@gnu-taler/taler-util";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
@@ -65,8 +66,7 @@ export function LoadingInfoView({ error }: 
State.LoadingInfoError): VNode {
 export function SuccessView(state: State.Success): VNode {
   const { i18n } = useTranslationContext();
   const currentTosVersionIsAccepted =
-    state.currentExchange.tos.acceptedVersion ===
-    state.currentExchange.tos.currentVersion;
+    state.currentExchange.tosStatus === ExchangeTosStatus.Accepted;
   return (
     <WalletAction>
       <LogoHeader />
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index c6f016e00..c0e35b17b 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -17,6 +17,7 @@
 import {
   Amounts,
   CoinDumpJson,
+  CoinStatus,
   ExchangeListItem,
   NotificationType,
 } from "@gnu-taler/taler-util";
@@ -86,7 +87,7 @@ type CoinsInfo = CoinDumpJson["coins"];
 type CalculatedCoinfInfo = {
   ageKeysCount: number | undefined;
   denom_value: number;
-  remain_value: number;
+  //remain_value: number;
   status: string;
   from_refresh: boolean;
   id: string;
@@ -143,10 +144,10 @@ export function View({
       prev[cur.exchange_base_url].push({
         ageKeysCount: cur.ageCommitmentProof?.proof.privateKeys.length,
         denom_value: parseFloat(Amounts.stringifyValue(denom)),
-        remain_value: parseFloat(
-          Amounts.stringifyValue(Amounts.parseOrThrow(cur.remaining_value)),
-        ),
-        status: cur.coin_suspended ? "suspended" : "ok",
+        // remain_value: parseFloat(
+        //   Amounts.stringifyValue(Amounts.parseOrThrow(cur.remaining_value)),
+        // ),
+        status: cur.coin_status,
         from_refresh: cur.refresh_parent_coin_pub !== undefined,
         id: cur.coin_pub,
       });
@@ -254,8 +255,8 @@ export function View({
 
         const coins = allcoins.reduce(
           (prev, cur) => {
-            if (cur.remain_value > 0) prev.usable.push(cur);
-            if (cur.remain_value === 0) prev.spent.push(cur);
+            if (cur.status === CoinStatus.Fresh) prev.usable.push(cur);
+            if (cur.status === CoinStatus.Dormant) prev.spent.push(cur);
             return prev;
           },
           {
@@ -356,7 +357,6 @@ function ShowAllCoins({
               <tr key={idx}>
                 <td>{c.id.substring(0, 5)}</td>
                 <td>{c.denom_value}</td>
-                <td>{c.remain_value}</td>
                 <td>{c.status}</td>
                 <td>{c.from_refresh ? "true" : "false"}</td>
                 <td>{String(c.ageKeysCount)}</td>
@@ -396,7 +396,6 @@ function ShowAllCoins({
               <tr key={idx}>
                 <td>{c.id.substring(0, 5)}</td>
                 <td>{c.denom_value}</td>
-                <td>{c.remain_value}</td>
                 <td>{c.status}</td>
                 <td>{c.from_refresh ? "true" : "false"}</td>
               </tr>
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx 
b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
index 80843ac27..8412c4a12 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
@@ -14,7 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { ExchangeListItem, WalletCoreVersion } from "@gnu-taler/taler-util";
+import {
+  ExchangeListItem,
+  ExchangeTosStatus,
+  WalletCoreVersion,
+} from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { Checkbox } from "../components/Checkbox.js";
 import { ErrorTalerOperation } from "../components/ErrorTalerOperation.js";
@@ -36,7 +40,6 @@ import { useBackupDeviceName } from 
"../hooks/useBackupDeviceName.js";
 import { useAutoOpenPermissions } from "../hooks/useAutoOpenPermissions.js";
 import { ToggleHandler } from "../mui/handlers.js";
 import { Pages } from "../NavigationBar.js";
-import { buildTermsOfServiceStatus } from 
"../components/TermsOfService/utils.js";
 import * as wxApi from "../wxApi.js";
 import { platform } from "../platform/api.js";
 import { useClipboardPermissions } from "../hooks/useClipboardPermissions.js";
@@ -181,26 +184,21 @@ export function SettingsView({
               <tbody>
                 {knownExchanges.map((e, idx) => {
                   function Status(): VNode {
-                    const status = buildTermsOfServiceStatus(
-                      e.tos.content,
-                      e.tos.acceptedVersion,
-                      e.tos.currentVersion,
-                    );
-                    switch (status) {
-                      case "accepted":
+                    switch (e.tosStatus) {
+                      case ExchangeTosStatus.Accepted:
                         return (
                           <SuccessText>
                             <i18n.Translate>ok</i18n.Translate>
                           </SuccessText>
                         );
-                      case "changed":
+                      case ExchangeTosStatus.Changed:
                         return (
                           <WarningText>
                             <i18n.Translate>changed</i18n.Translate>
                           </WarningText>
                         );
-                      case "new":
-                      case "notfound":
+                      case ExchangeTosStatus.New:
+                      case ExchangeTosStatus.NotFound:
                         return (
                           <DestructiveText>
                             <i18n.Translate>not accepted</i18n.Translate>

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]