gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (59ef010b0 -> 93e0f26b4)


From: gnunet
Subject: [taler-wallet-core] branch master updated (59ef010b0 -> 93e0f26b4)
Date: Thu, 14 Sep 2023 19:18:11 +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 59ef010b0 update testing
     new f4587c44f wallet-core: use typed microsecond timestamps in DB
     new 1ce53e1c2 wallet-core: consistently use usec timestamps in DB
     new 93e0f26b4 -remove unused record

The 3 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/time.ts                    |   4 +
 packages/taler-util/src/transactions-types.ts      |  24 ++-
 .../src/crypto/cryptoImplementation.ts             |  22 +-
 packages/taler-wallet-core/src/db.ts               | 234 +++++++++++++--------
 .../taler-wallet-core/src/operations/attention.ts  |   7 +-
 .../src/operations/backup/index.ts                 |  31 ++-
 .../taler-wallet-core/src/operations/common.ts     |  45 ++--
 .../taler-wallet-core/src/operations/deposits.ts   |  17 +-
 .../taler-wallet-core/src/operations/exchanges.ts  |  63 ++++--
 .../src/operations/pay-merchant.ts                 |  37 ++--
 .../src/operations/pay-peer-pull-credit.ts         |  27 ++-
 .../src/operations/pay-peer-pull-debit.ts          |   3 +-
 .../src/operations/pay-peer-push-credit.ts         |  46 ++--
 .../src/operations/pay-peer-push-debit.ts          |  11 +-
 .../taler-wallet-core/src/operations/pending.ts    | 112 ++++++----
 .../taler-wallet-core/src/operations/recoup.ts     |   5 +-
 .../taler-wallet-core/src/operations/refresh.ts    |  25 ++-
 .../taler-wallet-core/src/operations/reward.ts     |  16 +-
 .../src/operations/transactions.ts                 |  52 +++--
 .../src/operations/withdraw.test.ts                | 114 +++++-----
 .../taler-wallet-core/src/operations/withdraw.ts   |  29 ++-
 packages/taler-wallet-core/src/pending-types.ts    |  14 +-
 .../taler-wallet-core/src/util/denominations.ts    |   9 +-
 .../src/util/instructedAmountConversion.ts         |  17 +-
 24 files changed, 578 insertions(+), 386 deletions(-)

diff --git a/packages/taler-util/src/time.ts b/packages/taler-util/src/time.ts
index 46ed37637..a63f9b296 100644
--- a/packages/taler-util/src/time.ts
+++ b/packages/taler-util/src/time.ts
@@ -52,6 +52,10 @@ export interface TalerProtocolTimestamp {
   readonly _flavor?: typeof flavor_TalerProtocolTimestamp;
 }
 
+/**
+ * Precise timestamp, typically used in the wallet-core
+ * API but not in other Taler APIs so far.
+ */
 export interface TalerPreciseTimestamp {
   /**
    * Seconds (as integer) since epoch.
diff --git a/packages/taler-util/src/transactions-types.ts 
b/packages/taler-util/src/transactions-types.ts
index 304183ceb..63db206bd 100644
--- a/packages/taler-util/src/transactions-types.ts
+++ b/packages/taler-util/src/transactions-types.ts
@@ -67,7 +67,7 @@ export interface TransactionsRequest {
    */
   includeRefreshes?: boolean;
 
-  filterByState?: TransactionStateFilter
+  filterByState?: TransactionStateFilter;
 }
 
 export interface TransactionState {
@@ -629,6 +629,17 @@ export interface TransactionRefresh extends 
TransactionCommon {
   refreshOutputAmount: AmountString;
 }
 
+export interface DepositTransactionTrackingState {
+  // Raw wire transfer identifier of the deposit.
+  wireTransferId: string;
+  // When was the wire transfer given to the bank.
+  timestampExecuted: TalerProtocolTimestamp;
+  // Total amount transfer for this wtid (including fees)
+  amountRaw: AmountString;
+  // Wire fee amount for this exchange
+  wireFee: AmountString;
+}
+
 /**
  * Deposit transaction, which effectively sends
  * money from this wallet somewhere else.
@@ -662,16 +673,7 @@ export interface TransactionDeposit extends 
TransactionCommon {
    */
   deposited: boolean;
 
-  trackingState: Array<{
-    // Raw wire transfer identifier of the deposit.
-    wireTransferId: string;
-    // When was the wire transfer given to the bank.
-    timestampExecuted: TalerProtocolTimestamp;
-    // Total amount transfer for this wtid (including fees)
-    amountRaw: AmountString;
-    // Wire fee amount for this exchange
-    wireFee: AmountString;
-  }>;
+  trackingState: Array<DepositTransactionTrackingState>;
 }
 
 export interface TransactionByIdRequest {
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts 
b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index 35777e714..56392f090 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -87,7 +87,7 @@ import {
   WithdrawalPlanchet,
 } from "@gnu-taler/taler-util";
 // FIXME: Crypto should not use DB Types!
-import { DenominationRecord } from "../db.js";
+import { DenominationRecord, timestampProtocolFromDb } from "../db.js";
 import {
   CreateRecoupRefreshReqRequest,
   CreateRecoupReqRequest,
@@ -962,10 +962,22 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
     const value: AmountJson = Amounts.parseOrThrow(denom.value);
     const p = 
buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
       .put(decodeCrock(masterPub))
-      .put(timestampRoundedToBuffer(denom.stampStart))
-      .put(timestampRoundedToBuffer(denom.stampExpireWithdraw))
-      .put(timestampRoundedToBuffer(denom.stampExpireDeposit))
-      .put(timestampRoundedToBuffer(denom.stampExpireLegal))
+      .put(timestampRoundedToBuffer(timestampProtocolFromDb(denom.stampStart)))
+      .put(
+        timestampRoundedToBuffer(
+          timestampProtocolFromDb(denom.stampExpireWithdraw),
+        ),
+      )
+      .put(
+        timestampRoundedToBuffer(
+          timestampProtocolFromDb(denom.stampExpireDeposit),
+        ),
+      )
+      .put(
+        timestampRoundedToBuffer(
+          timestampProtocolFromDb(denom.stampExpireLegal),
+        ),
+      )
       .put(amountToBuffer(value))
       .put(amountToBuffer(denom.fees.feeWithdraw))
       .put(amountToBuffer(denom.fees.feeDeposit))
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 9bf9a29cc..597cdf5a5 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -27,8 +27,8 @@ import {
   structuredEncapsulate,
 } from "@gnu-taler/idb-bridge";
 import {
+  AbsoluteTime,
   AgeCommitmentProof,
-  AmountJson,
   AmountString,
   Amounts,
   AttentionInfo,
@@ -45,23 +45,20 @@ import {
   ExchangeAuditor,
   ExchangeGlobalFees,
   HashCodeString,
-  InternationalizedString,
   Logger,
-  MerchantContractTerms,
-  MerchantInfo,
   PayCoinSelection,
-  PeerContractTerms,
   RefreshReason,
   TalerErrorDetail,
   TalerPreciseTimestamp,
   TalerProtocolDuration,
   TalerProtocolTimestamp,
+  //TalerProtocolTimestamp,
   TransactionIdStr,
   UnblindedSignature,
   WireInfo,
   codecForAny,
 } from "@gnu-taler/taler-util";
-import { RetryInfo, TaskIdentifiers } from "./operations/common.js";
+import { DbRetryInfo, TaskIdentifiers } from "./operations/common.js";
 import {
   DbAccess,
   DbReadOnlyTransaction,
@@ -151,6 +148,91 @@ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
  */
 export const WALLET_DB_MINOR_VERSION = 1;
 
+declare const symDbProtocolTimestamp: unique symbol;
+
+declare const symDbPreciseTimestamp: unique symbol;
+
+/**
+ * Timestamp, stored as microseconds.
+ *
+ * Always rounded to a full second.
+ */
+export type DbProtocolTimestamp = number & { [symDbProtocolTimestamp]: true };
+
+/**
+ * Timestamp, stored as microseconds.
+ */
+export type DbPreciseTimestamp = number & { [symDbPreciseTimestamp]: true };
+
+const DB_TIMESTAMP_FOREVER = Number.MAX_SAFE_INTEGER;
+
+export function timestampPreciseFromDb(
+  dbTs: DbPreciseTimestamp,
+): TalerPreciseTimestamp {
+  return TalerPreciseTimestamp.fromMilliseconds(Math.floor(dbTs / 1000));
+}
+
+export function timestampOptionalPreciseFromDb(
+  dbTs: DbPreciseTimestamp | undefined,
+): TalerPreciseTimestamp | undefined {
+  if (!dbTs) {
+    return undefined;
+  }
+  return TalerPreciseTimestamp.fromMilliseconds(Math.floor(dbTs / 1000));
+}
+
+export function timestampPreciseToDb(
+  stamp: TalerPreciseTimestamp,
+): DbPreciseTimestamp {
+  if (stamp.t_s === "never") {
+    return DB_TIMESTAMP_FOREVER as DbPreciseTimestamp;
+  } else {
+    let tUs = stamp.t_s * 1000000;
+    if (stamp.off_us) {
+      tUs == stamp.off_us;
+    }
+    return tUs as DbPreciseTimestamp;
+  }
+}
+
+export function timestampProtocolToDb(
+  stamp: TalerProtocolTimestamp,
+): DbProtocolTimestamp {
+  if (stamp.t_s === "never") {
+    return DB_TIMESTAMP_FOREVER as DbProtocolTimestamp;
+  } else {
+    let tUs = stamp.t_s * 1000000;
+    return tUs as DbProtocolTimestamp;
+  }
+}
+
+export function timestampProtocolFromDb(
+  stamp: DbProtocolTimestamp,
+): TalerProtocolTimestamp {
+  return TalerProtocolTimestamp.fromSeconds(Math.floor(stamp / 1000000));
+}
+
+export function timestampAbsoluteFromDb(
+  stamp: DbProtocolTimestamp | DbPreciseTimestamp,
+): AbsoluteTime {
+  if (stamp >= DB_TIMESTAMP_FOREVER) {
+    return AbsoluteTime.never();
+  }
+  return AbsoluteTime.fromMilliseconds(Math.floor(stamp / 1000));
+}
+
+export function timestampOptionalAbsoluteFromDb(
+  stamp: DbProtocolTimestamp | DbPreciseTimestamp | undefined,
+): AbsoluteTime | undefined {
+  if (stamp == null) {
+    return undefined;
+  }
+  if (stamp >= DB_TIMESTAMP_FOREVER) {
+    return AbsoluteTime.never();
+  }
+  return AbsoluteTime.fromMilliseconds(Math.floor(stamp / 1000));
+}
+
 /**
  * Format of the operation status code: 0x0abc_nnnn
 
@@ -217,7 +299,7 @@ export enum WithdrawalGroupStatus {
    * Exchange is doing AML checks.
    */
   PendingAml = 0x0100_0006,
-  SuspendedAml = 0x0100_0006,
+  SuspendedAml = 0x0110_0006,
 
   /**
    * The corresponding withdraw record has been created.
@@ -268,14 +350,14 @@ export interface ReserveBankInfo {
    *
    * Set to undefined if that hasn't happened yet.
    */
-  timestampReserveInfoPosted: TalerPreciseTimestamp | undefined;
+  timestampReserveInfoPosted: DbPreciseTimestamp | undefined;
 
   /**
    * Time when the reserve was confirmed by the bank.
    *
    * Set to undefined if not confirmed yet.
    */
-  timestampBankConfirmed: TalerPreciseTimestamp | undefined;
+  timestampBankConfirmed: DbPreciseTimestamp | undefined;
 }
 
 /**
@@ -349,22 +431,22 @@ export interface DenominationRecord {
   /**
    * Validity start date of the denomination.
    */
-  stampStart: TalerProtocolTimestamp;
+  stampStart: DbProtocolTimestamp;
 
   /**
    * Date after which the currency can't be withdrawn anymore.
    */
-  stampExpireWithdraw: TalerProtocolTimestamp;
+  stampExpireWithdraw: DbProtocolTimestamp;
 
   /**
    * Date after the denomination officially doesn't exist anymore.
    */
-  stampExpireLegal: TalerProtocolTimestamp;
+  stampExpireLegal: DbProtocolTimestamp;
 
   /**
    * Data after which coins of this denomination can't be deposited anymore.
    */
-  stampExpireDeposit: TalerProtocolTimestamp;
+  stampExpireDeposit: DbProtocolTimestamp;
 
   /**
    * Signature by the exchange's master key over the denomination
@@ -406,7 +488,7 @@ export interface DenominationRecord {
    * Latest list issue date of the "/keys" response
    * that includes this denomination.
    */
-  listIssueDate: TalerProtocolTimestamp;
+  listIssueDate: DbProtocolTimestamp;
 }
 
 export namespace DenominationRecord {
@@ -418,10 +500,10 @@ export namespace DenominationRecord {
       feeRefresh: Amounts.stringify(d.fees.feeRefresh),
       feeRefund: Amounts.stringify(d.fees.feeRefund),
       feeWithdraw: Amounts.stringify(d.fees.feeWithdraw),
-      stampExpireDeposit: d.stampExpireDeposit,
-      stampExpireLegal: d.stampExpireLegal,
-      stampExpireWithdraw: d.stampExpireWithdraw,
-      stampStart: d.stampStart,
+      stampExpireDeposit: timestampProtocolFromDb(d.stampExpireDeposit),
+      stampExpireLegal: timestampProtocolFromDb(d.stampExpireLegal),
+      stampExpireWithdraw: timestampProtocolFromDb(d.stampExpireWithdraw),
+      stampStart: timestampProtocolFromDb(d.stampStart),
       value: Amounts.stringify(d.value),
       exchangeBaseUrl: d.exchangeBaseUrl,
     };
@@ -429,9 +511,9 @@ export namespace DenominationRecord {
 }
 
 export interface ExchangeSignkeysRecord {
-  stampStart: TalerProtocolTimestamp;
-  stampExpire: TalerProtocolTimestamp;
-  stampEnd: TalerProtocolTimestamp;
+  stampStart: DbProtocolTimestamp;
+  stampExpire: DbProtocolTimestamp;
+  stampEnd: DbProtocolTimestamp;
   signkeyPub: EddsaPublicKeyString;
   masterSig: EddsaSignatureString;
 
@@ -488,7 +570,7 @@ export interface ExchangeDetailsRecord {
   tosAccepted:
     | {
         etag: string;
-        timestamp: TalerPreciseTimestamp;
+        timestamp: DbPreciseTimestamp;
       }
     | undefined;
 
@@ -500,25 +582,6 @@ export interface ExchangeDetailsRecord {
   ageMask?: number;
 }
 
-export interface ExchangeTosRecord {
-  exchangeBaseUrl: string;
-
-  etag: string;
-
-  /**
-   * Terms of service text or undefined if not downloaded yet.
-   *
-   * This is just used as a cache of the last downloaded ToS.
-   *
-   */
-  termsOfServiceText: string | undefined;
-
-  /**
-   * Content-type of the last downloaded termsOfServiceText.
-   */
-  termsOfServiceContentType: string | undefined;
-}
-
 export interface ExchangeDetailsPointer {
   masterPublicKey: string;
 
@@ -528,7 +591,7 @@ export interface ExchangeDetailsPointer {
    * Timestamp when the (masterPublicKey, currency) pointer
    * has been updated.
    */
-  updateClock: TalerPreciseTimestamp;
+  updateClock: DbPreciseTimestamp;
 }
 
 export enum ExchangeEntryDbRecordStatus {
@@ -548,11 +611,6 @@ export enum ExchangeEntryDbUpdateStatus {
   ReadyUpdate = 7,
 }
 
-/**
- * Timestamp stored as a IEEE 754 double, in milliseconds.
- */
-export type DbIndexableTimestampMs = number;
-
 /**
  * Exchange record as stored in the wallet's database.
  */
@@ -567,7 +625,7 @@ export interface ExchangeEntryRecord {
    *
    * Used mostly in the UI to suggest exchanges.
    */
-  lastWithdrawal?: TalerPreciseTimestamp;
+  lastWithdrawal?: DbPreciseTimestamp;
 
   /**
    * Pointer to the current exchange details.
@@ -588,17 +646,12 @@ export interface ExchangeEntryRecord {
   /**
    * Last time when the exchange /keys info was updated.
    */
-  lastUpdate: TalerPreciseTimestamp | undefined;
+  lastUpdate: DbPreciseTimestamp | undefined;
 
   /**
    * Next scheduled update for the exchange.
-   *
-   * (This field must always be present, so we can index on the timestamp.)
-   *
-   * FIXME: To index on the timestamp, this needs to be a number of
-   * binary timestamp!
    */
-  nextUpdateStampMs: DbIndexableTimestampMs;
+  nextUpdateStamp: DbPreciseTimestamp;
 
   lastKeysEtag: string | undefined;
 
@@ -608,7 +661,7 @@ export interface ExchangeEntryRecord {
    * Updated whenever the exchange's denominations are updated or when
    * the refresh check has been done.
    */
-  nextRefreshCheckStampMs: DbIndexableTimestampMs;
+  nextRefreshCheckStamp: DbPreciseTimestamp;
 
   /**
    * Public key of the reserve that we're currently using for
@@ -816,7 +869,7 @@ export interface RewardRecord {
    * Has the user accepted the tip?  Only after the tip has been accepted coins
    * withdrawn from the tip may be used.
    */
-  acceptedTimestamp: TalerPreciseTimestamp | undefined;
+  acceptedTimestamp: DbPreciseTimestamp | undefined;
 
   /**
    * The tipped amount.
@@ -831,7 +884,7 @@ export interface RewardRecord {
   /**
    * Timestamp, the tip can't be picked up anymore after this deadline.
    */
-  rewardExpiration: TalerProtocolTimestamp;
+  rewardExpiration: DbProtocolTimestamp;
 
   /**
    * The exchange that will sign our coins, chosen by the merchant.
@@ -869,7 +922,7 @@ export interface RewardRecord {
    */
   merchantRewardId: string;
 
-  createdTimestamp: TalerPreciseTimestamp;
+  createdTimestamp: DbPreciseTimestamp;
 
   /**
    * The url to be redirected after the tip is accepted.
@@ -880,7 +933,7 @@ export interface RewardRecord {
    * Timestamp for when the wallet finished picking up the tip
    * from the merchant.
    */
-  pickedUpTimestamp: TalerPreciseTimestamp | undefined;
+  pickedUpTimestamp: DbPreciseTimestamp | undefined;
 
   status: RewardRecordStatus;
 }
@@ -978,12 +1031,12 @@ export interface RefreshGroupRecord {
    */
   statusPerCoin: RefreshCoinStatus[];
 
-  timestampCreated: TalerPreciseTimestamp;
+  timestampCreated: DbPreciseTimestamp;
 
   /**
    * Timestamp when the refresh session finished.
    */
-  timestampFinished: TalerPreciseTimestamp | undefined;
+  timestampFinished: DbPreciseTimestamp | undefined;
 }
 
 /**
@@ -1208,7 +1261,7 @@ export interface PurchaseRecord {
    * Timestamp of the first time that sending a payment to the merchant
    * for this purchase was successful.
    */
-  timestampFirstSuccessfulPay: TalerPreciseTimestamp | undefined;
+  timestampFirstSuccessfulPay: DbPreciseTimestamp | undefined;
 
   merchantPaySig: string | undefined;
 
@@ -1223,19 +1276,19 @@ export interface PurchaseRecord {
   /**
    * When was the purchase record created?
    */
-  timestamp: TalerPreciseTimestamp;
+  timestamp: DbPreciseTimestamp;
 
   /**
    * When was the purchase made?
    * Refers to the time that the user accepted.
    */
-  timestampAccept: TalerPreciseTimestamp | undefined;
+  timestampAccept: DbPreciseTimestamp | undefined;
 
   /**
    * When was the last refund made?
    * Set to 0 if no refund was made on the purchase.
    */
-  timestampLastRefundStatus: TalerPreciseTimestamp | undefined;
+  timestampLastRefundStatus: DbPreciseTimestamp | undefined;
 
   /**
    * Last session signature that we submitted to /pay (if any).
@@ -1245,7 +1298,7 @@ export interface PurchaseRecord {
   /**
    * Continue querying the refund status until this deadline has expired.
    */
-  autoRefundDeadline: TalerProtocolTimestamp | undefined;
+  autoRefundDeadline: DbProtocolTimestamp | undefined;
 
   /**
    * How much merchant has refund to be taken but the wallet
@@ -1285,12 +1338,12 @@ export interface WalletBackupConfState {
   /**
    * Timestamp stored in the last backup.
    */
-  lastBackupTimestamp?: TalerPreciseTimestamp;
+  lastBackupTimestamp?: DbPreciseTimestamp;
 
   /**
    * Last time we tried to do a backup.
    */
-  lastBackupCheckTimestamp?: TalerPreciseTimestamp;
+  lastBackupCheckTimestamp?: DbPreciseTimestamp;
   lastBackupNonce?: string;
 }
 
@@ -1398,12 +1451,12 @@ export interface WithdrawalGroupRecord {
    * When was the withdrawal operation started started?
    * Timestamp in milliseconds.
    */
-  timestampStart: TalerPreciseTimestamp;
+  timestampStart: DbPreciseTimestamp;
 
   /**
    * When was the withdrawal operation completed?
    */
-  timestampFinish?: TalerPreciseTimestamp;
+  timestampFinish?: DbPreciseTimestamp;
 
   /**
    * Current status of the reserve.
@@ -1494,9 +1547,9 @@ export interface RecoupGroupRecord {
 
   exchangeBaseUrl: string;
 
-  timestampStarted: TalerPreciseTimestamp;
+  timestampStarted: DbPreciseTimestamp;
 
-  timestampFinished: TalerPreciseTimestamp | undefined;
+  timestampFinished: DbPreciseTimestamp | undefined;
 
   /**
    * Public keys that identify the coins being recouped
@@ -1530,7 +1583,7 @@ export type BackupProviderState =
     }
   | {
       tag: BackupProviderStateTag.Ready;
-      nextBackupTimestamp: TalerPreciseTimestamp;
+      nextBackupTimestamp: DbPreciseTimestamp;
     }
   | {
       tag: BackupProviderStateTag.Retrying;
@@ -1575,7 +1628,7 @@ export interface BackupProviderRecord {
    * Does NOT correspond to the timestamp of the backup,
    * which only changes when the backup content changes.
    */
-  lastBackupCycleTimestamp?: TalerPreciseTimestamp;
+  lastBackupCycleTimestamp?: DbPreciseTimestamp;
 
   /**
    * Proposal that we're currently trying to pay for.
@@ -1626,7 +1679,7 @@ export interface DepositTrackingInfo {
   // Raw wire transfer identifier of the deposit.
   wireTransferId: string;
   // When was the wire transfer given to the bank.
-  timestampExecuted: TalerProtocolTimestamp;
+  timestampExecuted: DbProtocolTimestamp;
   // Total amount transfer for this wtid (including fees)
   amountRaw: AmountString;
   // Wire fee amount for this exchange
@@ -1648,7 +1701,7 @@ export interface DepositGroupRecord {
    */
   amount: AmountString;
 
-  wireTransferDeadline: TalerProtocolTimestamp;
+  wireTransferDeadline: DbProtocolTimestamp;
 
   merchantPub: string;
   merchantPriv: string;
@@ -1678,9 +1731,9 @@ export interface DepositGroupRecord {
    */
   counterpartyEffectiveDepositAmount: AmountString;
 
-  timestampCreated: TalerPreciseTimestamp;
+  timestampCreated: DbPreciseTimestamp;
 
-  timestampFinished: TalerPreciseTimestamp | undefined;
+  timestampFinished: DbPreciseTimestamp | undefined;
 
   operationStatus: DepositOperationStatus;
 
@@ -1789,9 +1842,9 @@ export interface PeerPushDebitRecord {
    */
   contractEncNonce: string;
 
-  purseExpiration: TalerProtocolTimestamp;
+  purseExpiration: DbProtocolTimestamp;
 
-  timestampCreated: TalerPreciseTimestamp;
+  timestampCreated: DbPreciseTimestamp;
 
   abortRefreshGroupId?: string;
 
@@ -1864,7 +1917,7 @@ export interface PeerPullCreditRecord {
 
   contractEncNonce: string;
 
-  mergeTimestamp: TalerPreciseTimestamp;
+  mergeTimestamp: DbPreciseTimestamp;
 
   mergeReserveRowId: number;
 
@@ -1916,7 +1969,7 @@ export interface PeerPushPaymentIncomingRecord {
 
   contractPriv: string;
 
-  timestamp: TalerPreciseTimestamp;
+  timestamp: DbPreciseTimestamp;
 
   estimatedAmountEffective: AmountString;
 
@@ -1988,7 +2041,7 @@ export interface PeerPullPaymentIncomingRecord {
 
   contractTermsHash: string;
 
-  timestampCreated: TalerPreciseTimestamp;
+  timestampCreated: DbPreciseTimestamp;
 
   /**
    * Contract priv that we got from the other party.
@@ -2035,7 +2088,7 @@ export interface OperationRetryRecord {
 
   lastError?: TalerErrorDetail;
 
-  retryInfo: RetryInfo;
+  retryInfo: DbRetryInfo;
 }
 
 /**
@@ -2088,14 +2141,13 @@ export interface UserAttentionRecord {
 
   /**
    * When the notification was created.
-   * FIXME: This should be a TalerPreciseTimestamp
    */
-  createdMs: number;
+  created: DbPreciseTimestamp;
 
   /**
    * When the user mark this notification as read.
    */
-  read: TalerPreciseTimestamp | undefined;
+  read: DbPreciseTimestamp | undefined;
 }
 
 export interface DbExchangeHandle {
@@ -2139,7 +2191,7 @@ export interface RefundGroupRecord {
   /**
    * Timestamp when the refund group was created.
    */
-  timestampCreated: TalerPreciseTimestamp;
+  timestampCreated: DbPreciseTimestamp;
 
   proposalId: string;
 
@@ -2191,12 +2243,12 @@ export interface RefundItemRecord {
   /**
    * Execution time as claimed by the merchant
    */
-  executionTime: TalerProtocolTimestamp;
+  executionTime: DbProtocolTimestamp;
 
   /**
    * Time when the wallet became aware of the refund.
    */
-  obtainedTime: TalerPreciseTimestamp;
+  obtainedTime: DbPreciseTimestamp;
 
   refundAmount: AmountString;
 
diff --git a/packages/taler-wallet-core/src/operations/attention.ts 
b/packages/taler-wallet-core/src/operations/attention.ts
index 7d84b43ef..92d69e93e 100644
--- a/packages/taler-wallet-core/src/operations/attention.ts
+++ b/packages/taler-wallet-core/src/operations/attention.ts
@@ -31,6 +31,7 @@ import {
   UserAttentionUnreadList,
 } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../internal-wallet-state.js";
+import { timestampPreciseFromDb, timestampPreciseToDb } from "../index.js";
 
 const logger = new Logger("operations/attention.ts");
 
@@ -74,7 +75,7 @@ export async function getUserAttentions(
           return;
         pending.push({
           info: x.info,
-          when: TalerPreciseTimestamp.fromMilliseconds(x.createdMs),
+          when: timestampPreciseFromDb(x.created),
           read: x.read !== undefined,
         });
       });
@@ -94,7 +95,7 @@ export async function markAttentionRequestAsRead(
       if (!ua) throw Error("attention request not found");
       tx.userAttention.put({
         ...ua,
-        read: TalerPreciseTimestamp.now(),
+        read: timestampPreciseToDb(TalerPreciseTimestamp.now()),
       });
     });
 }
@@ -117,7 +118,7 @@ export async function addAttentionRequest(
       await tx.userAttention.put({
         info,
         entityId,
-        createdMs: AbsoluteTime.now().t_ms as number,
+        created: timestampPreciseToDb(TalerPreciseTimestamp.now()),
         read: undefined,
       });
     });
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts 
b/packages/taler-wallet-core/src/operations/backup/index.ts
index a5e8dbd42..7a2771c57 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -84,6 +84,9 @@ import {
   ConfigRecord,
   ConfigRecordKey,
   WalletBackupConfState,
+  timestampOptionalPreciseFromDb,
+  timestampPreciseFromDb,
+  timestampPreciseToDb,
 } from "../../db.js";
 import { InternalWalletState } from "../../internal-wallet-state.js";
 import { assertUnreachable } from "../../util/assertUnreachable.js";
@@ -259,10 +262,12 @@ async function runBackupCycleForProvider(
         if (!prov) {
           return;
         }
-        prov.lastBackupCycleTimestamp = TalerPreciseTimestamp.now();
+        prov.lastBackupCycleTimestamp = timestampPreciseToDb(
+          TalerPreciseTimestamp.now(),
+        );
         prov.state = {
           tag: BackupProviderStateTag.Ready,
-          nextBackupTimestamp: getNextBackupTimestamp(),
+          nextBackupTimestamp: timestampPreciseToDb(getNextBackupTimestamp()),
         };
         await tx.backupProviders.put(prov);
       });
@@ -361,10 +366,12 @@ async function runBackupCycleForProvider(
           return;
         }
         prov.lastBackupHash = encodeCrock(currentBackupHash);
-        prov.lastBackupCycleTimestamp = TalerPreciseTimestamp.now();
+        prov.lastBackupCycleTimestamp = timestampPreciseToDb(
+          TalerPreciseTimestamp.now(),
+        );
         prov.state = {
           tag: BackupProviderStateTag.Ready,
-          nextBackupTimestamp: getNextBackupTimestamp(),
+          nextBackupTimestamp: timestampPreciseToDb(getNextBackupTimestamp()),
         };
         await tx.backupProviders.put(prov);
       });
@@ -594,7 +601,9 @@ export async function addBackupProvider(
         if (req.activate) {
           oldProv.state = {
             tag: BackupProviderStateTag.Ready,
-            nextBackupTimestamp: TalerPreciseTimestamp.now(),
+            nextBackupTimestamp: timestampPreciseToDb(
+              TalerPreciseTimestamp.now(),
+            ),
           };
           logger.info("setting existing backup provider to active");
           await tx.backupProviders.put(oldProv);
@@ -616,7 +625,9 @@ export async function addBackupProvider(
       if (req.activate) {
         state = {
           tag: BackupProviderStateTag.Ready,
-          nextBackupTimestamp: TalerPreciseTimestamp.now(),
+          nextBackupTimestamp: timestampPreciseToDb(
+            TalerPreciseTimestamp.now(),
+          ),
         };
       } else {
         state = {
@@ -840,7 +851,9 @@ export async function getBackupInfo(
     providers.push({
       active: x.provider.state.tag !== BackupProviderStateTag.Provisional,
       syncProviderBaseUrl: x.provider.baseUrl,
-      lastSuccessfulBackupTimestamp: x.provider.lastBackupCycleTimestamp,
+      lastSuccessfulBackupTimestamp: timestampOptionalPreciseFromDb(
+        x.provider.lastBackupCycleTimestamp,
+      ),
       paymentProposalIds: x.provider.paymentProposalIds,
       lastError:
         x.provider.state.tag === BackupProviderStateTag.Retrying
@@ -917,7 +930,9 @@ async function backupRecoveryTheirs(
             shouldRetryFreshProposal: false,
             state: {
               tag: BackupProviderStateTag.Ready,
-              nextBackupTimestamp: TalerPreciseTimestamp.now(),
+              nextBackupTimestamp: timestampPreciseToDb(
+                TalerPreciseTimestamp.now(),
+              ),
             },
             uids: [encodeCrock(getRandomBytes(32))],
           });
diff --git a/packages/taler-wallet-core/src/operations/common.ts 
b/packages/taler-wallet-core/src/operations/common.ts
index 50dd3dc5c..e8e492c08 100644
--- a/packages/taler-wallet-core/src/operations/common.ts
+++ b/packages/taler-wallet-core/src/operations/common.ts
@@ -40,6 +40,7 @@ import {
   TalerError,
   TalerErrorCode,
   TalerErrorDetail,
+  TalerPreciseTimestamp,
   TombstoneIdStr,
   TransactionIdStr,
   TransactionType,
@@ -49,6 +50,7 @@ import { CryptoApiStoppedError } from 
"../crypto/workers/crypto-dispatcher.js";
 import {
   BackupProviderRecord,
   CoinRecord,
+  DbPreciseTimestamp,
   DepositGroupRecord,
   ExchangeDetailsRecord,
   ExchangeEntryDbRecordStatus,
@@ -62,6 +64,7 @@ import {
   RecoupGroupRecord,
   RefreshGroupRecord,
   RewardRecord,
+  timestampPreciseToDb,
   WalletStoresV1,
   WithdrawalGroupRecord,
 } from "../db.js";
@@ -360,11 +363,11 @@ async function storePendingTaskError(
       retryRecord = {
         id: pendingTaskId,
         lastError: e,
-        retryInfo: RetryInfo.reset(),
+        retryInfo: DbRetryInfo.reset(),
       };
     } else {
       retryRecord.lastError = e;
-      retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo);
+      retryRecord.retryInfo = DbRetryInfo.increment(retryRecord.retryInfo);
     }
     await tx.operationRetries.put(retryRecord);
     return taskToTransactionNotification(ws, tx, pendingTaskId, e);
@@ -383,7 +386,7 @@ export async function resetPendingTaskTimeout(
     if (retryRecord) {
       // Note that we don't reset the lastError, it should still be visible
       // while the retry runs.
-      retryRecord.retryInfo = RetryInfo.reset();
+      retryRecord.retryInfo = DbRetryInfo.reset();
       await tx.operationRetries.put(retryRecord);
     }
     return taskToTransactionNotification(ws, tx, pendingTaskId, undefined);
@@ -403,14 +406,14 @@ async function storePendingTaskPending(
     if (!retryRecord) {
       retryRecord = {
         id: pendingTaskId,
-        retryInfo: RetryInfo.reset(),
+        retryInfo: DbRetryInfo.reset(),
       };
     } else {
       if (retryRecord.lastError) {
         hadError = true;
       }
       delete retryRecord.lastError;
-      retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo);
+      retryRecord.retryInfo = DbRetryInfo.increment(retryRecord.retryInfo);
     }
     await tx.operationRetries.put(retryRecord);
     if (hadError) {
@@ -736,9 +739,9 @@ export interface TaskRunLongpollResult {
   type: TaskRunResultType.Longpoll;
 }
 
-export interface RetryInfo {
-  firstTry: AbsoluteTime;
-  nextRetry: AbsoluteTime;
+export interface DbRetryInfo {
+  firstTry: DbPreciseTimestamp;
+  nextRetry: DbPreciseTimestamp;
   retryCounter: number;
 }
 
@@ -755,7 +758,7 @@ const defaultRetryPolicy: RetryPolicy = {
 };
 
 function updateTimeout(
-  r: RetryInfo,
+  r: DbRetryInfo,
   p: RetryPolicy = defaultRetryPolicy,
 ): void {
   const now = AbsoluteTime.now();
@@ -763,7 +766,9 @@ function updateTimeout(
     throw Error("assertion failed");
   }
   if (p.backoffDelta.d_ms === "forever") {
-    r.nextRetry = AbsoluteTime.never();
+    r.nextRetry = timestampPreciseToDb(
+      AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
+    );
     return;
   }
 
@@ -775,12 +780,12 @@ function updateTimeout(
     (p.maxTimeout.d_ms === "forever"
       ? nextIncrement
       : Math.min(p.maxTimeout.d_ms, nextIncrement));
-  r.nextRetry = AbsoluteTime.fromMilliseconds(t);
+  r.nextRetry = 
timestampPreciseToDb(TalerPreciseTimestamp.fromMilliseconds(t));
 }
 
-export namespace RetryInfo {
+export namespace DbRetryInfo {
   export function getDuration(
-    r: RetryInfo | undefined,
+    r: DbRetryInfo | undefined,
     p: RetryPolicy = defaultRetryPolicy,
   ): Duration {
     if (!r) {
@@ -797,11 +802,11 @@ export namespace RetryInfo {
     };
   }
 
-  export function reset(p: RetryPolicy = defaultRetryPolicy): RetryInfo {
-    const now = AbsoluteTime.now();
-    const info = {
-      firstTry: now,
-      nextRetry: now,
+  export function reset(p: RetryPolicy = defaultRetryPolicy): DbRetryInfo {
+    const now = TalerPreciseTimestamp.now();
+    const info: DbRetryInfo = {
+      firstTry: timestampPreciseToDb(now),
+      nextRetry: timestampPreciseToDb(now),
       retryCounter: 0,
     };
     updateTimeout(info, p);
@@ -809,9 +814,9 @@ export namespace RetryInfo {
   }
 
   export function increment(
-    r: RetryInfo | undefined,
+    r: DbRetryInfo | undefined,
     p: RetryPolicy = defaultRetryPolicy,
-  ): RetryInfo {
+  ): DbRetryInfo {
     if (!r) {
       return reset(p);
     }
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index 2de8f30a1..111d15989 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -73,6 +73,9 @@ import {
   RefreshOperationStatus,
   createRefreshGroup,
   getTotalRefreshCost,
+  timestampPreciseToDb,
+  timestampProtocolFromDb,
+  timestampProtocolToDb,
 } from "../index.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
@@ -799,7 +802,7 @@ async function processDepositGroupPendingTrack(
             amountRaw: Amounts.stringify(raw),
             wireFee: Amounts.stringify(wireFee),
             exchangePub: track.exchange_pub,
-            timestampExecuted: track.execution_time,
+            timestampExecuted: timestampProtocolToDb(track.execution_time),
             wireTransferId: track.wtid,
           },
           id: track.exchange_sig,
@@ -857,7 +860,9 @@ async function processDepositGroupPendingTrack(
         }
       }
       if (allWired) {
-        dg.timestampFinished = TalerPreciseTimestamp.now();
+        dg.timestampFinished = timestampPreciseToDb(
+          TalerPreciseTimestamp.now(),
+        );
         dg.operationStatus = DepositOperationStatus.Finished;
         await tx.depositGroups.put(dg);
       }
@@ -1375,7 +1380,9 @@ export async function createDepositGroup(
     amount: contractData.amount,
     noncePriv: noncePair.priv,
     noncePub: noncePair.pub,
-    timestampCreated: AbsoluteTime.toPreciseTimestamp(now),
+    timestampCreated: timestampPreciseToDb(
+      AbsoluteTime.toPreciseTimestamp(now),
+    ),
     timestampFinished: undefined,
     statusPerCoin: payCoinSel.coinSel.coinPubs.map(
       () => DepositElementStatus.DepositPending,
@@ -1388,7 +1395,9 @@ export async function createDepositGroup(
     counterpartyEffectiveDepositAmount: Amounts.stringify(
       counterpartyEffectiveDepositAmount,
     ),
-    wireTransferDeadline: contractTerms.wire_transfer_deadline,
+    wireTransferDeadline: timestampProtocolToDb(
+      contractTerms.wire_transfer_deadline,
+    ),
     wire: {
       payto_uri: req.depositPaytoUri,
       salt: wireSalt,
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 43a08ed3b..5e966b719 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -74,6 +74,9 @@ import {
   ExchangeEntryDbRecordStatus,
   ExchangeEntryDbUpdateStatus,
   isWithdrawableDenom,
+  timestampPreciseFromDb,
+  timestampPreciseToDb,
+  timestampProtocolToDb,
   WalletDbReadWriteTransaction,
 } from "../index.js";
 import { InternalWalletState, TrustInfo } from "../internal-wallet-state.js";
@@ -174,7 +177,7 @@ export async function acceptExchangeTermsOfService(
       if (d) {
         d.tosAccepted = {
           etag: etag || d.tosCurrentEtag,
-          timestamp: TalerPreciseTimestamp.now(),
+          timestamp: timestampPreciseToDb(TalerPreciseTimestamp.now()),
         };
         await tx.exchangeDetails.put(d);
       }
@@ -316,8 +319,12 @@ export async function addPresetExchangeEntry(
       detailsPointer: undefined,
       lastUpdate: undefined,
       lastKeysEtag: undefined,
-      nextRefreshCheckStampMs: AbsoluteTime.getStampMsNever(),
-      nextUpdateStampMs: AbsoluteTime.getStampMsNever(),
+      nextRefreshCheckStamp: timestampPreciseToDb(
+        AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
+      ),
+      nextUpdateStamp: timestampPreciseToDb(
+        AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
+      ),
     };
     await tx.exchanges.put(r);
   }
@@ -343,8 +350,12 @@ export async function provideExchangeRecordInTx(
       baseUrl: baseUrl,
       detailsPointer: undefined,
       lastUpdate: undefined,
-      nextUpdateStampMs: AbsoluteTime.getStampMsNever(),
-      nextRefreshCheckStampMs: AbsoluteTime.getStampMsNever(),
+      nextUpdateStamp: timestampPreciseToDb(
+        AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
+      ),
+      nextRefreshCheckStamp: timestampPreciseToDb(
+        AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
+      ),
       lastKeysEtag: undefined,
     };
     await tx.exchanges.put(r);
@@ -445,13 +456,19 @@ async function downloadExchangeKeysInfo(
             isRevoked: false,
             value: Amounts.stringify(value),
             currency: value.currency,
-            stampExpireDeposit: denomIn.stamp_expire_deposit,
-            stampExpireLegal: denomIn.stamp_expire_legal,
-            stampExpireWithdraw: denomIn.stamp_expire_withdraw,
-            stampStart: denomIn.stamp_start,
+            stampExpireDeposit: timestampProtocolToDb(
+              denomIn.stamp_expire_deposit,
+            ),
+            stampExpireLegal: 
timestampProtocolToDb(denomIn.stamp_expire_legal),
+            stampExpireWithdraw: timestampProtocolToDb(
+              denomIn.stamp_expire_withdraw,
+            ),
+            stampStart: timestampProtocolToDb(denomIn.stamp_start),
             verificationStatus: DenominationVerificationStatus.Unverified,
             masterSig: denomIn.master_sig,
-            listIssueDate: exchangeKeysJsonUnchecked.list_issue_date,
+            listIssueDate: timestampProtocolToDb(
+              exchangeKeysJsonUnchecked.list_issue_date,
+            ),
             fees: {
               feeDeposit: Amounts.stringify(denomGroup.fee_deposit),
               feeRefresh: Amounts.stringify(denomGroup.fee_refresh),
@@ -613,7 +630,9 @@ export async function updateExchangeFromUrlHandler(
     !forceNow &&
     exchangeDetails !== undefined &&
     !AbsoluteTime.isExpired(
-      AbsoluteTime.fromStampMs(exchange.nextUpdateStampMs),
+      AbsoluteTime.fromPreciseTimestamp(
+        timestampPreciseFromDb(exchange.nextUpdateStamp),
+      ),
     )
   ) {
     logger.trace("using existing exchange info");
@@ -753,17 +772,21 @@ export async function updateExchangeFromUrlHandler(
       if (existingDetails?.rowId) {
         newDetails.rowId = existingDetails.rowId;
       }
-      r.lastUpdate = TalerPreciseTimestamp.now();
-      r.nextUpdateStampMs = AbsoluteTime.toStampMs(
-        AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry),
+      r.lastUpdate = timestampPreciseToDb(TalerPreciseTimestamp.now());
+      r.nextUpdateStamp = timestampPreciseToDb(
+        AbsoluteTime.toPreciseTimestamp(
+          AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry),
+        ),
       );
       // New denominations might be available.
-      r.nextRefreshCheckStampMs = AbsoluteTime.getStampMsNow();
+      r.nextRefreshCheckStamp = timestampPreciseToDb(
+        TalerPreciseTimestamp.now(),
+      );
       if (detailsPointerChanged) {
         r.detailsPointer = {
           currency: newDetails.currency,
           masterPublicKey: newDetails.masterPublicKey,
-          updateClock: TalerPreciseTimestamp.now(),
+          updateClock: timestampPreciseToDb(TalerPreciseTimestamp.now()),
         };
       }
       await tx.exchanges.put(r);
@@ -776,9 +799,9 @@ export async function updateExchangeFromUrlHandler(
           exchangeDetailsRowId: drRowId.key,
           masterSig: sk.master_sig,
           signkeyPub: sk.key,
-          stampEnd: sk.stamp_end,
-          stampExpire: sk.stamp_expire,
-          stampStart: sk.stamp_start,
+          stampEnd: timestampProtocolToDb(sk.stamp_end),
+          stampExpire: timestampProtocolToDb(sk.stamp_expire),
+          stampStart: timestampProtocolToDb(sk.stamp_start),
         });
       }
 
@@ -813,7 +836,7 @@ export async function updateExchangeFromUrlHandler(
               );
             }
           } else {
-            x.listIssueDate = keysInfo.listIssueDate;
+            x.listIssueDate = timestampProtocolToDb(keysInfo.listIssueDate);
             if (!x.isOffered) {
               x.isOffered = true;
               logger.info(
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts 
b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index fe0cbeda0..157541ed3 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -103,6 +103,9 @@ import {
   RefundGroupStatus,
   RefundItemRecord,
   RefundItemStatus,
+  timestampPreciseToDb,
+  timestampProtocolFromDb,
+  timestampProtocolToDb,
 } from "../index.js";
 import {
   EXCHANGE_COINS_LOCK,
@@ -114,7 +117,7 @@ import { checkDbInvariant } from "../util/invariants.js";
 import { GetReadOnlyAccess } from "../util/query.js";
 import {
   constructTaskIdentifier,
-  RetryInfo,
+  DbRetryInfo,
   runLongpollAsync,
   runTaskWithErrorReporting,
   spendCoins,
@@ -216,11 +219,13 @@ async function failProposalPermanently(
   notifyTransition(ws, transactionId, transitionInfo);
 }
 
-function getProposalRequestTimeout(retryInfo?: RetryInfo): Duration {
+function getProposalRequestTimeout(retryInfo?: DbRetryInfo): Duration {
   return Duration.clamp({
     lower: Duration.fromSpec({ seconds: 1 }),
     upper: Duration.fromSpec({ seconds: 60 }),
-    value: retryInfo ? RetryInfo.getDuration(retryInfo) : 
Duration.fromSpec({}),
+    value: retryInfo
+      ? DbRetryInfo.getDuration(retryInfo)
+      : Duration.fromSpec({}),
   });
 }
 
@@ -644,7 +649,7 @@ async function createPurchase(
     noncePriv: priv,
     noncePub: pub,
     claimToken,
-    timestamp: TalerPreciseTimestamp.now(),
+    timestamp: timestampPreciseToDb(TalerPreciseTimestamp.now()),
     merchantBaseUrl,
     orderId,
     proposalId: proposalId,
@@ -717,7 +722,7 @@ async function storeFirstPaySuccess(
       if (purchase.purchaseStatus === PurchaseStatus.PendingPaying) {
         purchase.purchaseStatus = PurchaseStatus.Done;
       }
-      purchase.timestampFirstSuccessfulPay = now;
+      purchase.timestampFirstSuccessfulPay = timestampPreciseToDb(now);
       purchase.lastSessionId = sessionId;
       purchase.merchantPaySig = payResponse.sig;
       purchase.posConfirmation = payResponse.pos_confirmation;
@@ -737,8 +742,10 @@ async function storeFirstPaySuccess(
         const ar = Duration.fromTalerProtocolDuration(protoAr);
         logger.info("auto_refund present");
         purchase.purchaseStatus = PurchaseStatus.PendingQueryingAutoRefund;
-        purchase.autoRefundDeadline = AbsoluteTime.toProtocolTimestamp(
-          AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
+        purchase.autoRefundDeadline = timestampProtocolToDb(
+          AbsoluteTime.toProtocolTimestamp(
+            AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
+          ),
         );
       }
       await tx.purchases.put(purchase);
@@ -941,7 +948,9 @@ async function unblockBackup(
         .forEachAsync(async (bp) => {
           bp.state = {
             tag: BackupProviderStateTag.Ready,
-            nextBackupTimestamp: TalerPreciseTimestamp.now(),
+            nextBackupTimestamp: timestampPreciseToDb(
+              TalerPreciseTimestamp.now(),
+            ),
           };
           tx.backupProviders.put(bp);
         });
@@ -1447,7 +1456,7 @@ export async function confirmPay(
             totalPayCost: Amounts.stringify(payCostInfo),
           };
           p.lastSessionId = sessionId;
-          p.timestampAccept = TalerPreciseTimestamp.now();
+          p.timestampAccept = 
timestampPreciseToDb(TalerPreciseTimestamp.now());
           p.purchaseStatus = PurchaseStatus.PendingPaying;
           await tx.purchases.put(p);
           await spendCoins(ws, tx, {
@@ -2340,7 +2349,9 @@ async function processPurchaseAutoRefund(
     if (
       !purchase.autoRefundDeadline ||
       AbsoluteTime.isExpired(
-        AbsoluteTime.fromProtocolTimestamp(purchase.autoRefundDeadline),
+        AbsoluteTime.fromProtocolTimestamp(
+          timestampProtocolFromDb(purchase.autoRefundDeadline),
+        ),
       )
     ) {
       const transitionInfo = await ws.db
@@ -2791,7 +2802,7 @@ async function storeRefunds(
               proposalId: purchase.proposalId,
               refundGroupId: newRefundGroupId,
               status: RefundGroupStatus.Pending,
-              timestampCreated: now,
+              timestampCreated: timestampPreciseToDb(now),
               amountEffective: Amounts.stringify(
                 Amounts.zeroOfCurrency(currency),
               ),
@@ -2801,8 +2812,8 @@ async function storeRefunds(
           const status: RefundItemStatus = getItemStatus(rf);
           const newItem: RefundItemRecord = {
             coinPub: rf.coin_pub,
-            executionTime: rf.execution_time,
-            obtainedTime: now,
+            executionTime: timestampProtocolToDb(rf.execution_time),
+            obtainedTime: timestampPreciseToDb(now),
             refundAmount: rf.refund_amount,
             refundGroupId: newGroup.refundGroupId,
             rtxid: rf.rtransaction_id,
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
index 0355eb152..6ec8822ab 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
@@ -60,6 +60,9 @@ import {
   PeerPullPaymentCreditStatus,
   WithdrawalGroupStatus,
   WithdrawalRecordType,
+  timestampOptionalPreciseFromDb,
+  timestampPreciseFromDb,
+  timestampPreciseToDb,
   updateExchangeFromUrl,
 } from "../index.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
@@ -395,12 +398,14 @@ async function handlePeerPullCreditCreatePurse(
     nonce: pullIni.contractEncNonce,
   });
 
+  const mergeTimestamp = timestampPreciseFromDb(pullIni.mergeTimestamp);
+
   const purseExpiration = contractTerms.purse_expiration;
   const sigRes = await ws.cryptoApi.signReservePurseCreate({
     contractTermsHash: pullIni.contractTermsHash,
     flags: WalletAccountMergeFlags.CreateWithPurseFee,
     mergePriv: pullIni.mergePriv,
-    mergeTimestamp: TalerPreciseTimestamp.round(pullIni.mergeTimestamp),
+    mergeTimestamp: TalerPreciseTimestamp.round(mergeTimestamp),
     purseAmount: pullIni.amount,
     purseExpiration: purseExpiration,
     purseFee: purseFee,
@@ -412,7 +417,7 @@ async function handlePeerPullCreditCreatePurse(
 
   const reservePurseReqBody: ExchangeReservePurseRequest = {
     merge_sig: sigRes.mergeSig,
-    merge_timestamp: TalerPreciseTimestamp.round(pullIni.mergeTimestamp),
+    merge_timestamp: TalerPreciseTimestamp.round(mergeTimestamp),
     h_contract_terms: pullIni.contractTermsHash,
     merge_pub: pullIni.mergePub,
     min_age: 0,
@@ -695,11 +700,17 @@ async function getPreferredExchangeForCurrency(
         if (candidate.lastWithdrawal && !e.lastWithdrawal) {
           continue;
         }
-        if (candidate.lastWithdrawal && e.lastWithdrawal) {
+        const exchangeLastWithdrawal = timestampOptionalPreciseFromDb(
+          e.lastWithdrawal,
+        );
+        const candidateLastWithdrawal = timestampOptionalPreciseFromDb(
+          candidate.lastWithdrawal,
+        );
+        if (exchangeLastWithdrawal && candidateLastWithdrawal) {
           if (
             AbsoluteTime.cmp(
-              AbsoluteTime.fromPreciseTimestamp(e.lastWithdrawal),
-              AbsoluteTime.fromPreciseTimestamp(candidate.lastWithdrawal),
+              AbsoluteTime.fromPreciseTimestamp(exchangeLastWithdrawal),
+              AbsoluteTime.fromPreciseTimestamp(candidateLastWithdrawal),
             ) > 0
           ) {
             candidate = e;
@@ -741,8 +752,6 @@ export async function initiatePeerPullPayment(
     exchangeBaseUrl: exchangeBaseUrl,
   });
 
-  const mergeTimestamp = TalerPreciseTimestamp.now();
-
   const pursePair = await ws.cryptoApi.createEddsaKeypair({});
   const mergePair = await ws.cryptoApi.createEddsaKeypair({});
 
@@ -766,6 +775,8 @@ export async function initiatePeerPullPayment(
     undefined,
   );
 
+  const mergeTimestamp = TalerPreciseTimestamp.now();
+
   const transitionInfo = await ws.db
     .mktx((x) => [x.peerPullCredit, x.contractTerms])
     .runReadWrite(async (tx) => {
@@ -778,7 +789,7 @@ export async function initiatePeerPullPayment(
         mergePriv: mergePair.priv,
         mergePub: mergePair.pub,
         status: PeerPullPaymentCreditStatus.PendingCreatePurse,
-        mergeTimestamp,
+        mergeTimestamp: timestampPreciseToDb(mergeTimestamp),
         contractEncNonce,
         mergeReserveRowId: mergeReserveRowId,
         contractPriv: contractKeyPair.priv,
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
index 5bcfa3418..48cbf574f 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
@@ -59,6 +59,7 @@ import {
   PendingTaskType,
   RefreshOperationStatus,
   createRefreshGroup,
+  timestampPreciseToDb,
 } from "../index.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
 import { checkLogicInvariant } from "../util/invariants.js";
@@ -595,7 +596,7 @@ export async function preparePeerPullDebit(
           contractPriv: contractPriv,
           exchangeBaseUrl: exchangeBaseUrl,
           pursePub: pursePub,
-          timestampCreated: TalerPreciseTimestamp.now(),
+          timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()),
           contractTermsHash,
           amount: contractTerms.amount,
           status: PeerPullDebitRecordStatus.DialogProposed,
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
index 89d9e3b49..e4698c203 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
@@ -59,6 +59,7 @@ import {
   PendingTaskType,
   WithdrawalGroupStatus,
   WithdrawalRecordType,
+  timestampPreciseToDb,
 } from "../index.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
 import { checkDbInvariant } from "../util/invariants.js";
@@ -129,12 +130,10 @@ export async function preparePeerPushCredit(
       amountEffective: existing.existingPushInc.estimatedAmountEffective,
       amountRaw: existing.existingContractTerms.amount,
       contractTerms: existing.existingContractTerms,
-      peerPushCreditId:
-        existing.existingPushInc.peerPushCreditId,
+      peerPushCreditId: existing.existingPushInc.peerPushCreditId,
       transactionId: constructTransactionIdentifier({
         tag: TransactionType.PeerPushCredit,
-        peerPushCreditId:
-          existing.existingPushInc.peerPushCreditId,
+        peerPushCreditId: existing.existingPushInc.peerPushCreditId,
       }),
     };
   }
@@ -196,7 +195,7 @@ export async function preparePeerPushCredit(
         exchangeBaseUrl: exchangeBaseUrl,
         mergePriv: dec.mergePriv,
         pursePub: pursePub,
-        timestamp: TalerPreciseTimestamp.now(),
+        timestamp: timestampPreciseToDb(TalerPreciseTimestamp.now()),
         contractTermsHash,
         status: PeerPushCreditStatus.DialogProposed,
         withdrawalGroupId,
@@ -263,16 +262,11 @@ async function longpollKycStatus(
       const transitionInfo = await ws.db
         .mktx((x) => [x.peerPushCredit])
         .runReadWrite(async (tx) => {
-          const peerInc = await tx.peerPushCredit.get(
-            peerPushCreditId,
-          );
+          const peerInc = await tx.peerPushCredit.get(peerPushCreditId);
           if (!peerInc) {
             return;
           }
-          if (
-            peerInc.status !==
-            PeerPushCreditStatus.PendingMergeKycRequired
-          ) {
+          if (peerInc.status !== PeerPushCreditStatus.PendingMergeKycRequired) 
{
             return;
           }
           const oldTxState = computePeerPushCreditTransactionState(peerInc);
@@ -333,9 +327,7 @@ async function processPeerPushCreditKycRequired(
     const { transitionInfo, result } = await ws.db
       .mktx((x) => [x.peerPushCredit])
       .runReadWrite(async (tx) => {
-        const peerInc = await tx.peerPushCredit.get(
-          peerPushCreditId,
-        );
+        const peerInc = await tx.peerPushCredit.get(peerPushCreditId);
         if (!peerInc) {
           return {
             transitionInfo: undefined,
@@ -466,9 +458,7 @@ async function handlePendingMerge(
       x.exchangeDetails,
     ])
     .runReadWrite(async (tx) => {
-      const peerInc = await tx.peerPushCredit.get(
-        peerPushCreditId,
-      );
+      const peerInc = await tx.peerPushCredit.get(peerPushCreditId);
       if (!peerInc) {
         return undefined;
       }
@@ -520,9 +510,7 @@ async function handlePendingWithdrawing(
   const transitionInfo = await ws.db
     .mktx((x) => [x.peerPushCredit, x.withdrawalGroups])
     .runReadWrite(async (tx) => {
-      const ppi = await tx.peerPushCredit.get(
-        peerInc.peerPushCreditId,
-      );
+      const ppi = await tx.peerPushCredit.get(peerInc.peerPushCreditId);
       if (!ppi) {
         finished = true;
         return;
@@ -631,9 +619,7 @@ export async function confirmPeerPushCredit(
     }
     peerPushCreditId = parsedTx.peerPushCreditId;
   } else {
-    throw Error(
-      "no transaction ID (or deprecated peerPushCreditId) provided",
-    );
+    throw Error("no transaction ID (or deprecated peerPushCreditId) provided");
   }
 
   await ws.db
@@ -683,9 +669,7 @@ export async function suspendPeerPushCreditTransaction(
   const transitionInfo = await ws.db
     .mktx((x) => [x.peerPushCredit])
     .runReadWrite(async (tx) => {
-      const pushCreditRec = await tx.peerPushCredit.get(
-        peerPushCreditId,
-      );
+      const pushCreditRec = await tx.peerPushCredit.get(peerPushCreditId);
       if (!pushCreditRec) {
         logger.warn(`peer push credit ${peerPushCreditId} not found`);
         return;
@@ -746,9 +730,7 @@ export async function abortPeerPushCreditTransaction(
   const transitionInfo = await ws.db
     .mktx((x) => [x.peerPushCredit])
     .runReadWrite(async (tx) => {
-      const pushCreditRec = await tx.peerPushCredit.get(
-        peerPushCreditId,
-      );
+      const pushCreditRec = await tx.peerPushCredit.get(peerPushCreditId);
       if (!pushCreditRec) {
         logger.warn(`peer push credit ${peerPushCreditId} not found`);
         return;
@@ -820,9 +802,7 @@ export async function resumePeerPushCreditTransaction(
   const transitionInfo = await ws.db
     .mktx((x) => [x.peerPushCredit])
     .runReadWrite(async (tx) => {
-      const pushCreditRec = await tx.peerPushCredit.get(
-        peerPushCreditId,
-      );
+      const pushCreditRec = await tx.peerPushCredit.get(peerPushCreditId);
       if (!pushCreditRec) {
         logger.warn(`peer push credit ${peerPushCreditId} not found`);
         return;
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
index e80ffc059..a7b9f79eb 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
@@ -55,10 +55,14 @@ import {
   PeerPushDebitStatus,
   RefreshOperationStatus,
   createRefreshGroup,
+  timestampPreciseToDb,
+  timestampProtocolFromDb,
+  timestampProtocolToDb,
 } from "../index.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { PendingTaskType } from "../pending-types.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
+import { PeerCoinRepair, selectPeerCoins } from "../util/coinSelection.js";
 import { checkLogicInvariant } from "../util/invariants.js";
 import {
   TaskRunResult,
@@ -77,7 +81,6 @@ import {
   notifyTransition,
   stopLongpolling,
 } from "./transactions.js";
-import { PeerCoinRepair, selectPeerCoins } from "../util/coinSelection.js";
 
 const logger = new Logger("pay-peer-push-debit.ts");
 
@@ -207,7 +210,7 @@ async function processPeerPushDebitCreateReserve(
     mergePub: peerPushInitiation.mergePub,
     minAge: 0,
     purseAmount: peerPushInitiation.amount,
-    purseExpiration,
+    purseExpiration: timestampProtocolFromDb(purseExpiration),
     pursePriv: peerPushInitiation.pursePriv,
   });
 
@@ -666,10 +669,10 @@ export async function initiatePeerPushDebit(
         exchangeBaseUrl: sel.exchangeBaseUrl,
         mergePriv: mergePair.priv,
         mergePub: mergePair.pub,
-        purseExpiration: purseExpiration,
+        purseExpiration: timestampProtocolToDb(purseExpiration),
         pursePriv: pursePair.priv,
         pursePub: pursePair.pub,
-        timestampCreated: TalerPreciseTimestamp.now(),
+        timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()),
         status: PeerPushDebitStatus.PendingCreatePurse,
         contractEncNonce,
         coinSel: {
diff --git a/packages/taler-wallet-core/src/operations/pending.ts 
b/packages/taler-wallet-core/src/operations/pending.ts
index 6115f848b..1819aa1b8 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -21,42 +21,46 @@
 /**
  * Imports.
  */
+import { GlobalIDB } from "@gnu-taler/idb-bridge";
+import { AbsoluteTime, TransactionRecordFilter } from "@gnu-taler/taler-util";
 import {
-  PurchaseStatus,
-  WalletStoresV1,
   BackupProviderStateTag,
-  RefreshCoinStatus,
-  PeerPushDebitStatus,
-  PeerPullDebitRecordStatus,
-  PeerPushCreditStatus,
-  PeerPullPaymentCreditStatus,
-  WithdrawalGroupStatus,
-  RewardRecordStatus,
-  DepositOperationStatus,
-  RefreshGroupRecord,
-  WithdrawalGroupRecord,
+  DepositElementStatus,
   DepositGroupRecord,
-  RewardRecord,
-  PurchaseRecord,
+  DepositOperationStatus,
+  ExchangeEntryDbUpdateStatus,
   PeerPullCreditRecord,
+  PeerPullDebitRecordStatus,
+  PeerPullPaymentCreditStatus,
   PeerPullPaymentIncomingRecord,
+  PeerPushCreditStatus,
   PeerPushDebitRecord,
+  PeerPushDebitStatus,
   PeerPushPaymentIncomingRecord,
+  PurchaseRecord,
+  PurchaseStatus,
+  RefreshCoinStatus,
+  RefreshGroupRecord,
+  RefreshOperationStatus,
   RefundGroupRecord,
   RefundGroupStatus,
-  ExchangeEntryDbUpdateStatus,
-  RefreshOperationStatus,
-  DepositElementStatus,
+  RewardRecord,
+  RewardRecordStatus,
+  WalletStoresV1,
+  WithdrawalGroupRecord,
+  WithdrawalGroupStatus,
+  timestampAbsoluteFromDb,
+  timestampOptionalAbsoluteFromDb,
+  timestampPreciseFromDb,
+  timestampPreciseToDb,
 } from "../db.js";
+import { InternalWalletState } from "../internal-wallet-state.js";
 import {
   PendingOperationsResponse,
   PendingTaskType,
   TaskId,
 } from "../pending-types.js";
-import { AbsoluteTime, TransactionRecordFilter } from "@gnu-taler/taler-util";
-import { InternalWalletState } from "../internal-wallet-state.js";
 import { GetReadOnlyAccess } from "../util/query.js";
-import { GlobalIDB } from "@gnu-taler/idb-bridge";
 import { TaskIdentifiers } from "./common.js";
 
 function getPendingCommon(
@@ -99,12 +103,14 @@ async function gatherExchangePending(
     }
     const opTag = TaskIdentifiers.forExchangeUpdate(exch);
     let opr = await tx.operationRetries.get(opTag);
-    const timestampDue =
-      opr?.retryInfo.nextRetry ??
-      AbsoluteTime.fromStampMs(exch.nextUpdateStampMs);
+    const timestampDue = opr?.retryInfo.nextRetry ?? 
exch.nextRefreshCheckStamp;
     resp.pendingOperations.push({
       type: PendingTaskType.ExchangeUpdate,
-      ...getPendingCommon(ws, opTag, timestampDue),
+      ...getPendingCommon(
+        ws,
+        opTag,
+        
AbsoluteTime.fromPreciseTimestamp(timestampPreciseFromDb(timestampDue)),
+      ),
       givesLifeness: false,
       exchangeBaseUrl: exch.baseUrl,
       lastError: opr?.lastError,
@@ -115,8 +121,16 @@ async function gatherExchangePending(
     if (!opr?.lastError) {
       resp.pendingOperations.push({
         type: PendingTaskType.ExchangeCheckRefresh,
-        ...getPendingCommon(ws, opTag, timestampDue),
-        timestampDue: AbsoluteTime.fromStampMs(exch.nextRefreshCheckStampMs),
+        ...getPendingCommon(
+          ws,
+          opTag,
+          AbsoluteTime.fromPreciseTimestamp(
+            timestampPreciseFromDb(timestampDue),
+          ),
+        ),
+        timestampDue: AbsoluteTime.fromPreciseTimestamp(
+          timestampPreciseFromDb(exch.nextRefreshCheckStamp),
+        ),
         givesLifeness: false,
         exchangeBaseUrl: exch.baseUrl,
       });
@@ -165,7 +179,9 @@ async function gatherRefreshPending(
     }
     const opId = TaskIdentifiers.forRefresh(r);
     const retryRecord = await tx.operationRetries.get(opId);
-    const timestampDue = retryRecord?.retryInfo.nextRetry ?? 
AbsoluteTime.now();
+    const timestampDue =
+      timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+      AbsoluteTime.now();
     resp.pendingOperations.push({
       type: PendingTaskType.Refresh,
       ...getPendingCommon(ws, opId, timestampDue),
@@ -222,8 +238,8 @@ async function gatherWithdrawalPending(
       opr = {
         id: opTag,
         retryInfo: {
-          firstTry: now,
-          nextRetry: now,
+          firstTry: timestampPreciseToDb(AbsoluteTime.toPreciseTimestamp(now)),
+          nextRetry: 
timestampPreciseToDb(AbsoluteTime.toPreciseTimestamp(now)),
           retryCounter: 0,
         },
       };
@@ -233,7 +249,8 @@ async function gatherWithdrawalPending(
       ...getPendingCommon(
         ws,
         opTag,
-        opr.retryInfo?.nextRetry ?? AbsoluteTime.now(),
+        timestampOptionalAbsoluteFromDb(opr.retryInfo?.nextRetry) ??
+          AbsoluteTime.now(),
       ),
       givesLifeness: true,
       withdrawalGroupId: wsr.withdrawalGroupId,
@@ -285,7 +302,9 @@ async function gatherDepositPending(
     }
     const opId = TaskIdentifiers.forDeposit(dg);
     const retryRecord = await tx.operationRetries.get(opId);
-    const timestampDue = retryRecord?.retryInfo.nextRetry ?? 
AbsoluteTime.now();
+    const timestampDue =
+      timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+      AbsoluteTime.now();
     resp.pendingOperations.push({
       type: PendingTaskType.Deposit,
       ...getPendingCommon(ws, opId, timestampDue),
@@ -330,13 +349,15 @@ async function gatherRewardPending(
   await iterRecordsForReward(tx, { onlyState: "nonfinal" }, async (tip) => {
     const opId = TaskIdentifiers.forTipPickup(tip);
     const retryRecord = await tx.operationRetries.get(opId);
-    const timestampDue = retryRecord?.retryInfo.nextRetry ?? 
AbsoluteTime.now();
+    const timestampDue =
+      timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+      AbsoluteTime.now();
     if (tip.acceptedTimestamp) {
       resp.pendingOperations.push({
         type: PendingTaskType.RewardPickup,
         ...getPendingCommon(ws, opId, timestampDue),
         givesLifeness: true,
-        timestampDue: retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(),
+        timestampDue,
         merchantBaseUrl: tip.merchantBaseUrl,
         tipId: tip.walletRewardId,
         merchantTipId: tip.merchantRewardId,
@@ -390,7 +411,9 @@ async function gatherPurchasePending(
   await iterRecordsForPurchase(tx, { onlyState: "nonfinal" }, async (pr) => {
     const opId = TaskIdentifiers.forPay(pr);
     const retryRecord = await tx.operationRetries.get(opId);
-    const timestampDue = retryRecord?.retryInfo.nextRetry ?? 
AbsoluteTime.now();
+    const timestampDue =
+      timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+      AbsoluteTime.now();
     resp.pendingOperations.push({
       type: PendingTaskType.Purchase,
       ...getPendingCommon(ws, opId, timestampDue),
@@ -419,7 +442,9 @@ async function gatherRecoupPending(
     }
     const opId = TaskIdentifiers.forRecoup(rg);
     const retryRecord = await tx.operationRetries.get(opId);
-    const timestampDue = retryRecord?.retryInfo.nextRetry ?? 
AbsoluteTime.now();
+    const timestampDue =
+      timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+      AbsoluteTime.now();
     resp.pendingOperations.push({
       type: PendingTaskType.Recoup,
       ...getPendingCommon(ws, opId, timestampDue),
@@ -444,7 +469,7 @@ async function gatherBackupPending(
     const opId = TaskIdentifiers.forBackup(bp);
     const retryRecord = await tx.operationRetries.get(opId);
     if (bp.state.tag === BackupProviderStateTag.Ready) {
-      const timestampDue = AbsoluteTime.fromPreciseTimestamp(
+      const timestampDue = timestampAbsoluteFromDb(
         bp.state.nextBackupTimestamp,
       );
       resp.pendingOperations.push({
@@ -456,7 +481,8 @@ async function gatherBackupPending(
       });
     } else if (bp.state.tag === BackupProviderStateTag.Retrying) {
       const timestampDue =
-        retryRecord?.retryInfo?.nextRetry ?? AbsoluteTime.now();
+        timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo?.nextRetry) ??
+        AbsoluteTime.now();
       resp.pendingOperations.push({
         type: PendingTaskType.Backup,
         ...getPendingCommon(ws, opId, timestampDue),
@@ -503,7 +529,8 @@ async function gatherPeerPullInitiationPending(
       const opId = TaskIdentifiers.forPeerPullPaymentInitiation(pi);
       const retryRecord = await tx.operationRetries.get(opId);
       const timestampDue =
-        retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
+        timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+        AbsoluteTime.now();
       resp.pendingOperations.push({
         type: PendingTaskType.PeerPullCredit,
         ...getPendingCommon(ws, opId, timestampDue),
@@ -549,7 +576,8 @@ async function gatherPeerPullDebitPending(
       const opId = TaskIdentifiers.forPeerPullPaymentDebit(pi);
       const retryRecord = await tx.operationRetries.get(opId);
       const timestampDue =
-        retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
+        timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+        AbsoluteTime.now();
       resp.pendingOperations.push({
         type: PendingTaskType.PeerPullDebit,
         ...getPendingCommon(ws, opId, timestampDue),
@@ -595,7 +623,8 @@ async function gatherPeerPushInitiationPending(
       const opId = TaskIdentifiers.forPeerPushPaymentInitiation(pi);
       const retryRecord = await tx.operationRetries.get(opId);
       const timestampDue =
-        retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
+        timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+        AbsoluteTime.now();
       resp.pendingOperations.push({
         type: PendingTaskType.PeerPushDebit,
         ...getPendingCommon(ws, opId, timestampDue),
@@ -645,7 +674,8 @@ async function gatherPeerPushCreditPending(
       const opId = TaskIdentifiers.forPeerPushCredit(pi);
       const retryRecord = await tx.operationRetries.get(opId);
       const timestampDue =
-        retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
+        timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
+        AbsoluteTime.now();
       resp.pendingOperations.push({
         type: PendingTaskType.PeerPushCredit,
         ...getPendingCommon(ws, opId, timestampDue),
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts 
b/packages/taler-wallet-core/src/operations/recoup.ts
index 6a18e5de6..782e98d1c 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -47,6 +47,7 @@ import {
   WithdrawCoinSource,
   WithdrawalGroupStatus,
   WithdrawalRecordType,
+  timestampPreciseToDb,
 } from "../db.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { checkDbInvariant } from "../util/invariants.js";
@@ -391,7 +392,7 @@ export async function processRecoupGroup(
       if (!rg2) {
         return;
       }
-      rg2.timestampFinished = TalerPreciseTimestamp.now();
+      rg2.timestampFinished = 
timestampPreciseToDb(TalerPreciseTimestamp.now());
       if (rg2.scheduleRefreshCoins.length > 0) {
         const refreshGroupId = await createRefreshGroup(
           ws,
@@ -424,7 +425,7 @@ export async function createRecoupGroup(
     exchangeBaseUrl: exchangeBaseUrl,
     coinPubs: coinPubs,
     timestampFinished: undefined,
-    timestampStarted: TalerPreciseTimestamp.now(),
+    timestampStarted: timestampPreciseToDb(TalerPreciseTimestamp.now()),
     recoupFinishedPerCoin: coinPubs.map(() => false),
     scheduleRefreshCoins: [],
   };
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index 75adbc860..95aedbbd6 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -80,6 +80,8 @@ import {
   isWithdrawableDenom,
   PendingTaskType,
   RefreshSessionRecord,
+  timestampPreciseToDb,
+  timestampProtocolFromDb,
 } from "../index.js";
 import {
   EXCHANGE_COINS_LOCK,
@@ -157,10 +159,10 @@ function updateGroupStatus(rg: RefreshGroupRecord): { 
final: boolean } {
   );
   if (allFinal) {
     if (anyFailed) {
-      rg.timestampFinished = TalerPreciseTimestamp.now();
+      rg.timestampFinished = timestampPreciseToDb(TalerPreciseTimestamp.now());
       rg.operationStatus = RefreshOperationStatus.Failed;
     } else {
-      rg.timestampFinished = TalerPreciseTimestamp.now();
+      rg.timestampFinished = timestampPreciseToDb(TalerPreciseTimestamp.now());
       rg.operationStatus = RefreshOperationStatus.Finished;
     }
     return { final: true };
@@ -1099,12 +1101,14 @@ export async function createRefreshGroup(
     expectedOutputPerCoin: estimatedOutputPerCoin.map((x) =>
       Amounts.stringify(x),
     ),
-    timestampCreated: TalerPreciseTimestamp.now(),
+    timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()),
   };
 
   if (oldCoinPubs.length == 0) {
     logger.warn("created refresh group with zero coins");
-    refreshGroup.timestampFinished = TalerPreciseTimestamp.now();
+    refreshGroup.timestampFinished = timestampPreciseToDb(
+      TalerPreciseTimestamp.now(),
+    );
     refreshGroup.operationStatus = RefreshOperationStatus.Finished;
   }
 
@@ -1122,10 +1126,10 @@ export async function createRefreshGroup(
  */
 function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
   const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(
-    d.stampExpireWithdraw,
+    timestampProtocolFromDb(d.stampExpireWithdraw),
   );
   const expireDeposit = AbsoluteTime.fromProtocolTimestamp(
-    d.stampExpireDeposit,
+    timestampProtocolFromDb(d.stampExpireDeposit),
   );
   const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
   const deltaDiv = durationMul(delta, 0.75);
@@ -1137,10 +1141,10 @@ function getAutoRefreshCheckThreshold(d: 
DenominationRecord): AbsoluteTime {
  */
 function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime {
   const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(
-    d.stampExpireWithdraw,
+    timestampProtocolFromDb(d.stampExpireWithdraw),
   );
   const expireDeposit = AbsoluteTime.fromProtocolTimestamp(
-    d.stampExpireDeposit,
+    timestampProtocolFromDb(d.stampExpireDeposit),
   );
   const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
   const deltaDiv = durationMul(delta, 0.5);
@@ -1224,8 +1228,9 @@ export async function autoRefresh(
       logger.trace(
         `next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,
       );
-      exchange.nextRefreshCheckStampMs =
-        AbsoluteTime.toStampMs(minCheckThreshold);
+      exchange.nextRefreshCheckStamp = timestampPreciseToDb(
+        AbsoluteTime.toPreciseTimestamp(minCheckThreshold),
+      );
       await tx.exchanges.put(exchange);
     });
   return TaskRunResult.finished();
diff --git a/packages/taler-wallet-core/src/operations/reward.ts 
b/packages/taler-wallet-core/src/operations/reward.ts
index 6ae021174..ddcfb20ac 100644
--- a/packages/taler-wallet-core/src/operations/reward.ts
+++ b/packages/taler-wallet-core/src/operations/reward.ts
@@ -50,6 +50,10 @@ import {
   DenominationRecord,
   RewardRecord,
   RewardRecordStatus,
+  timestampPreciseFromDb,
+  timestampPreciseToDb,
+  timestampProtocolFromDb,
+  timestampProtocolToDb,
 } from "../db.js";
 import { makeErrorDetail } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../internal-wallet-state.js";
@@ -199,11 +203,11 @@ export async function prepareTip(
       acceptedTimestamp: undefined,
       status: RewardRecordStatus.DialogAccept,
       rewardAmountRaw: Amounts.stringify(amount),
-      rewardExpiration: tipPickupStatus.expiration,
+      rewardExpiration: timestampProtocolToDb(tipPickupStatus.expiration),
       exchangeBaseUrl: tipPickupStatus.exchange_url,
       next_url: tipPickupStatus.next_url,
       merchantBaseUrl: res.merchantBaseUrl,
-      createdTimestamp: TalerPreciseTimestamp.now(),
+      createdTimestamp: timestampPreciseToDb(TalerPreciseTimestamp.now()),
       merchantRewardId: res.merchantRewardId,
       rewardAmountEffective: Amounts.stringify(selectedDenoms.totalCoinValue),
       denomsSel: selectedDenoms,
@@ -229,7 +233,7 @@ export async function prepareTip(
     rewardAmountRaw: Amounts.stringify(tipRecord.rewardAmountRaw),
     exchangeBaseUrl: tipRecord.exchangeBaseUrl,
     merchantBaseUrl: tipRecord.merchantBaseUrl,
-    expirationTimestamp: tipRecord.rewardExpiration,
+    expirationTimestamp: timestampProtocolFromDb(tipRecord.rewardExpiration),
     rewardAmountEffective: Amounts.stringify(tipRecord.rewardAmountEffective),
     walletRewardId: tipRecord.walletRewardId,
     transactionId,
@@ -411,7 +415,7 @@ export async function processTip(
         return;
       }
       const oldTxState = computeRewardTransactionStatus(tr);
-      tr.pickedUpTimestamp = TalerPreciseTimestamp.now();
+      tr.pickedUpTimestamp = timestampPreciseToDb(TalerPreciseTimestamp.now());
       tr.status = RewardRecordStatus.Done;
       await tx.rewards.put(tr);
       const newTxState = computeRewardTransactionStatus(tr);
@@ -448,7 +452,9 @@ export async function acceptTip(
         return { tipRecord };
       }
       const oldTxState = computeRewardTransactionStatus(tipRecord);
-      tipRecord.acceptedTimestamp = TalerPreciseTimestamp.now();
+      tipRecord.acceptedTimestamp = timestampPreciseToDb(
+        TalerPreciseTimestamp.now(),
+      );
       tipRecord.status = RewardRecordStatus.PendingPickup;
       await tx.rewards.put(tipRecord);
       const newTxState = computeRewardTransactionStatus(tipRecord);
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index d7b277faf..cf2006406 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -20,6 +20,7 @@
 import {
   AbsoluteTime,
   Amounts,
+  DepositTransactionTrackingState,
   j2s,
   Logger,
   NotificationType,
@@ -65,7 +66,13 @@ import {
   WithdrawalGroupStatus,
   WithdrawalRecordType,
 } from "../db.js";
-import { GetReadOnlyAccess, WalletStoresV1 } from "../index.js";
+import {
+  GetReadOnlyAccess,
+  timestampOptionalPreciseFromDb,
+  timestampPreciseFromDb,
+  timestampProtocolFromDb,
+  WalletStoresV1,
+} from "../index.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { PendingTaskType } from "../pending-types.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
@@ -470,7 +477,7 @@ function buildTransactionForPushPaymentDebit(
       expiration: contractTerms.purse_expiration,
       summary: contractTerms.summary,
     },
-    timestamp: pi.timestampCreated,
+    timestamp: timestampPreciseFromDb(pi.timestampCreated),
     talerUri: stringifyPayPushUri({
       exchangeBaseUrl: pi.exchangeBaseUrl,
       contractPriv: pi.contractPriv,
@@ -501,7 +508,7 @@ function buildTransactionForPullPaymentDebit(
       expiration: contractTerms.purse_expiration,
       summary: contractTerms.summary,
     },
-    timestamp: pi.timestampCreated,
+    timestamp: timestampPreciseFromDb(pi.timestampCreated),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.PeerPullDebit,
       peerPullDebitId: pi.peerPullDebitId,
@@ -543,8 +550,7 @@ function buildTransactionForPeerPullCredit(
       amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
       amountRaw: Amounts.stringify(wsr.instructedAmount),
       exchangeBaseUrl: wsr.exchangeBaseUrl,
-      // Old transactions don't have it!
-      timestamp: pullCredit.mergeTimestamp ?? TalerPreciseTimestamp.now(),
+      timestamp: timestampPreciseFromDb(pullCredit.mergeTimestamp),
       info: {
         expiration: peerContractTerms.purse_expiration,
         summary: peerContractTerms.summary,
@@ -575,8 +581,7 @@ function buildTransactionForPeerPullCredit(
     amountEffective: Amounts.stringify(pullCredit.estimatedAmountEffective),
     amountRaw: Amounts.stringify(peerContractTerms.amount),
     exchangeBaseUrl: pullCredit.exchangeBaseUrl,
-    // Old transactions don't have it!
-    timestamp: pullCredit.mergeTimestamp ?? TalerProtocolTimestamp.now(),
+    timestamp: timestampPreciseFromDb(pullCredit.mergeTimestamp),
     info: {
       expiration: peerContractTerms.purse_expiration,
       summary: peerContractTerms.summary,
@@ -617,7 +622,7 @@ function buildTransactionForPeerPushCredit(
         expiration: peerContractTerms.purse_expiration,
         summary: peerContractTerms.summary,
       },
-      timestamp: wsr.timestampStart,
+      timestamp: timestampPreciseFromDb(wsr.timestampStart),
       transactionId: constructTransactionIdentifier({
         tag: TransactionType.PeerPushCredit,
         peerPushCreditId: pushInc.peerPushCreditId,
@@ -640,7 +645,7 @@ function buildTransactionForPeerPushCredit(
       summary: peerContractTerms.summary,
     },
     kycUrl: pushInc.kycUrl,
-    timestamp: pushInc.timestamp,
+    timestamp: timestampPreciseFromDb(pushInc.timestamp),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.PeerPushCredit,
       peerPushCreditId: pushInc.peerPushCreditId,
@@ -673,7 +678,7 @@ function buildTransactionForBankIntegratedWithdraw(
     },
     kycUrl: wgRecord.kycUrl,
     exchangeBaseUrl: wgRecord.exchangeBaseUrl,
-    timestamp: wgRecord.timestampStart,
+    timestamp: timestampPreciseFromDb(wgRecord.timestampStart),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.Withdrawal,
       withdrawalGroupId: wgRecord.withdrawalGroupId,
@@ -717,7 +722,7 @@ function buildTransactionForManualWithdraw(
     },
     kycUrl: withdrawalGroup.kycUrl,
     exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl,
-    timestamp: withdrawalGroup.timestampStart,
+    timestamp: timestampPreciseFromDb(withdrawalGroup.timestampStart),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.Withdrawal,
       withdrawalGroupId: withdrawalGroup.withdrawalGroupId,
@@ -748,7 +753,7 @@ function buildTransactionForRefund(
       tag: TransactionType.Payment,
       proposalId: refundRecord.proposalId,
     }),
-    timestamp: refundRecord.timestampCreated,
+    timestamp: timestampPreciseFromDb(refundRecord.timestampCreated),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.Refund,
       refundGroupId: refundRecord.refundGroupId,
@@ -786,7 +791,7 @@ function buildTransactionForRefresh(
     refreshOutputAmount: Amounts.stringify(outputAmount),
     originatingTransactionId:
       refreshGroupRecord.reasonDetails?.originatingTransactionId,
-    timestamp: refreshGroupRecord.timestampCreated,
+    timestamp: timestampPreciseFromDb(refreshGroupRecord.timestampCreated),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.Refresh,
       refreshGroupId: refreshGroupRecord.refreshGroupId,
@@ -806,15 +811,26 @@ function buildTransactionForDeposit(
     }
   }
 
+  const trackingState: DepositTransactionTrackingState[] = [];
+
+  for (const ts of Object.values(dg.trackingState ?? {})) {
+    trackingState.push({
+      amountRaw: ts.amountRaw,
+      timestampExecuted: timestampProtocolFromDb(ts.timestampExecuted),
+      wireFee: ts.wireFee,
+      wireTransferId: ts.wireTransferId,
+    });
+  }
+
   return {
     type: TransactionType.Deposit,
     txState: computeDepositTransactionStatus(dg),
     txActions: computeDepositTransactionActions(dg),
     amountRaw: Amounts.stringify(dg.counterpartyEffectiveDepositAmount),
     amountEffective: Amounts.stringify(dg.totalPayCost),
-    timestamp: dg.timestampCreated,
+    timestamp: timestampPreciseFromDb(dg.timestampCreated),
     targetPaytoUri: dg.wire.payto_uri,
-    wireTransferDeadline: dg.wireTransferDeadline,
+    wireTransferDeadline: timestampProtocolFromDb(dg.wireTransferDeadline),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.Deposit,
       depositGroupId: dg.depositGroupId,
@@ -827,7 +843,7 @@ function buildTransactionForDeposit(
         )) /
       dg.statusPerCoin.length,
     depositGroupId: dg.depositGroupId,
-    trackingState: Object.values(dg.trackingState ?? {}),
+    trackingState,
     deposited,
     ...(ort?.lastError ? { error: ort.lastError } : {}),
   };
@@ -845,7 +861,7 @@ function buildTransactionForTip(
     txActions: computeTipTransactionActions(tipRecord),
     amountEffective: Amounts.stringify(tipRecord.rewardAmountEffective),
     amountRaw: Amounts.stringify(tipRecord.rewardAmountRaw),
-    timestamp: tipRecord.acceptedTimestamp,
+    timestamp: timestampPreciseFromDb(tipRecord.acceptedTimestamp),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.Reward,
       walletRewardId: tipRecord.walletRewardId,
@@ -922,7 +938,7 @@ async function buildTransactionForPurchase(
         : Amounts.stringify(purchaseRecord.refundAmountAwaiting),
     refunds,
     posConfirmation: purchaseRecord.posConfirmation,
-    timestamp,
+    timestamp: timestampPreciseFromDb(timestamp),
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.Payment,
       proposalId: purchaseRecord.proposalId,
diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts 
b/packages/taler-wallet-core/src/operations/withdraw.test.ts
index 2d9286610..cb8aa5e81 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.test.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts
@@ -16,7 +16,11 @@
 
 import { Amounts, DenomKeyType } from "@gnu-taler/taler-util";
 import test from "ava";
-import { DenominationRecord, DenominationVerificationStatus } from "../db.js";
+import {
+  DenominationRecord,
+  DenominationVerificationStatus,
+  timestampProtocolToDb,
+} from "../db.js";
 import { selectWithdrawalDenominations } from "../util/coinSelection.js";
 
 test("withdrawal selection bug repro", (t) => {
@@ -64,22 +68,22 @@ test("withdrawal selection bug repro", (t) => {
       isRevoked: false,
       masterSig:
         
"4F0P456CNNTTWK8BFJHGM3JTD6FVVNZY8EP077GYAHDJ5Y81S5RQ3SMS925NXMDVG9A88JAAP0E2GDZBC21PP5NHFFVWHAW3AVT8J3R",
-      stampExpireDeposit: {
+      stampExpireDeposit: timestampProtocolToDb({
         t_s: 1742909388,
-      },
-      stampExpireLegal: {
+      }),
+      stampExpireLegal: timestampProtocolToDb({
         t_s: 1900589388,
-      },
-      stampExpireWithdraw: {
+      }),
+      stampExpireWithdraw: timestampProtocolToDb({
         t_s: 1679837388,
-      },
-      stampStart: {
+      }),
+      stampStart: timestampProtocolToDb({
         t_s: 1585229388,
-      },
+      }),
       verificationStatus: DenominationVerificationStatus.Unverified,
       currency: "KUDOS",
       value: "KUDOS:1000",
-      listIssueDate: { t_s: 0 },
+      listIssueDate: timestampProtocolToDb({ t_s: 0 }),
     },
     {
       denomPub: {
@@ -119,22 +123,22 @@ test("withdrawal selection bug repro", (t) => {
       isRevoked: false,
       masterSig:
         
"P99AW82W46MZ0AKW7Z58VQPXFNTJQM9DVTYPBDF6KVYF38PPVDAZTV7JQ8TY7HGEC7JJJAY4E7AY7J3W1WV10DAZZQHHKTAVTSRAC20",
-      stampExpireDeposit: {
+      stampExpireDeposit: timestampProtocolToDb({
         t_s: 1742909388,
-      },
-      stampExpireLegal: {
+      }),
+      stampExpireLegal: timestampProtocolToDb({
         t_s: 1900589388,
-      },
-      stampExpireWithdraw: {
+      }),
+      stampExpireWithdraw: timestampProtocolToDb({
         t_s: 1679837388,
-      },
-      stampStart: {
+      }),
+      stampStart: timestampProtocolToDb({
         t_s: 1585229388,
-      },
+      }),
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: "KUDOS:10",
       currency: "KUDOS",
-      listIssueDate: { t_s: 0 },
+      listIssueDate: timestampProtocolToDb({ t_s: 0 }),
     },
     {
       denomPub: {
@@ -173,22 +177,22 @@ test("withdrawal selection bug repro", (t) => {
       isRevoked: false,
       masterSig:
         
"8S4VZGHE5WE0N5ZVCHYW9KZZR4YAKK15S46MV1HR1QB9AAMH3NWPW4DCR4NYGJK33Q8YNFY80SWNS6XKAP5DEVK933TM894FJ2VGE3G",
-      stampExpireDeposit: {
+      stampExpireDeposit: timestampProtocolToDb({
         t_s: 1742909388,
-      },
-      stampExpireLegal: {
+      }),
+      stampExpireLegal: timestampProtocolToDb({
         t_s: 1900589388,
-      },
-      stampExpireWithdraw: {
+      }),
+      stampExpireWithdraw: timestampProtocolToDb({
         t_s: 1679837388,
-      },
-      stampStart: {
+      }),
+      stampStart: timestampProtocolToDb({
         t_s: 1585229388,
-      },
+      }),
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: "KUDOS:5",
       currency: "KUDOS",
-      listIssueDate: { t_s: 0 },
+      listIssueDate: timestampProtocolToDb({ t_s: 0 }),
     },
     {
       denomPub: {
@@ -228,22 +232,22 @@ test("withdrawal selection bug repro", (t) => {
       isRevoked: false,
       masterSig:
         
"E3AWGAG8VB42P3KXM8B04Z6M483SX59R3Y4T53C3NXCA2NPB6C7HVCMVX05DC6S58E9X40NGEBQNYXKYMYCF3ASY2C4WP1WCZ4ME610",
-      stampExpireDeposit: {
+      stampExpireDeposit: timestampProtocolToDb({
         t_s: 1742909388,
-      },
-      stampExpireLegal: {
+      }),
+      stampExpireLegal: timestampProtocolToDb({
         t_s: 1900589388,
-      },
-      stampExpireWithdraw: {
+      }),
+      stampExpireWithdraw: timestampProtocolToDb({
         t_s: 1679837388,
-      },
-      stampStart: {
+      }),
+      stampStart: timestampProtocolToDb({
         t_s: 1585229388,
-      },
+      }),
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: "KUDOS:1",
       currency: "KUDOS",
-      listIssueDate: { t_s: 0 },
+      listIssueDate: timestampProtocolToDb({ t_s: 0 }),
     },
     {
       denomPub: {
@@ -282,18 +286,18 @@ test("withdrawal selection bug repro", (t) => {
       isRevoked: false,
       masterSig:
         
"0ES1RKV002XB4YP21SN0QB7RSDHGYT0XAE65JYN8AVJAA6H7JZFN7JADXT521DJS89XMGPZGR8GCXF1516Y0Q9QDV00E6NMFA6CF838",
-      stampExpireDeposit: {
+      stampExpireDeposit: timestampProtocolToDb({
         t_s: 1742909388,
-      },
-      stampExpireLegal: {
+      }),
+      stampExpireLegal: timestampProtocolToDb({
         t_s: 1900589388,
-      },
-      stampExpireWithdraw: {
+      }),
+      stampExpireWithdraw: timestampProtocolToDb({
         t_s: 1679837388,
-      },
-      stampStart: {
+      }),
+      stampStart: timestampProtocolToDb({
         t_s: 1585229388,
-      },
+      }),
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: Amounts.stringify({
         currency: "KUDOS",
@@ -301,7 +305,7 @@ test("withdrawal selection bug repro", (t) => {
         value: 0,
       }),
       currency: "KUDOS",
-      listIssueDate: { t_s: 0 },
+      listIssueDate: timestampProtocolToDb({ t_s: 0 }),
     },
     {
       denomPub: {
@@ -340,22 +344,22 @@ test("withdrawal selection bug repro", (t) => {
       isRevoked: false,
       masterSig:
         
"58QEB6C6N7602E572E3JYANVVJ9BRW0V9E2ZFDW940N47YVQDK9SAFPWBN5YGT3G1742AFKQ0CYR4DM2VWV0Z0T1XMEKWN6X2EZ9M0R",
-      stampExpireDeposit: {
+      stampExpireDeposit: timestampProtocolToDb({
         t_s: 1742909388,
-      },
-      stampExpireLegal: {
+      }),
+      stampExpireLegal: timestampProtocolToDb({
         t_s: 1900589388,
-      },
-      stampExpireWithdraw: {
+      }),
+      stampExpireWithdraw: timestampProtocolToDb({
         t_s: 1679837388,
-      },
-      stampStart: {
+      }),
+      stampStart: timestampProtocolToDb({
         t_s: 1585229388,
-      },
+      }),
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: "KUDOS:2",
       currency: "KUDOS",
-      listIssueDate: { t_s: 0 },
+      listIssueDate: timestampProtocolToDb({ t_s: 0 }),
     },
   ];
 
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index 32e63f4f6..fb503d75f 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -131,6 +131,7 @@ import {
   ExchangeEntryDbUpdateStatus,
   PendingTaskType,
   isWithdrawableDenom,
+  timestampPreciseToDb,
 } from "../index.js";
 import {
   TransitionInfo,
@@ -1325,7 +1326,7 @@ async function processWithdrawalGroupAbortingBank(
       }
       const txStatusOld = computeWithdrawalTransactionStatus(wg);
       wg.status = WithdrawalGroupStatus.AbortedBank;
-      wg.timestampFinish = TalerPreciseTimestamp.now();
+      wg.timestampFinish = timestampPreciseToDb(TalerPreciseTimestamp.now());
       const txStatusNew = computeWithdrawalTransactionStatus(wg);
       await tx.withdrawalGroups.put(wg);
       return {
@@ -1458,7 +1459,7 @@ async function processWithdrawalGroupPendingReady(
         }
         const txStatusOld = computeWithdrawalTransactionStatus(wg);
         wg.status = WithdrawalGroupStatus.Done;
-        wg.timestampFinish = TalerPreciseTimestamp.now();
+        wg.timestampFinish = timestampPreciseToDb(TalerPreciseTimestamp.now());
         const txStatusNew = computeWithdrawalTransactionStatus(wg);
         await tx.withdrawalGroups.put(wg);
         return {
@@ -1554,7 +1555,7 @@ async function processWithdrawalGroupPendingReady(
       const oldTxState = computeWithdrawalTransactionStatus(wg);
       logger.info(`now withdrawn ${numFinished} of ${numTotalCoins} coins`);
       if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {
-        wg.timestampFinish = TalerPreciseTimestamp.now();
+        wg.timestampFinish = timestampPreciseToDb(TalerPreciseTimestamp.now());
         wg.status = WithdrawalGroupStatus.Done;
         await makeCoinsVisible(ws, tx, transactionId);
       }
@@ -2047,8 +2048,9 @@ async function registerReserveWithBank(
       if (r.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) {
         throw Error("invariant failed");
       }
-      r.wgInfo.bankInfo.timestampReserveInfoPosted =
-        AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
+      r.wgInfo.bankInfo.timestampReserveInfoPosted = timestampPreciseToDb(
+        AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now()),
+      );
       const oldTxState = computeWithdrawalTransactionStatus(r);
       r.status = WithdrawalGroupStatus.PendingWaitConfirmBank;
       const newTxState = computeWithdrawalTransactionStatus(r);
@@ -2130,7 +2132,7 @@ async function processReserveBankStatus(
         }
         const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
         const oldTxState = computeWithdrawalTransactionStatus(r);
-        r.wgInfo.bankInfo.timestampBankConfirmed = now;
+        r.wgInfo.bankInfo.timestampBankConfirmed = timestampPreciseToDb(now);
         r.status = WithdrawalGroupStatus.FailedBankAborted;
         const newTxState = computeWithdrawalTransactionStatus(r);
         await tx.withdrawalGroups.put(r);
@@ -2179,7 +2181,7 @@ async function processReserveBankStatus(
       if (status.transfer_done) {
         logger.info("withdrawal: transfer confirmed by bank.");
         const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
-        r.wgInfo.bankInfo.timestampBankConfirmed = now;
+        r.wgInfo.bankInfo.timestampBankConfirmed = timestampPreciseToDb(now);
         r.status = WithdrawalGroupStatus.PendingQueryingStatus;
       } else {
         logger.info("withdrawal: transfer not yet confirmed by bank");
@@ -2285,7 +2287,7 @@ export async function 
internalPrepareCreateWithdrawalGroup(
     denomsSel: initialDenomSel,
     exchangeBaseUrl: canonExchange,
     instructedAmount: Amounts.stringify(amount),
-    timestampStart: now,
+    timestampStart: timestampPreciseToDb(now),
     rawWithdrawalAmount: initialDenomSel.totalWithdrawCost,
     effectiveWithdrawalAmount: initialDenomSel.totalCoinValue,
     secretSeed,
@@ -2339,8 +2341,7 @@ export async function 
internalPerformCreateWithdrawalGroup(
   if (!prep.creationInfo) {
     return { withdrawalGroup, transitionInfo: undefined };
   }
-  const { amount, canonExchange, exchangeDetails } =
-    prep.creationInfo;
+  const { amount, canonExchange, exchangeDetails } = prep.creationInfo;
 
   await tx.withdrawalGroups.add(withdrawalGroup);
   await tx.reserves.put({
@@ -2350,7 +2351,7 @@ export async function 
internalPerformCreateWithdrawalGroup(
 
   const exchange = await tx.exchanges.get(withdrawalGroup.exchangeBaseUrl);
   if (exchange) {
-    exchange.lastWithdrawal = TalerPreciseTimestamp.now();
+    exchange.lastWithdrawal = 
timestampPreciseToDb(TalerPreciseTimestamp.now());
     exchange.entryStatus = ExchangeEntryDbRecordStatus.Used;
     await tx.exchanges.put(exchange);
   }
@@ -2541,11 +2542,7 @@ export async function createManualWithdrawal(
   });
 
   const exchangePaytoUris = await ws.db
-    .mktx((x) => [
-      x.withdrawalGroups,
-      x.exchanges,
-      x.exchangeDetails,
-    ])
+    .mktx((x) => [x.withdrawalGroups, x.exchanges, x.exchangeDetails])
     .runReadOnly(async (tx) => {
       return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId);
     });
diff --git a/packages/taler-wallet-core/src/pending-types.ts 
b/packages/taler-wallet-core/src/pending-types.ts
index 627888b4d..e7a40e81b 100644
--- a/packages/taler-wallet-core/src/pending-types.ts
+++ b/packages/taler-wallet-core/src/pending-types.ts
@@ -25,7 +25,7 @@
  * Imports.
  */
 import { TalerErrorDetail, AbsoluteTime } from "@gnu-taler/taler-util";
-import { RetryInfo } from "./operations/common.js";
+import { DbRetryInfo } from "./operations/common.js";
 
 export enum PendingTaskType {
   ExchangeUpdate = "exchange-update",
@@ -137,7 +137,7 @@ export interface PendingRefreshTask {
   lastError?: TalerErrorDetail;
   refreshGroupId: string;
   finishedPerCoin: boolean[];
-  retryInfo?: RetryInfo;
+  retryInfo?: DbRetryInfo;
 }
 
 /**
@@ -156,7 +156,7 @@ export interface PendingTipPickupTask {
 export interface PendingPurchaseTask {
   type: PendingTaskType.Purchase;
   proposalId: string;
-  retryInfo?: RetryInfo;
+  retryInfo?: DbRetryInfo;
   /**
    * Status of the payment as string, used only for debugging.
    */
@@ -167,7 +167,7 @@ export interface PendingPurchaseTask {
 export interface PendingRecoupTask {
   type: PendingTaskType.Recoup;
   recoupGroupId: string;
-  retryInfo?: RetryInfo;
+  retryInfo?: DbRetryInfo;
   lastError: TalerErrorDetail | undefined;
 }
 
@@ -177,7 +177,7 @@ export interface PendingRecoupTask {
 export interface PendingWithdrawTask {
   type: PendingTaskType.Withdraw;
   lastError: TalerErrorDetail | undefined;
-  retryInfo?: RetryInfo;
+  retryInfo?: DbRetryInfo;
   withdrawalGroupId: string;
 }
 
@@ -187,7 +187,7 @@ export interface PendingWithdrawTask {
 export interface PendingDepositTask {
   type: PendingTaskType.Deposit;
   lastError: TalerErrorDetail | undefined;
-  retryInfo: RetryInfo | undefined;
+  retryInfo: DbRetryInfo | undefined;
   depositGroupId: string;
 }
 
@@ -233,7 +233,7 @@ export interface PendingTaskInfoCommon {
    * Retry info.  Currently used to stop the wallet after any operation
    * exceeds a number of retries.
    */
-  retryInfo?: RetryInfo;
+  retryInfo?: DbRetryInfo;
 }
 
 /**
diff --git a/packages/taler-wallet-core/src/util/denominations.ts 
b/packages/taler-wallet-core/src/util/denominations.ts
index 76716cf7a..db6e69956 100644
--- a/packages/taler-wallet-core/src/util/denominations.ts
+++ b/packages/taler-wallet-core/src/util/denominations.ts
@@ -26,10 +26,9 @@ import {
   FeeDescriptionPair,
   TalerProtocolTimestamp,
   TimePoint,
-  WireFee,
 } from "@gnu-taler/taler-util";
 import { DenominationRecord } from "../db.js";
-import { WalletConfig } from "../index.js";
+import { timestampProtocolFromDb } from "../index.js";
 
 /**
  * Given a list of denominations with the same value and same period of time:
@@ -457,9 +456,11 @@ export function isWithdrawableDenom(
   denomselAllowLate?: boolean,
 ): boolean {
   const now = AbsoluteTime.now();
-  const start = AbsoluteTime.fromProtocolTimestamp(d.stampStart);
+  const start = AbsoluteTime.fromProtocolTimestamp(
+    timestampProtocolFromDb(d.stampStart),
+  );
   const withdrawExpire = AbsoluteTime.fromProtocolTimestamp(
-    d.stampExpireWithdraw,
+    timestampProtocolFromDb(d.stampExpireWithdraw),
   );
   const started = AbsoluteTime.cmp(now, start) >= 0;
   let lastPossibleWithdraw: AbsoluteTime;
diff --git a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts 
b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
index 54c08eee4..a0394a687 100644
--- a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
+++ b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
@@ -14,6 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { GlobalIDB } from "@gnu-taler/idb-bridge";
 import {
   AbsoluteTime,
   AgeRestriction,
@@ -29,14 +30,14 @@ import {
   parsePaytoUri,
   strcmp,
 } from "@gnu-taler/taler-util";
-import { checkDbInvariant } from "./invariants.js";
 import {
   DenominationRecord,
   InternalWalletState,
   getExchangeDetails,
+  timestampProtocolFromDb,
 } from "../index.js";
 import { CoinInfo } from "./coinSelection.js";
-import { GlobalIDB } from "@gnu-taler/idb-bridge";
+import { checkDbInvariant } from "./invariants.js";
 
 /**
  * If the operation going to be plan subtracts
@@ -224,10 +225,10 @@ async function getAvailableDenoms(
           );
           for (const denom of ds) {
             const expiresWithdraw = AbsoluteTime.fromProtocolTimestamp(
-              denom.stampExpireWithdraw,
+              timestampProtocolFromDb(denom.stampExpireWithdraw),
             );
             const expiresDeposit = AbsoluteTime.fromProtocolTimestamp(
-              denom.stampExpireDeposit,
+              timestampProtocolFromDb(denom.stampExpireDeposit),
             );
             creditDeadline = AbsoluteTime.min(deadline, expiresWithdraw);
             debitDeadline = AbsoluteTime.min(deadline, expiresDeposit);
@@ -270,10 +271,10 @@ async function getAvailableDenoms(
               continue;
             }
             const expiresWithdraw = AbsoluteTime.fromProtocolTimestamp(
-              denom.stampExpireWithdraw,
+              timestampProtocolFromDb(denom.stampExpireWithdraw),
             );
             const expiresDeposit = AbsoluteTime.fromProtocolTimestamp(
-              denom.stampExpireDeposit,
+              timestampProtocolFromDb(denom.stampExpireDeposit),
             );
             creditDeadline = AbsoluteTime.min(deadline, expiresWithdraw);
             debitDeadline = AbsoluteTime.min(deadline, expiresDeposit);
@@ -318,7 +319,9 @@ function buildCoinInfoFromDenom(
     exchangeBaseUrl: denom.exchangeBaseUrl,
     duration: AbsoluteTime.difference(
       AbsoluteTime.now(),
-      AbsoluteTime.fromProtocolTimestamp(denom.stampExpireDeposit),
+      AbsoluteTime.fromProtocolTimestamp(
+        timestampProtocolFromDb(denom.stampExpireDeposit),
+      ),
     ),
     totalAvailable: total,
     value: Amounts.parseOrThrow(denom.value),

-- 
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]