gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 01/02: wallet: t_s/d_us migration


From: gnunet
Subject: [taler-wallet-core] 01/02: wallet: t_s/d_us migration
Date: Tue, 22 Mar 2022 21:16:47 +0100

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

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

commit f8d12f7b0d4af1b1769b89e80c87f9c169678564
Author: Florian Dold <florian@dold.me>
AuthorDate: Fri Mar 18 15:32:41 2022 +0100

    wallet: t_s/d_us migration
---
 contrib/sample-data/history1.json                  | 402 ---------------------
 packages/anastasis-core/src/index.ts               |  28 +-
 packages/anastasis-core/src/reducer-types.ts       |  11 +-
 .../pages/home/BackupFinishedScreen.stories.tsx    |   4 +-
 .../src/pages/home/BackupFinishedScreen.tsx        |   4 +-
 .../src/pages/home/SecretEditorScreen.tsx          |   2 +-
 packages/taler-util/src/RequestThrottler.ts        |  10 +-
 packages/taler-util/src/ReserveTransaction.ts      |  96 ++---
 packages/taler-util/src/backupTypes.ts             |  74 ++--
 packages/taler-util/src/talerTypes.ts              |  51 ++-
 packages/taler-util/src/time.ts                    | 356 ++++++++++--------
 packages/taler-util/src/transactionsTypes.ts       |   4 +-
 packages/taler-util/src/types-test.ts              |  16 +-
 packages/taler-util/src/walletTypes.ts             |  14 +-
 packages/taler-wallet-cli/src/harness/harness.ts   |  18 +-
 .../src/harness/merchantApiTypes.ts                |  12 +-
 .../src/integrationtests/test-bank-api.ts          |   4 +-
 .../integrationtests/test-exchange-timetravel.ts   |  24 +-
 .../src/integrationtests/test-libeufin-basic.ts    |   5 +-
 .../test-merchant-instances-delete.ts              |   2 +-
 .../src/integrationtests/test-refund-auto.ts       |  15 +-
 .../src/integrationtests/test-refund-gone.ts       |  10 +-
 packages/taler-wallet-core/src/bank-api-client.ts  |  12 +-
 packages/taler-wallet-core/src/common.ts           |  11 +-
 .../src/crypto/workers/cryptoApi.ts                |   2 +-
 .../src/crypto/workers/cryptoImplementation.ts     |  16 +-
 packages/taler-wallet-core/src/db.ts               | 100 +++--
 packages/taler-wallet-core/src/dbless.ts           |   9 +-
 .../src/operations/backup/export.ts                |  11 +-
 .../src/operations/backup/import.ts                |  10 +-
 .../src/operations/backup/index.ts                 |  40 +-
 .../taler-wallet-core/src/operations/deposits.ts   |  59 ++-
 .../taler-wallet-core/src/operations/exchanges.ts  |  66 ++--
 packages/taler-wallet-core/src/operations/pay.ts   |  33 +-
 .../taler-wallet-core/src/operations/pending.ts    |  42 ++-
 .../taler-wallet-core/src/operations/recoup.ts     |   6 +-
 .../taler-wallet-core/src/operations/refresh.ts    |  74 ++--
 .../taler-wallet-core/src/operations/refund.ts     |  41 ++-
 .../taler-wallet-core/src/operations/reserves.ts   |  19 +-
 .../taler-wallet-core/src/operations/testing.ts    |   4 +-
 packages/taler-wallet-core/src/operations/tip.ts   |  11 +-
 .../src/operations/transactions.ts                 |  29 +-
 .../src/operations/withdraw.test.ts                |  60 +--
 .../taler-wallet-core/src/operations/withdraw.ts   |  37 +-
 packages/taler-wallet-core/src/pending-types.ts    |  11 +-
 packages/taler-wallet-core/src/util/http.ts        |  17 +-
 packages/taler-wallet-core/src/util/retries.ts     |  26 +-
 packages/taler-wallet-core/src/wallet.ts           |  19 +-
 .../src/components/PendingTransactions.stories.tsx |  62 +---
 .../src/components/PendingTransactions.tsx         |  13 +-
 .../src/components/Time.tsx                        |   4 +-
 .../src/components/TransactionItem.tsx             |  16 +-
 .../src/cta/Tip.stories.tsx                        |   9 +-
 .../src/popup/DeveloperPage.tsx                    |   2 +-
 .../src/wallet/Backup.stories.tsx                  |  16 +-
 .../src/wallet/BackupPage.tsx                      |  18 +-
 .../src/wallet/History.stories.tsx                 |   7 +-
 .../src/wallet/History.tsx                         |   2 +-
 .../src/wallet/ProviderDetail.stories.tsx          |  26 +-
 .../src/wallet/ProviderDetailPage.tsx              |   5 +-
 .../src/wallet/Transaction.stories.tsx             |  13 +-
 .../src/wallet/Transaction.tsx                     |  20 +-
 62 files changed, 903 insertions(+), 1237 deletions(-)

diff --git a/contrib/sample-data/history1.json 
b/contrib/sample-data/history1.json
deleted file mode 100644
index 8d0076a3..00000000
--- a/contrib/sample-data/history1.json
+++ /dev/null
@@ -1,402 +0,0 @@
-{
-  "history": [
-    {
-      "type": "exchange-added",
-      "builtIn": false,
-      "eventId": "exchange-added;https%3A%2F%2Fexchange.demo.taler.net%2F",
-      "exchangeBaseUrl": "https://exchange.demo.taler.net/";,
-      "timestamp": {
-        "t_ms": 1578334008633
-      }
-    },
-    {
-      "type": "exchange-updated",
-      "eventId": "exchange-updated;https%3A%2F%2Fexchange.demo.taler.net%2F",
-      "exchangeBaseUrl": "https://exchange.demo.taler.net/";,
-      "timestamp": {
-        "t_ms": 1578334009266
-      }
-    },
-    {
-      "type": "reserve-balance-updated",
-      "eventId": 
"reserve-balance-updated;HHG1KBFSW4PM8J43D14GVJYB8F5J56RDHANY1EQSW6RTYDAQJC6G",
-      "amountExpected": "KUDOS:5",
-      "amountReserveBalance": "KUDOS:5",
-      "timestamp": {
-        "t_ms": 1578334039291
-      },
-      "newHistoryTransactions": [
-        {
-          "amount": "KUDOS:5",
-          "sender_account_url": "payto://x-taler-bank/bank.demo.taler.net/65",
-          "timestamp": {
-            "t_ms": 1578334028000
-          },
-          "wire_reference": "000000000038Y",
-          "type": "DEPOSIT"
-        }
-      ],
-      "reserveShortInfo": {
-        "exchangeBaseUrl": "https://exchange.demo.taler.net/";,
-        "reserveCreationDetail": {
-          "type": "taler-bank-withdraw",
-          "bankUrl": 
"https://bank.demo.taler.net/api/withdraw-operation/6fd6a78f-3d12-4c91-b5e4-c6fc31f44e8d";
-        },
-        "reservePub": "JPE7VR8R985WQ7ZX3EEYRTEGJQ1FAFE7P3JK1J7WFJEP7AGNTJD0"
-      }
-    },
-    {
-      "type": "withdrawn",
-      "withdrawSessionId": 
"SFW3JS0JV0GZQQ1W07TNQEAGBD84X2QMH38PJ2CCTTKSDKQFCBY0",
-      "eventId": 
"withdrawn;SFW3JS0JV0GZQQ1W07TNQEAGBD84X2QMH38PJ2CCTTKSDKQFCBY0",
-      "amountWithdrawnEffective": "KUDOS:4.8",
-      "amountWithdrawnRaw": "KUDOS:5",
-      "exchangeBaseUrl": "https://exchange.demo.taler.net/";,
-      "timestamp": {
-        "t_ms": 1578334039853
-      },
-      "withdrawalSource": {
-        "type": "reserve",
-        "reservePub": "JPE7VR8R985WQ7ZX3EEYRTEGJQ1FAFE7P3JK1J7WFJEP7AGNTJD0"
-      }
-    },
-    {
-      "type": "order-accepted",
-      "eventId": 
"order-accepted;RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
-      "orderShortInfo": {
-        "amount": "KUDOS:0.5",
-        "orderId": "2020.006-G1NT65XRPQ8GP",
-        "merchantBaseUrl": 
"https://backend.demo.taler.net/public/instances/FSF/";,
-        "proposalId": "RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
-        "summary": "Essay: 2. The GNU Project"
-      },
-      "timestamp": {
-        "t_ms": 1578334078823
-      }
-    },
-    {
-      "type": "order-redirected",
-      "eventId": 
"order-redirected;0W4EBHQJ90XX4TSQ9C0M6K9MBFJ1ENKTWH4R3CXFT986A2QHCESG",
-      "alreadyPaidOrderShortInfo": {
-        "amount": "KUDOS:0.5",
-        "orderId": "2020.006-G1NT65XRPQ8GP",
-        "merchantBaseUrl": 
"https://backend.demo.taler.net/public/instances/FSF/";,
-        "proposalId": "RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
-        "summary": "Essay: 2. The GNU Project"
-      },
-      "newOrderShortInfo": {
-        "amount": "KUDOS:0.5",
-        "orderId": "2020.006-00W4ANVVKAHAP",
-        "merchantBaseUrl": 
"https://backend.demo.taler.net/public/instances/FSF/";,
-        "proposalId": "0W4EBHQJ90XX4TSQ9C0M6K9MBFJ1ENKTWH4R3CXFT986A2QHCESG",
-        "summary": "Essay: 2. The GNU Project"
-      },
-      "timestamp": {
-        "t_ms": 1578334108380
-      }
-    },
-    {
-      "type": "payment-sent",
-      "eventId": 
"payment-sent;RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
-      "orderShortInfo": {
-        "amount": "KUDOS:0.5",
-        "orderId": "2020.006-G1NT65XRPQ8GP",
-        "merchantBaseUrl": 
"https://backend.demo.taler.net/public/instances/FSF/";,
-        "proposalId": "RNFEQ2FHF6NPM5M58HJH635TD4CE9S2SRNK3VN9SCMH3H7H0REBG",
-        "summary": "Essay: 2. The GNU Project"
-      },
-      "replay": true,
-      "sessionId": "ab48396f-3aa1-4e1f-bfb5-30852d1e0d5e",
-      "timestamp": {
-        "t_ms": 1578334108677
-      },
-      "numCoins": 6,
-      "amountPaidWithFees": "KUDOS:0.54"
-    },
-    {
-      "type": "exchange-added",
-      "builtIn": false,
-      "eventId": "exchange-added;https%3A%2F%2Fexchange.test.taler.net%2F",
-      "exchangeBaseUrl": "https://exchange.test.taler.net/";,
-      "timestamp": {
-        "t_ms": 1578334134741
-      }
-    },
-    {
-      "type": "exchange-updated",
-      "eventId": "exchange-updated;https%3A%2F%2Fexchange.test.taler.net%2F",
-      "exchangeBaseUrl": "https://exchange.test.taler.net/";,
-      "timestamp": {
-        "t_ms": 1578334135451
-      }
-    },
-    {
-      "type": "reserve-balance-updated",
-      "eventId": 
"reserve-balance-updated;498DDH4ZB41QX45FH38T4Y8JM14WX8Q2J1VKKZTE0CMS6TCPYZAG",
-      "amountExpected": "TESTKUDOS:5",
-      "amountReserveBalance": "TESTKUDOS:5",
-      "timestamp": {
-        "t_ms": 1578334141843
-      },
-      "newHistoryTransactions": [
-        {
-          "amount": "TESTKUDOS:5",
-          "sender_account_url": "payto://x-taler-bank/bank.test.taler.net/9",
-          "timestamp": {
-            "t_ms": 1578334138000
-          },
-          "wire_reference": "0000000000184",
-          "type": "DEPOSIT"
-        }
-      ],
-      "reserveShortInfo": {
-        "exchangeBaseUrl": "https://exchange.test.taler.net/";,
-        "reserveCreationDetail": {
-          "type": "taler-bank-withdraw",
-          "bankUrl": 
"https://bank.test.taler.net/api/withdraw-operation/e6210f62-d27b-4f58-815c-c5160de8804c";
-        },
-        "reservePub": "ZQ2N7V8M035HAD1HTW7ZX22NM9GAXDCGX6GSJECD2KEY9TN3C0V0"
-      }
-    },
-    {
-      "type": "withdrawn",
-      "withdrawSessionId": 
"AAVX0GVZ8GRPYX2RWANQ9J279ABA7KNFYEQ3A0C63TW7NMV0GAT0",
-      "eventId": 
"withdrawn;AAVX0GVZ8GRPYX2RWANQ9J279ABA7KNFYEQ3A0C63TW7NMV0GAT0",
-      "amountWithdrawnEffective": "TESTKUDOS:5",
-      "amountWithdrawnRaw": "TESTKUDOS:5",
-      "exchangeBaseUrl": "https://exchange.test.taler.net/";,
-      "timestamp": {
-        "t_ms": 1578334142432
-      },
-      "withdrawalSource": {
-        "type": "reserve",
-        "reservePub": "ZQ2N7V8M035HAD1HTW7ZX22NM9GAXDCGX6GSJECD2KEY9TN3C0V0"
-      }
-    },
-    {
-      "type": "refreshed",
-      "refreshGroupId": "2TARBASBNCE0X7F0D89Z3TJGPXKRARFSBH3HKZ5JFQRKPV9CA5C0",
-      "eventId": 
"refreshed;2TARBASBNCE0X7F0D89Z3TJGPXKRARFSBH3HKZ5JFQRKPV9CA5C0",
-      "timestamp": {
-        "t_ms": 1578334142528
-      },
-      "refreshReason": "pay",
-      "amountRefreshedEffective": "KUDOS:0",
-      "amountRefreshedRaw": "KUDOS:0.06",
-      "numInputCoins": 6,
-      "numOutputCoins": 0,
-      "numRefreshedInputCoins": 0
-    },
-    {
-      "type": "order-accepted",
-      "eventId": 
"order-accepted;W39MQT31M1ZY3NPF9ZSGXM8Q1K5XS5D5R1J10ZSHBREC6EZ570F0",
-      "orderShortInfo": {
-        "amount": "TESTKUDOS:1",
-        "orderId": "2020.006-00GBW7AD1VFRW",
-        "merchantBaseUrl": 
"https://backend.test.taler.net/public/instances/GNUnet/";,
-        "proposalId": "W39MQT31M1ZY3NPF9ZSGXM8Q1K5XS5D5R1J10ZSHBREC6EZ570F0",
-        "summary": "Donation to GNUnet"
-      },
-      "timestamp": {
-        "t_ms": 1578334230099
-      }
-    },
-    {
-      "type": "payment-sent",
-      "eventId": 
"payment-sent;W39MQT31M1ZY3NPF9ZSGXM8Q1K5XS5D5R1J10ZSHBREC6EZ570F0",
-      "orderShortInfo": {
-        "amount": "TESTKUDOS:1",
-        "orderId": "2020.006-00GBW7AD1VFRW",
-        "merchantBaseUrl": 
"https://backend.test.taler.net/public/instances/GNUnet/";,
-        "proposalId": "W39MQT31M1ZY3NPF9ZSGXM8Q1K5XS5D5R1J10ZSHBREC6EZ570F0",
-        "summary": "Donation to GNUnet"
-      },
-      "replay": false,
-      "timestamp": {
-        "t_ms": 1578334232527
-      },
-      "numCoins": 4,
-      "amountPaidWithFees": "TESTKUDOS:1"
-    },
-    {
-      "type": "order-accepted",
-      "eventId": 
"order-accepted;Y8230SR6DP52J61CHEAPM5NHRVK408YP5KJP6VRBGZ3QZ0TBZQ90",
-      "orderShortInfo": {
-        "amount": "TESTKUDOS:0.1",
-        "orderId": "2020.006-02RFGFSSAZY9Y",
-        "merchantBaseUrl": 
"https://backend.test.taler.net/public/instances/Taler/";,
-        "proposalId": "Y8230SR6DP52J61CHEAPM5NHRVK408YP5KJP6VRBGZ3QZ0TBZQ90",
-        "summary": "Donation to Taler"
-      },
-      "timestamp": {
-        "t_ms": 1578334258703
-      }
-    },
-    {
-      "type": "payment-sent",
-      "eventId": 
"payment-sent;Y8230SR6DP52J61CHEAPM5NHRVK408YP5KJP6VRBGZ3QZ0TBZQ90",
-      "orderShortInfo": {
-        "amount": "TESTKUDOS:0.1",
-        "orderId": "2020.006-02RFGFSSAZY9Y",
-        "merchantBaseUrl": 
"https://backend.test.taler.net/public/instances/Taler/";,
-        "proposalId": "Y8230SR6DP52J61CHEAPM5NHRVK408YP5KJP6VRBGZ3QZ0TBZQ90",
-        "summary": "Donation to Taler"
-      },
-      "replay": false,
-      "timestamp": {
-        "t_ms": 1578334260497
-      },
-      "numCoins": 1,
-      "amountPaidWithFees": "TESTKUDOS:0.1"
-    },
-    {
-      "type": "reserve-balance-updated",
-      "eventId": 
"reserve-balance-updated;NBZX24YB4GEHFXFXD5NJAC84ZZD63DFAD6Q7YFJQGX8WX9YQ7B90",
-      "amountExpected": "TESTKUDOS:15",
-      "amountReserveBalance": "TESTKUDOS:15",
-      "timestamp": {
-        "t_ms": 1578334530519
-      },
-      "newHistoryTransactions": [
-        {
-          "amount": "TESTKUDOS:15",
-          "sender_account_url": "payto://x-taler-bank/bank.test.taler.net/9",
-          "timestamp": {
-            "t_ms": 1578334522000
-          },
-          "wire_reference": "000000000018C",
-          "type": "DEPOSIT"
-        }
-      ],
-      "reserveShortInfo": {
-        "exchangeBaseUrl": "https://exchange.test.taler.net/";,
-        "reserveCreationDetail": {
-          "type": "taler-bank-withdraw",
-          "bankUrl": 
"https://bank.test.taler.net/api/withdraw-operation/6b5fae55-3634-4e6b-a877-2f8bd76dfaf9";
-        },
-        "reservePub": "5XZC4DQMNR3482443727Q2RNKRVEBEW7SFJ8N9TYV5AZ74AZJB4G"
-      }
-    },
-    {
-      "type": "withdrawn",
-      "withdrawSessionId": 
"312AKNY5BGF08PY1ZK0Z2QBEZ3481NFP9BN36Z08FHJQQZW80EZG",
-      "eventId": 
"withdrawn;312AKNY5BGF08PY1ZK0Z2QBEZ3481NFP9BN36Z08FHJQQZW80EZG",
-      "amountWithdrawnEffective": "TESTKUDOS:15",
-      "amountWithdrawnRaw": "TESTKUDOS:15",
-      "exchangeBaseUrl": "https://exchange.test.taler.net/";,
-      "timestamp": {
-        "t_ms": 1578334531085
-      },
-      "withdrawalSource": {
-        "type": "reserve",
-        "reservePub": "5XZC4DQMNR3482443727Q2RNKRVEBEW7SFJ8N9TYV5AZ74AZJB4G"
-      }
-    },
-    {
-      "type": "refreshed",
-      "refreshGroupId": "3FN9PFD2JCPS3FDHZDPRS50VQT4G7SE54JDTG2ZW2HV1R3PJ9KBG",
-      "eventId": 
"refreshed;3FN9PFD2JCPS3FDHZDPRS50VQT4G7SE54JDTG2ZW2HV1R3PJ9KBG",
-      "timestamp": {
-        "t_ms": 1578334532478
-      },
-      "refreshReason": "pay",
-      "amountRefreshedEffective": "TESTKUDOS:2.46",
-      "amountRefreshedRaw": "TESTKUDOS:2.46",
-      "numInputCoins": 1,
-      "numOutputCoins": 6,
-      "numRefreshedInputCoins": 1
-    },
-    {
-      "type": "refreshed",
-      "refreshGroupId": "DF0DQ6MGJ39R0891B0P86MY2QTMPF9FPDJN30PRBMXBZ8XEVHZE0",
-      "eventId": 
"refreshed;DF0DQ6MGJ39R0891B0P86MY2QTMPF9FPDJN30PRBMXBZ8XEVHZE0",
-      "timestamp": {
-        "t_ms": 1578334533177
-      },
-      "refreshReason": "pay",
-      "amountRefreshedEffective": "TESTKUDOS:1.12",
-      "amountRefreshedRaw": "TESTKUDOS:1.12",
-      "numInputCoins": 4,
-      "numOutputCoins": 3,
-      "numRefreshedInputCoins": 1
-    },
-    {
-      "type": "order-accepted",
-      "eventId": 
"order-accepted;KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
-      "orderShortInfo": {
-        "amount": "TESTKUDOS:0.5",
-        "orderId": "2020.006-03WSPCDEZK6HG",
-        "merchantBaseUrl": 
"https://backend.test.taler.net/public/instances/FSF/";,
-        "proposalId": "KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
-        "summary": "Essay: 6. Why Software Should Be Free"
-      },
-      "timestamp": {
-        "t_ms": 1578334554161
-      }
-    },
-    {
-      "type": "payment-sent",
-      "eventId": 
"payment-sent;KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
-      "orderShortInfo": {
-        "amount": "TESTKUDOS:0.5",
-        "orderId": "2020.006-03WSPCDEZK6HG",
-        "merchantBaseUrl": 
"https://backend.test.taler.net/public/instances/FSF/";,
-        "proposalId": "KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
-        "summary": "Essay: 6. Why Software Should Be Free"
-      },
-      "replay": false,
-      "sessionId": "489c048b-7702-49e8-b66f-2de5e33f0008",
-      "timestamp": {
-        "t_ms": 1578334556292
-      },
-      "numCoins": 5,
-      "amountPaidWithFees": "TESTKUDOS:0.5"
-    },
-    {
-      "type": "refreshed",
-      "refreshGroupId": "NG8Z05Q8CM7KCC98PDNDQR0G920J2SGYM15ACBV0PMBE6XA8Q87G",
-      "eventId": 
"refreshed;NG8Z05Q8CM7KCC98PDNDQR0G920J2SGYM15ACBV0PMBE6XA8Q87G",
-      "timestamp": {
-        "t_ms": 1578334568850
-      },
-      "refreshReason": "pay",
-      "amountRefreshedEffective": "TESTKUDOS:0.06",
-      "amountRefreshedRaw": "TESTKUDOS:0.06",
-      "numInputCoins": 5,
-      "numOutputCoins": 2,
-      "numRefreshedInputCoins": 1
-    },
-    {
-      "type": "refund",
-      "eventId": "refund;FRW9DGXKPFS9HZEGFYMABDF6FRXDYNMFHH23FT71AAKN8FHGV2EG",
-      "refundGroupId": "FRW9DGXKPFS9HZEGFYMABDF6FRXDYNMFHH23FT71AAKN8FHGV2EG",
-      "orderShortInfo": {
-        "amount": "TESTKUDOS:0.5",
-        "orderId": "2020.006-03WSPCDEZK6HG",
-        "merchantBaseUrl": 
"https://backend.test.taler.net/public/instances/FSF/";,
-        "proposalId": "KYSVHAENJFQB308KF6ENPM46VJRFAXFRDGW856P7MM90AW60REZ0",
-        "summary": "Essay: 6. Why Software Should Be Free"
-      },
-      "timestamp": {
-        "t_ms": 1578334581129
-      },
-      "amountRefundedEffective": "TESTKUDOS:0.5",
-      "amountRefundedRaw": "TESTKUDOS:0.5",
-      "amountRefundedInvalid": "TESTKUDOS:0"
-    },
-    {
-      "type": "refreshed",
-      "refreshGroupId": "TY8G0FDE83YJY3CWBYKMV891CADG06X8MTBZHE42XNQV2B2C95RG",
-      "eventId": 
"refreshed;TY8G0FDE83YJY3CWBYKMV891CADG06X8MTBZHE42XNQV2B2C95RG",
-      "timestamp": {
-        "t_ms": 1578334585583
-      },
-      "refreshReason": "refund",
-      "amountRefreshedEffective": "TESTKUDOS:0.5",
-      "amountRefreshedRaw": "TESTKUDOS:0.5",
-      "numInputCoins": 5,
-      "numOutputCoins": 6,
-      "numRefreshedInputCoins": 5
-    }
-  ]
-}
diff --git a/packages/anastasis-core/src/index.ts 
b/packages/anastasis-core/src/index.ts
index 15e1e5d9..d1afc706 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -19,8 +19,9 @@ import {
   parsePayUri,
   stringToBytes,
   TalerErrorCode,
+  TalerProtocolTimestamp,
   TalerSignaturePurpose,
-  Timestamp,
+  AbsoluteTime,
 } from "@gnu-taler/taler-util";
 import { anastasisData } from "./anastasis-data.js";
 import {
@@ -631,15 +632,13 @@ async function uploadSecret(
     logger.info(`got response for policy upload (http status ${resp.status})`);
     if (resp.status === HttpStatusCode.NoContent) {
       let policyVersion = 0;
-      let policyExpiration: Timestamp = { t_ms: 0 };
+      let policyExpiration: TalerProtocolTimestamp = { t_s: 0 };
       try {
         policyVersion = Number(resp.headers.get("Anastasis-Version") ?? "0");
       } catch (e) {}
       try {
         policyExpiration = {
-          t_ms:
-            1000 *
-            Number(resp.headers.get("Anastasis-Policy-Expiration") ?? "0"),
+          t_s: Number(resp.headers.get("Anastasis-Policy-Expiration") ?? "0"),
         };
       } catch (e) {}
       successDetails[prov.provider_url] = {
@@ -1187,7 +1186,7 @@ async function addProviderBackup(
   state: ReducerStateBackup,
   args: ActionArgsAddProvider,
 ): Promise<ReducerStateBackup> {
-  const info = await getProviderInfo(args.provider_url)
+  const info = await getProviderInfo(args.provider_url);
   return {
     ...state,
     authentication_providers: {
@@ -1202,8 +1201,10 @@ async function deleteProviderBackup(
   state: ReducerStateBackup,
   args: ActionArgsDeleteProvider,
 ): Promise<ReducerStateBackup> {
-  const authentication_providers = {... state.authentication_providers ?? {} }
-  delete authentication_providers[args.provider_url]
+  const authentication_providers = {
+    ...(state.authentication_providers ?? {}),
+  };
+  delete authentication_providers[args.provider_url];
   return {
     ...state,
     authentication_providers,
@@ -1214,7 +1215,7 @@ async function addProviderRecovery(
   state: ReducerStateRecovery,
   args: ActionArgsAddProvider,
 ): Promise<ReducerStateRecovery> {
-  const info = await getProviderInfo(args.provider_url)
+  const info = await getProviderInfo(args.provider_url);
   return {
     ...state,
     authentication_providers: {
@@ -1228,8 +1229,10 @@ async function deleteProviderRecovery(
   state: ReducerStateRecovery,
   args: ActionArgsDeleteProvider,
 ): Promise<ReducerStateRecovery> {
-  const authentication_providers = {... state.authentication_providers ?? {} }
-  delete authentication_providers[args.provider_url]
+  const authentication_providers = {
+    ...(state.authentication_providers ?? {}),
+  };
+  delete authentication_providers[args.provider_url];
   return {
     ...state,
     authentication_providers,
@@ -1347,7 +1350,8 @@ async function updateUploadFees(
       x,
     ).amount;
   };
-  const years = Duration.toIntegerYears(Duration.getRemaining(expiration));
+  const expirationTime = AbsoluteTime.fromTimestamp(expiration);
+  const years = Duration.toIntegerYears(Duration.getRemaining(expirationTime));
   logger.info(`computing fees for ${years} years`);
   // For now, we compute fees for *all* available providers.
   for (const provUrl in state.authentication_providers ?? {}) {
diff --git a/packages/anastasis-core/src/reducer-types.ts 
b/packages/anastasis-core/src/reducer-types.ts
index 8d375552..2a869fe4 100644
--- a/packages/anastasis-core/src/reducer-types.ts
+++ b/packages/anastasis-core/src/reducer-types.ts
@@ -7,7 +7,8 @@ import {
   codecForString,
   codecForTimestamp,
   Duration,
-  Timestamp,
+  TalerProtocolTimestamp,
+  AbsoluteTime,
 } from "@gnu-taler/taler-util";
 import { ChallengeFeedback } from "./challenge-feedback-types.js";
 import { KeyShare } from "./crypto.js";
@@ -43,7 +44,7 @@ export interface PolicyProvider {
 export interface SuccessDetails {
   [provider_url: string]: {
     policy_version: number;
-    policy_expiration: Timestamp;
+    policy_expiration: TalerProtocolTimestamp;
   };
 }
 
@@ -112,7 +113,7 @@ export interface ReducerStateBackup {
 
   core_secret?: CoreSecret;
 
-  expiration?: Timestamp;
+  expiration?: TalerProtocolTimestamp;
 
   upload_fees?: { fee: AmountString }[];
 
@@ -369,7 +370,7 @@ export interface ActionArgsEnterSecret {
     value: string;
     mime?: string;
   };
-  expiration: Timestamp;
+  expiration: TalerProtocolTimestamp;
 }
 
 export interface ActionArgsSelectContinent {
@@ -438,7 +439,7 @@ export interface ActionArgsAddPolicy {
 }
 
 export interface ActionArgsUpdateExpiration {
-  expiration: Timestamp;
+  expiration: TalerProtocolTimestamp;
 }
 
 export interface ActionArgsChangeVersion {
diff --git 
a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx 
b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx
index bb0c3c4b..1c6ae4b2 100644
--- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx
@@ -51,13 +51,13 @@ export const WithDetails = createExample(TestedComponent, {
   success_details: {
     "https://anastasis.demo.taler.net/": {
       policy_expiration: {
-        t_ms: "never",
+        t_s: "never",
       },
       policy_version: 0,
     },
     "https://kudos.demo.anastasis.lu/": {
       policy_expiration: {
-        t_ms: new Date().getTime() + 60 * 60 * 24 * 1000,
+        t_s: new Date().getTime() + 60 * 60 * 24,
       },
       policy_version: 1,
     },
diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx 
b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx
index 66473345..d6272d84 100644
--- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx
@@ -35,9 +35,9 @@ export function BackupFinishedScreen(): VNode {
                 </a>
                 <p>
                   version {sd.policy_version}
-                  {sd.policy_expiration.t_ms !== "never"
+                  {sd.policy_expiration.t_s !== "never"
                     ? ` expires at: ${format(
-                        new Date(sd.policy_expiration.t_ms),
+                        new Date(sd.policy_expiration.t_s),
                         "dd-MM-yyyy",
                       )}`
                     : " without expiration date"}
diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx 
b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
index e401c3d7..d9bf084a 100644
--- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
@@ -52,7 +52,7 @@ export function SecretEditorScreen(): VNode {
       await tx.transition("enter_secret", {
         secret,
         expiration: {
-          t_ms: new Date().getTime() + 1000 * 60 * 60 * 24 * 365 * 5,
+          t_s: new Date().getTime() + 60 * 60 * 24 * 365 * 5,
         },
       });
       await tx.transition("next", {});
diff --git a/packages/taler-util/src/RequestThrottler.ts 
b/packages/taler-util/src/RequestThrottler.ts
index 7689b421..a151cc63 100644
--- a/packages/taler-util/src/RequestThrottler.ts
+++ b/packages/taler-util/src/RequestThrottler.ts
@@ -15,7 +15,7 @@
  */
 
 import { Logger } from "./logging.js";
-import { getTimestampNow, timestampCmp, timestampDifference } from "./time.js";
+import { AbsoluteTime } from "./time.js";
 
 /**
  * Implementation of token bucket throttling.
@@ -46,16 +46,16 @@ class OriginState {
   tokensSecond: number = MAX_PER_SECOND;
   tokensMinute: number = MAX_PER_MINUTE;
   tokensHour: number = MAX_PER_HOUR;
-  private lastUpdate = getTimestampNow();
+  private lastUpdate = AbsoluteTime.now();
 
   private refill(): void {
-    const now = getTimestampNow();
-    if (timestampCmp(now, this.lastUpdate) < 0) {
+    const now = AbsoluteTime.now();
+    if (AbsoluteTime.cmp(now, this.lastUpdate) < 0) {
       // Did the system time change?
       this.lastUpdate = now;
       return;
     }
-    const d = timestampDifference(now, this.lastUpdate);
+    const d = AbsoluteTime.difference(now, this.lastUpdate);
     if (d.d_ms === "forever") {
       throw Error("assertion failed");
     }
diff --git a/packages/taler-util/src/ReserveTransaction.ts 
b/packages/taler-util/src/ReserveTransaction.ts
index b282ef18..50610483 100644
--- a/packages/taler-util/src/ReserveTransaction.ts
+++ b/packages/taler-util/src/ReserveTransaction.ts
@@ -38,7 +38,11 @@ import {
   EddsaPublicKeyString,
   CoinPublicKeyString,
 } from "./talerTypes";
-import { Timestamp, codecForTimestamp } from "./time.js";
+import {
+  AbsoluteTime,
+  codecForTimestamp,
+  TalerProtocolTimestamp,
+} from "./time.js";
 
 export enum ReserveTransactionType {
   Withdraw = "WITHDRAW",
@@ -98,7 +102,7 @@ export interface ReserveCreditTransaction {
   /**
    * Timestamp of the incoming wire transfer.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 }
 
 export interface ReserveClosingTransaction {
@@ -139,7 +143,7 @@ export interface ReserveClosingTransaction {
   /**
    * Time when the reserve was closed.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 }
 
 export interface ReserveRecoupTransaction {
@@ -165,7 +169,7 @@ export interface ReserveRecoupTransaction {
   /**
    * Time when the funds were paid back into the reserve.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 
   /**
    * Public key of the coin that was paid back.
@@ -182,46 +186,50 @@ export type ReserveTransaction =
   | ReserveClosingTransaction
   | ReserveRecoupTransaction;
 
-export const codecForReserveWithdrawTransaction = (): 
Codec<ReserveWithdrawTransaction> =>
-  buildCodecForObject<ReserveWithdrawTransaction>()
-    .property("amount", codecForString())
-    .property("h_coin_envelope", codecForString())
-    .property("h_denom_pub", codecForString())
-    .property("reserve_sig", codecForString())
-    .property("type", codecForConstString(ReserveTransactionType.Withdraw))
-    .property("withdraw_fee", codecForString())
-    .build("ReserveWithdrawTransaction");
-
-export const codecForReserveCreditTransaction = (): 
Codec<ReserveCreditTransaction> =>
-  buildCodecForObject<ReserveCreditTransaction>()
-    .property("amount", codecForString())
-    .property("sender_account_url", codecForString())
-    .property("timestamp", codecForTimestamp)
-    .property("wire_reference", codecForNumber())
-    .property("type", codecForConstString(ReserveTransactionType.Credit))
-    .build("ReserveCreditTransaction");
-
-export const codecForReserveClosingTransaction = (): 
Codec<ReserveClosingTransaction> =>
-  buildCodecForObject<ReserveClosingTransaction>()
-    .property("amount", codecForString())
-    .property("closing_fee", codecForString())
-    .property("exchange_pub", codecForString())
-    .property("exchange_sig", codecForString())
-    .property("h_wire", codecForString())
-    .property("timestamp", codecForTimestamp)
-    .property("type", codecForConstString(ReserveTransactionType.Closing))
-    .property("wtid", codecForString())
-    .build("ReserveClosingTransaction");
-
-export const codecForReserveRecoupTransaction = (): 
Codec<ReserveRecoupTransaction> =>
-  buildCodecForObject<ReserveRecoupTransaction>()
-    .property("amount", codecForString())
-    .property("coin_pub", codecForString())
-    .property("exchange_pub", codecForString())
-    .property("exchange_sig", codecForString())
-    .property("timestamp", codecForTimestamp)
-    .property("type", codecForConstString(ReserveTransactionType.Recoup))
-    .build("ReserveRecoupTransaction");
+export const codecForReserveWithdrawTransaction =
+  (): Codec<ReserveWithdrawTransaction> =>
+    buildCodecForObject<ReserveWithdrawTransaction>()
+      .property("amount", codecForString())
+      .property("h_coin_envelope", codecForString())
+      .property("h_denom_pub", codecForString())
+      .property("reserve_sig", codecForString())
+      .property("type", codecForConstString(ReserveTransactionType.Withdraw))
+      .property("withdraw_fee", codecForString())
+      .build("ReserveWithdrawTransaction");
+
+export const codecForReserveCreditTransaction =
+  (): Codec<ReserveCreditTransaction> =>
+    buildCodecForObject<ReserveCreditTransaction>()
+      .property("amount", codecForString())
+      .property("sender_account_url", codecForString())
+      .property("timestamp", codecForTimestamp)
+      .property("wire_reference", codecForNumber())
+      .property("type", codecForConstString(ReserveTransactionType.Credit))
+      .build("ReserveCreditTransaction");
+
+export const codecForReserveClosingTransaction =
+  (): Codec<ReserveClosingTransaction> =>
+    buildCodecForObject<ReserveClosingTransaction>()
+      .property("amount", codecForString())
+      .property("closing_fee", codecForString())
+      .property("exchange_pub", codecForString())
+      .property("exchange_sig", codecForString())
+      .property("h_wire", codecForString())
+      .property("timestamp", codecForTimestamp)
+      .property("type", codecForConstString(ReserveTransactionType.Closing))
+      .property("wtid", codecForString())
+      .build("ReserveClosingTransaction");
+
+export const codecForReserveRecoupTransaction =
+  (): Codec<ReserveRecoupTransaction> =>
+    buildCodecForObject<ReserveRecoupTransaction>()
+      .property("amount", codecForString())
+      .property("coin_pub", codecForString())
+      .property("exchange_pub", codecForString())
+      .property("exchange_sig", codecForString())
+      .property("timestamp", codecForTimestamp)
+      .property("type", codecForConstString(ReserveTransactionType.Recoup))
+      .build("ReserveRecoupTransaction");
 
 export const codecForReserveTransaction = (): Codec<ReserveTransaction> =>
   buildCodecForUnion<ReserveTransaction>()
diff --git a/packages/taler-util/src/backupTypes.ts 
b/packages/taler-util/src/backupTypes.ts
index 41a9ce98..b31a8383 100644
--- a/packages/taler-util/src/backupTypes.ts
+++ b/packages/taler-util/src/backupTypes.ts
@@ -54,7 +54,7 @@
  * Imports.
  */
 import { DenominationPubKey, UnblindedSignature } from "./talerTypes.js";
-import { Duration, Timestamp } from "./time.js";
+import { TalerProtocolDuration, TalerProtocolTimestamp } from "./time.js";
 
 /**
  * Type alias for strings that are to be treated like amounts.
@@ -120,7 +120,7 @@ export interface WalletBackupContentV1 {
    * This timestamp should only be advanced if the content
    * of the backup changes.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 
   /**
    * Per-exchange data sorted by exchange master public key.
@@ -333,10 +333,10 @@ export interface BackupRecoupGroup {
   /**
    * Timestamp when the recoup was started.
    */
-  timestamp_created: Timestamp;
+  timestamp_created: TalerProtocolTimestamp;
 
-  timestamp_finish?: Timestamp;
-  finish_clock?: Timestamp;
+  timestamp_finish?: TalerProtocolTimestamp;
+  finish_clock?: TalerProtocolTimestamp;
   finish_is_failure?: boolean;
 
   /**
@@ -486,14 +486,14 @@ export interface BackupTip {
    * Has the user accepted the tip?  Only after the tip has been accepted coins
    * withdrawn from the tip may be used.
    */
-  timestamp_accepted: Timestamp | undefined;
+  timestamp_accepted: TalerProtocolTimestamp | undefined;
 
   /**
    * When was the tip first scanned by the wallet?
    */
-  timestamp_created: Timestamp;
+  timestamp_created: TalerProtocolTimestamp;
 
-  timestamp_finished?: Timestamp;
+  timestamp_finished?: TalerProtocolTimestamp;
   finish_is_failure?: boolean;
 
   /**
@@ -504,7 +504,7 @@ export interface BackupTip {
   /**
    * Timestamp, the tip can't be picked up anymore after this deadline.
    */
-  timestamp_expiration: Timestamp;
+  timestamp_expiration: TalerProtocolTimestamp;
 
   /**
    * The exchange that will sign our coins, chosen by the merchant.
@@ -613,9 +613,9 @@ export interface BackupRefreshGroup {
    */
   old_coins: BackupRefreshOldCoin[];
 
-  timestamp_created: Timestamp;
+  timestamp_created: TalerProtocolTimestamp;
 
-  timestamp_finish?: Timestamp;
+  timestamp_finish?: TalerProtocolTimestamp;
   finish_is_failure?: boolean;
 }
 
@@ -636,9 +636,9 @@ export interface BackupWithdrawalGroup {
    * When was the withdrawal operation started started?
    * Timestamp in milliseconds.
    */
-  timestamp_created: Timestamp;
+  timestamp_created: TalerProtocolTimestamp;
 
-  timestamp_finish?: Timestamp;
+  timestamp_finish?: TalerProtocolTimestamp;
   finish_is_failure?: boolean;
 
   /**
@@ -672,12 +672,12 @@ export interface BackupRefundItemCommon {
   /**
    * Execution time as claimed by the merchant
    */
-  execution_time: Timestamp;
+  execution_time: TalerProtocolTimestamp;
 
   /**
    * Time when the wallet became aware of the refund.
    */
-  obtained_time: Timestamp;
+  obtained_time: TalerProtocolTimestamp;
 
   /**
    * Amount refunded for the coin.
@@ -788,7 +788,7 @@ export interface BackupPurchase {
    * Timestamp of the first time that sending a payment to the merchant
    * for this purchase was successful.
    */
-  timestamp_first_successful_pay: Timestamp | undefined;
+  timestamp_first_successful_pay: TalerProtocolTimestamp | undefined;
 
   /**
    * Signature by the merchant confirming the payment.
@@ -799,7 +799,7 @@ export interface BackupPurchase {
    * When was the purchase made?
    * Refers to the time that the user accepted.
    */
-  timestamp_accept: Timestamp;
+  timestamp_accept: TalerProtocolTimestamp;
 
   /**
    * Pending refunds for the purchase.  A refund is pending
@@ -815,7 +815,7 @@ export interface BackupPurchase {
   /**
    * Continue querying the refund status until this deadline has expired.
    */
-  auto_refund_deadline: Timestamp | undefined;
+  auto_refund_deadline: TalerProtocolTimestamp | undefined;
 }
 
 /**
@@ -857,22 +857,22 @@ export interface BackupDenomination {
   /**
    * Validity start date of the denomination.
    */
-  stamp_start: Timestamp;
+  stamp_start: TalerProtocolTimestamp;
 
   /**
    * Date after which the currency can't be withdrawn anymore.
    */
-  stamp_expire_withdraw: Timestamp;
+  stamp_expire_withdraw: TalerProtocolTimestamp;
 
   /**
    * Date after the denomination officially doesn't exist anymore.
    */
-  stamp_expire_legal: Timestamp;
+  stamp_expire_legal: TalerProtocolTimestamp;
 
   /**
    * Data after which coins of this denomination can't be deposited anymore.
    */
-  stamp_expire_deposit: Timestamp;
+  stamp_expire_deposit: TalerProtocolTimestamp;
 
   /**
    * Signature by the exchange's master key over the denomination
@@ -903,7 +903,7 @@ export interface BackupDenomination {
    * The list issue date of the exchange "/keys" response
    * that this denomination was last seen in.
    */
-  list_issue_date: Timestamp;
+  list_issue_date: TalerProtocolTimestamp;
 }
 
 /**
@@ -923,14 +923,14 @@ export interface BackupReserve {
   /**
    * Time when the reserve was created.
    */
-  timestamp_created: Timestamp;
+  timestamp_created: TalerProtocolTimestamp;
 
   /**
    * Timestamp of the last observed activity.
    *
    * Used to compute when to give up querying the exchange.
    */
-  timestamp_last_activity: Timestamp;
+  timestamp_last_activity: TalerProtocolTimestamp;
 
   /**
    * Timestamp of when the reserve closed.
@@ -938,7 +938,7 @@ export interface BackupReserve {
    * Note that the last activity can be after the closing time
    * due to recouping.
    */
-  timestamp_closed?: Timestamp;
+  timestamp_closed?: TalerProtocolTimestamp;
 
   /**
    * Wire information (as payto URI) for the bank account that
@@ -977,14 +977,14 @@ export interface BackupReserve {
     /**
      * Time when the information about this reserve was posted to the bank.
      */
-    timestamp_reserve_info_posted: Timestamp | undefined;
+    timestamp_reserve_info_posted: TalerProtocolTimestamp | undefined;
 
     /**
      * Time when the reserve was confirmed by the bank.
      *
      * Set to undefined if not confirmed yet.
      */
-    timestamp_bank_confirmed: Timestamp | undefined;
+    timestamp_bank_confirmed: TalerProtocolTimestamp | undefined;
   };
 
   /**
@@ -1033,12 +1033,12 @@ export interface BackupExchangeWireFee {
   /**
    * Start date of the fee.
    */
-  start_stamp: Timestamp;
+  start_stamp: TalerProtocolTimestamp;
 
   /**
    * End date of the fee.
    */
-  end_stamp: Timestamp;
+  end_stamp: TalerProtocolTimestamp;
 
   /**
    * Signature made by the exchange master key.
@@ -1050,9 +1050,9 @@ export interface BackupExchangeWireFee {
  * Structure of one exchange signing key in the /keys response.
  */
 export class BackupExchangeSignKey {
-  stamp_start: Timestamp;
-  stamp_expire: Timestamp;
-  stamp_end: Timestamp;
+  stamp_start: TalerProtocolTimestamp;
+  stamp_expire: TalerProtocolTimestamp;
+  stamp_end: TalerProtocolTimestamp;
   key: string;
   master_sig: string;
 }
@@ -1112,7 +1112,7 @@ export interface BackupExchange {
    *
    * Used to facilitate automatic merging.
    */
-  update_clock: Timestamp;
+  update_clock: TalerProtocolTimestamp;
 }
 
 /**
@@ -1161,7 +1161,7 @@ export interface BackupExchangeDetails {
   /**
    * Closing delay of reserves.
    */
-  reserve_closing_delay: Duration;
+  reserve_closing_delay: TalerProtocolDuration;
 
   /**
    * Signing keys we got from the exchange, can also contain
@@ -1187,7 +1187,7 @@ export interface BackupExchangeDetails {
   /**
    * Timestamp when the ToS has been accepted.
    */
-  tos_accepted_timestamp: Timestamp | undefined;
+  tos_accepted_timestamp: TalerProtocolTimestamp | undefined;
 }
 
 export enum BackupProposalStatus {
@@ -1248,7 +1248,7 @@ export interface BackupProposal {
    * Timestamp of when the record
    * was created.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 
   /**
    * Private key for the nonce.
diff --git a/packages/taler-util/src/talerTypes.ts 
b/packages/taler-util/src/talerTypes.ts
index 4ccfffce..b1bf6ab3 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -38,15 +38,14 @@ import {
   codecForConstNumber,
   buildCodecForUnion,
   codecForConstString,
-  codecForEither,
 } from "./codec.js";
 import {
-  Timestamp,
   codecForTimestamp,
-  Duration,
   codecForDuration,
+  TalerProtocolTimestamp,
+  TalerProtocolDuration,
 } from "./time.js";
-import { Amounts, codecForAmountString } from "./amounts.js";
+import { codecForAmountString } from "./amounts.js";
 import { strcmp } from "./helpers.js";
 
 /**
@@ -86,24 +85,24 @@ export class ExchangeDenomination {
   /**
    * Start date from which withdraw is allowed.
    */
-  stamp_start: Timestamp;
+  stamp_start: TalerProtocolTimestamp;
 
   /**
    * End date for withdrawing.
    */
-  stamp_expire_withdraw: Timestamp;
+  stamp_expire_withdraw: TalerProtocolTimestamp;
 
   /**
    * Expiration date after which the exchange can forget about
    * the currency.
    */
-  stamp_expire_legal: Timestamp;
+  stamp_expire_legal: TalerProtocolTimestamp;
 
   /**
    * Date after which the coins of this denomination can't be
    * deposited anymore.
    */
-  stamp_expire_deposit: Timestamp;
+  stamp_expire_deposit: TalerProtocolTimestamp;
 
   /**
    * Signature over the denomination information by the exchange's master
@@ -394,7 +393,7 @@ export interface Product {
   taxes?: Tax[];
 
   // time indicating when this product should be delivered
-  delivery_date?: Timestamp;
+  delivery_date?: TalerProtocolTimestamp;
 }
 
 export interface InternationalizedString {
@@ -413,7 +412,7 @@ export interface ContractTerms {
   /**
    * Hash of the merchant's wire details.
    */
-  auto_refund?: Duration;
+  auto_refund?: TalerProtocolDuration;
 
   /**
    * Wire method the merchant wants to use.
@@ -445,7 +444,7 @@ export interface ContractTerms {
   /**
    * Deadline to pay for the contract.
    */
-  pay_deadline: Timestamp;
+  pay_deadline: TalerProtocolTimestamp;
 
   /**
    * Maximum deposit fee covered by the merchant.
@@ -466,7 +465,7 @@ export interface ContractTerms {
    * Time indicating when the order should be delivered.
    * May be overwritten by individual products.
    */
-  delivery_date?: Timestamp;
+  delivery_date?: TalerProtocolTimestamp;
 
   /**
    * Delivery location for (all!) products.
@@ -486,17 +485,17 @@ export interface ContractTerms {
   /**
    * Deadline for refunds.
    */
-  refund_deadline: Timestamp;
+  refund_deadline: TalerProtocolTimestamp;
 
   /**
    * Deadline for the wire transfer.
    */
-  wire_transfer_deadline: Timestamp;
+  wire_transfer_deadline: TalerProtocolTimestamp;
 
   /**
    * Time when the contract was generated by the merchant.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 
   /**
    * Order id to uniquely identify the purchase within
@@ -700,9 +699,9 @@ export class Recoup {
  * Structure of one exchange signing key in the /keys response.
  */
 export class ExchangeSignKeyJson {
-  stamp_start: Timestamp;
-  stamp_expire: Timestamp;
-  stamp_end: Timestamp;
+  stamp_start: TalerProtocolTimestamp;
+  stamp_expire: TalerProtocolTimestamp;
+  stamp_end: TalerProtocolTimestamp;
   key: EddsaPublicKeyString;
   master_sig: EddsaSignatureString;
 }
@@ -729,7 +728,7 @@ export class ExchangeKeysJson {
   /**
    * Timestamp when this response was issued.
    */
-  list_issue_date: Timestamp;
+  list_issue_date: TalerProtocolTimestamp;
 
   /**
    * List of revoked denominations.
@@ -747,7 +746,7 @@ export class ExchangeKeysJson {
    */
   version: string;
 
-  reserve_closing_delay: Duration;
+  reserve_closing_delay: TalerProtocolDuration;
 }
 
 /**
@@ -774,12 +773,12 @@ export class WireFeesJson {
   /**
    * Date from which the fee applies.
    */
-  start_date: Timestamp;
+  start_date: TalerProtocolTimestamp;
 
   /**
    * Data after which the fee doesn't apply anymore.
    */
-  end_date: Timestamp;
+  end_date: TalerProtocolTimestamp;
 }
 
 export interface AccountInfo {
@@ -850,7 +849,7 @@ export class TipPickupGetResponse {
 
   exchange_url: string;
 
-  expiration: Timestamp;
+  expiration: TalerProtocolTimestamp;
 }
 
 export enum DenomKeyType {
@@ -1067,7 +1066,7 @@ export interface MerchantCoinRefundSuccessStatus {
   // to the customer.
   refund_amount: AmountString;
 
-  execution_time: Timestamp;
+  execution_time: TalerProtocolTimestamp;
 }
 
 export interface MerchantCoinRefundFailureStatus {
@@ -1092,7 +1091,7 @@ export interface MerchantCoinRefundFailureStatus {
   // to the customer.
   refund_amount: AmountString;
 
-  execution_time: Timestamp;
+  execution_time: TalerProtocolTimestamp;
 }
 
 export interface MerchantOrderStatusUnpaid {
@@ -1733,7 +1732,7 @@ export interface DepositSuccess {
   transaction_base_url?: string;
 
   // timestamp when the deposit was received by the exchange.
-  exchange_timestamp: Timestamp;
+  exchange_timestamp: TalerProtocolTimestamp;
 
   // the EdDSA signature of TALER_DepositConfirmationPS using a current
   // signing key of the exchange affirming the successful
diff --git a/packages/taler-util/src/time.ts b/packages/taler-util/src/time.ts
index 9f957101..43cb7ad4 100644
--- a/packages/taler-util/src/time.ts
+++ b/packages/taler-util/src/time.ts
@@ -23,13 +23,41 @@
  */
 import { Codec, renderContext, Context } from "./codec.js";
 
-export interface Timestamp {
+export interface AbsoluteTime {
   /**
    * Timestamp in milliseconds.
    */
   readonly t_ms: number | "never";
 }
 
+export interface TalerProtocolTimestamp {
+  readonly t_s: number | "never";
+}
+
+export namespace TalerProtocolTimestamp {
+  export function now(): TalerProtocolTimestamp {
+    return AbsoluteTime.toTimestamp(AbsoluteTime.now());
+  }
+
+  export function zero(): TalerProtocolTimestamp {
+    return {
+      t_s: 0,
+    };
+  }
+
+  export function never(): TalerProtocolTimestamp {
+    return {
+      t_s: "never",
+    };
+  }
+
+  export function fromSeconds(s: number): TalerProtocolTimestamp {
+    return {
+      t_s: s,
+    };
+  }
+}
+
 export interface Duration {
   /**
    * Duration in milliseconds.
@@ -37,40 +65,32 @@ export interface Duration {
   readonly d_ms: number | "forever";
 }
 
+export interface TalerProtocolDuration {
+  readonly d_us: number | "forever";
+}
+
 let timeshift = 0;
 
 export function setDangerousTimetravel(dt: number): void {
   timeshift = dt;
 }
 
-export function getTimestampNow(): Timestamp {
-  return {
-    t_ms: new Date().getTime() + timeshift,
-  };
-}
-
-export function isTimestampExpired(t: Timestamp) {
-  return timestampCmp(t, getTimestampNow()) <= 0;
-}
-
-export function getDurationRemaining(
-  deadline: Timestamp,
-  now = getTimestampNow(),
-): Duration {
-  if (deadline.t_ms === "never") {
-    return { d_ms: "forever" };
-  }
-  if (now.t_ms === "never") {
-    throw Error("invalid argument for 'now'");
-  }
-  if (deadline.t_ms < now.t_ms) {
-    return { d_ms: 0 };
-  }
-  return { d_ms: deadline.t_ms - now.t_ms };
-}
-
 export namespace Duration {
-  export const getRemaining = getDurationRemaining;
+  export function getRemaining(
+    deadline: AbsoluteTime,
+    now = AbsoluteTime.now(),
+  ): Duration {
+    if (deadline.t_ms === "never") {
+      return { d_ms: "forever" };
+    }
+    if (now.t_ms === "never") {
+      throw Error("invalid argument for 'now'");
+    }
+    if (deadline.t_ms < now.t_ms) {
+      return { d_ms: 0 };
+    }
+    return { d_ms: deadline.t_ms - now.t_ms };
+  }
   export function toIntegerYears(d: Duration): number {
     if (typeof d.d_ms !== "number") {
       throw Error("infinite duration");
@@ -81,33 +101,152 @@ export namespace Duration {
   export function getForever(): Duration {
     return { d_ms: "forever" };
   }
+  export function fromTalerProtocolDuration(
+    d: TalerProtocolDuration,
+  ): Duration {
+    if (d.d_us === "forever") {
+      return {
+        d_ms: "forever",
+      };
+    }
+    return {
+      d_ms: d.d_us / 1000,
+    };
+  }
 }
 
-export namespace Timestamp {
-  export const now = getTimestampNow;
-  export const min = timestampMin;
-  export const isExpired = isTimestampExpired;
-  export const truncateToSecond = timestampTruncateToSecond;
-}
+export namespace AbsoluteTime {
+  export function now(): AbsoluteTime {
+    return {
+      t_ms: new Date().getTime() + timeshift,
+    };
+  }
 
-export function timestampMin(t1: Timestamp, t2: Timestamp): Timestamp {
-  if (t1.t_ms === "never") {
-    return { t_ms: t2.t_ms };
+  export function never(): AbsoluteTime {
+    return {
+      t_ms: "never",
+    };
   }
-  if (t2.t_ms === "never") {
-    return { t_ms: t2.t_ms };
+
+  export function cmp(t1: AbsoluteTime, t2: AbsoluteTime): number {
+    if (t1.t_ms === "never") {
+      if (t2.t_ms === "never") {
+        return 0;
+      }
+      return 1;
+    }
+    if (t2.t_ms === "never") {
+      return -1;
+    }
+    if (t1.t_ms == t2.t_ms) {
+      return 0;
+    }
+    if (t1.t_ms > t2.t_ms) {
+      return 1;
+    }
+    return -1;
+  }
+
+  export function min(t1: AbsoluteTime, t2: AbsoluteTime): AbsoluteTime {
+    if (t1.t_ms === "never") {
+      return { t_ms: t2.t_ms };
+    }
+    if (t2.t_ms === "never") {
+      return { t_ms: t2.t_ms };
+    }
+    return { t_ms: Math.min(t1.t_ms, t2.t_ms) };
   }
-  return { t_ms: Math.min(t1.t_ms, t2.t_ms) };
-}
 
-export function timestampMax(t1: Timestamp, t2: Timestamp): Timestamp {
-  if (t1.t_ms === "never") {
-    return { t_ms: "never" };
+  export function max(t1: AbsoluteTime, t2: AbsoluteTime): AbsoluteTime {
+    if (t1.t_ms === "never") {
+      return { t_ms: "never" };
+    }
+    if (t2.t_ms === "never") {
+      return { t_ms: "never" };
+    }
+    return { t_ms: Math.max(t1.t_ms, t2.t_ms) };
   }
-  if (t2.t_ms === "never") {
-    return { t_ms: "never" };
+
+  export function difference(t1: AbsoluteTime, t2: AbsoluteTime): Duration {
+    if (t1.t_ms === "never") {
+      return { d_ms: "forever" };
+    }
+    if (t2.t_ms === "never") {
+      return { d_ms: "forever" };
+    }
+    return { d_ms: Math.abs(t1.t_ms - t2.t_ms) };
+  }
+
+  export function isExpired(t: AbsoluteTime) {
+    return cmp(t, now()) <= 0;
+  }
+
+  export function fromTimestamp(t: TalerProtocolTimestamp): AbsoluteTime {
+    if (t.t_s === "never") {
+      return { t_ms: "never" };
+    }
+    return {
+      t_ms: t.t_s * 1000,
+    };
+  }
+
+  export function toTimestamp(at: AbsoluteTime): TalerProtocolTimestamp {
+    if (at.t_ms === "never") {
+      return { t_s: "never" };
+    }
+    return {
+      t_s: Math.floor(at.t_ms / 1000),
+    };
+  }
+
+  export function isBetween(
+    t: AbsoluteTime,
+    start: AbsoluteTime,
+    end: AbsoluteTime,
+  ): boolean {
+    if (cmp(t, start) < 0) {
+      return false;
+    }
+    if (cmp(t, end) > 0) {
+      return false;
+    }
+    return true;
+  }
+
+  export function toIsoString(t: AbsoluteTime): string {
+    if (t.t_ms === "never") {
+      return "<never>";
+    } else {
+      return new Date(t.t_ms).toISOString();
+    }
+  }
+
+  export function addDuration(t1: AbsoluteTime, d: Duration): AbsoluteTime {
+    if (t1.t_ms === "never" || d.d_ms === "forever") {
+      return { t_ms: "never" };
+    }
+    return { t_ms: t1.t_ms + d.d_ms };
+  }
+
+  export function subtractDuraction(
+    t1: AbsoluteTime,
+    d: Duration,
+  ): AbsoluteTime {
+    if (t1.t_ms === "never") {
+      return { t_ms: "never" };
+    }
+    if (d.d_ms === "forever") {
+      return { t_ms: 0 };
+    }
+    return { t_ms: Math.max(0, t1.t_ms - d.d_ms) };
+  }
+
+  export function stringify(t: AbsoluteTime): string {
+    if (t.t_ms === "never") {
+      return "never";
+    }
+    return new Date(t.t_ms).toISOString();
   }
-  return { t_ms: Math.max(t1.t_ms, t2.t_ms) };
 }
 
 const SECONDS = 1000;
@@ -135,19 +274,6 @@ export function durationFromSpec(spec: {
   return { d_ms };
 }
 
-/**
- * Truncate a timestamp so that that it represents a multiple
- * of seconds.  The timestamp is always rounded down.
- */
-export function timestampTruncateToSecond(t1: Timestamp): Timestamp {
-  if (t1.t_ms === "never") {
-    return { t_ms: "never" };
-  }
-  return {
-    t_ms: Math.floor(t1.t_ms / 1000) * 1000,
-  };
-}
-
 export function durationMin(d1: Duration, d2: Duration): Duration {
   if (d1.d_ms === "forever") {
     return { d_ms: d2.d_ms };
@@ -182,111 +308,33 @@ export function durationAdd(d1: Duration, d2: Duration): 
Duration {
   return { d_ms: d1.d_ms + d2.d_ms };
 }
 
-export function timestampCmp(t1: Timestamp, t2: Timestamp): number {
-  if (t1.t_ms === "never") {
-    if (t2.t_ms === "never") {
-      return 0;
-    }
-    return 1;
-  }
-  if (t2.t_ms === "never") {
-    return -1;
-  }
-  if (t1.t_ms == t2.t_ms) {
-    return 0;
-  }
-  if (t1.t_ms > t2.t_ms) {
-    return 1;
-  }
-  return -1;
-}
-
-export function timestampAddDuration(t1: Timestamp, d: Duration): Timestamp {
-  if (t1.t_ms === "never" || d.d_ms === "forever") {
-    return { t_ms: "never" };
-  }
-  return { t_ms: t1.t_ms + d.d_ms };
-}
-
-export function timestampSubtractDuraction(
-  t1: Timestamp,
-  d: Duration,
-): Timestamp {
-  if (t1.t_ms === "never") {
-    return { t_ms: "never" };
-  }
-  if (d.d_ms === "forever") {
-    return { t_ms: 0 };
-  }
-  return { t_ms: Math.max(0, t1.t_ms - d.d_ms) };
-}
-
-export function stringifyTimestamp(t: Timestamp): string {
-  if (t.t_ms === "never") {
-    return "never";
-  }
-  return new Date(t.t_ms).toISOString();
-}
-
-export function timestampDifference(t1: Timestamp, t2: Timestamp): Duration {
-  if (t1.t_ms === "never") {
-    return { d_ms: "forever" };
-  }
-  if (t2.t_ms === "never") {
-    return { d_ms: "forever" };
-  }
-  return { d_ms: Math.abs(t1.t_ms - t2.t_ms) };
-}
-
-export function timestampToIsoString(t: Timestamp): string {
-  if (t.t_ms === "never") {
-    return "<never>";
-  } else {
-    return new Date(t.t_ms).toISOString();
-  }
-}
-
-export function timestampIsBetween(
-  t: Timestamp,
-  start: Timestamp,
-  end: Timestamp,
-): boolean {
-  if (timestampCmp(t, start) < 0) {
-    return false;
-  }
-  if (timestampCmp(t, end) > 0) {
-    return false;
-  }
-  return true;
-}
-
-export const codecForTimestamp: Codec<Timestamp> = {
-  decode(x: any, c?: Context): Timestamp {
-    const t_ms = x.t_ms;
-    if (typeof t_ms === "string") {
-      if (t_ms === "never") {
-        return { t_ms: "never" };
+export const codecForTimestamp: Codec<TalerProtocolTimestamp> = {
+  decode(x: any, c?: Context): TalerProtocolTimestamp {
+    const t_s = x.t_s;
+    if (typeof t_s === "string") {
+      if (t_s === "never") {
+        return { t_s: "never" };
       }
       throw Error(`expected timestamp at ${renderContext(c)}`);
     }
-    if (typeof t_ms === "number") {
-      return { t_ms };
+    if (typeof t_s === "number") {
+      return { t_s };
     }
     throw Error(`expected timestamp at ${renderContext(c)}`);
   },
 };
 
-export const codecForDuration: Codec<Duration> = {
-  decode(x: any, c?: Context): Duration {
-    const d_ms = x.d_ms;
-    if (typeof d_ms === "string") {
-      if (d_ms === "forever") {
-        return { d_ms: "forever" };
+export const codecForDuration: Codec<TalerProtocolDuration> = {
+  decode(x: any, c?: Context): TalerProtocolDuration {
+    const d_us = x.d_us;
+    if (typeof d_us === "string") {
+      if (d_us === "forever") {
+        return { d_us: "forever" };
       }
       throw Error(`expected duration at ${renderContext(c)}`);
     }
-    if (typeof d_ms === "number") {
-      return { d_ms };
+    if (typeof d_us === "number") {
+      return { d_us };
     }
     throw Error(`expected duration at ${renderContext(c)}`);
   },
diff --git a/packages/taler-util/src/transactionsTypes.ts 
b/packages/taler-util/src/transactionsTypes.ts
index e780ca41..bccbc773 100644
--- a/packages/taler-util/src/transactionsTypes.ts
+++ b/packages/taler-util/src/transactionsTypes.ts
@@ -24,7 +24,7 @@
 /**
  * Imports.
  */
-import { Timestamp } from "./time.js";
+import { TalerProtocolTimestamp } from "./time.js";
 import {
   AmountString,
   Product,
@@ -73,7 +73,7 @@ export interface TransactionCommon {
   type: TransactionType;
 
   // main timestamp of the transaction
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 
   // true if the transaction is still pending, false otherwise
   // If a transaction is not longer pending, its timestamp will be updated,
diff --git a/packages/taler-util/src/types-test.ts 
b/packages/taler-util/src/types-test.ts
index 6998bb5f..e8af1311 100644
--- a/packages/taler-util/src/types-test.ts
+++ b/packages/taler-util/src/types-test.ts
@@ -29,13 +29,13 @@ test("contract terms validation", (t) => {
     merchant_pub: "12345",
     merchant: { name: "Foo" },
     order_id: "test_order",
-    pay_deadline: { t_ms: 42 },
-    wire_transfer_deadline: { t_ms: 42 },
+    pay_deadline: { t_s: 42 },
+    wire_transfer_deadline: { t_s: 42 },
     merchant_base_url: "https://example.com/pay";,
     products: [],
-    refund_deadline: { t_ms: 42 },
+    refund_deadline: { t_s: 42 },
     summary: "hello",
-    timestamp: { t_ms: 42 },
+    timestamp: { t_s: 42 },
     wire_method: "test",
   };
 
@@ -71,13 +71,13 @@ test("contract terms validation (locations)", (t) => {
       },
     },
     order_id: "test_order",
-    pay_deadline: { t_ms: 42 },
-    wire_transfer_deadline: { t_ms: 42 },
+    pay_deadline: { t_s: 42 },
+    wire_transfer_deadline: { t_s: 42 },
     merchant_base_url: "https://example.com/pay";,
     products: [],
-    refund_deadline: { t_ms: 42 },
+    refund_deadline: { t_s: 42 },
     summary: "hello",
-    timestamp: { t_ms: 42 },
+    timestamp: { t_s: 42 },
     wire_method: "test",
     delivery_location: {
       country: "FR",
diff --git a/packages/taler-util/src/walletTypes.ts 
b/packages/taler-util/src/walletTypes.ts
index 9a3f1f8f..3c4fa96c 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -32,7 +32,11 @@ import {
   codecForAmountJson,
   codecForAmountString,
 } from "./amounts.js";
-import { Timestamp, codecForTimestamp } from "./time.js";
+import {
+  AbsoluteTime,
+  codecForTimestamp,
+  TalerProtocolTimestamp,
+} from "./time.js";
 import {
   buildCodecForObject,
   codecForString,
@@ -299,7 +303,7 @@ export interface PrepareTipResult {
    * Time when the tip will expire.  After it expired, it can't be picked
    * up anymore.
    */
-  expirationTimestamp: Timestamp;
+  expirationTimestamp: TalerProtocolTimestamp;
 }
 
 export const codecForPrepareTipResult = (): Codec<PrepareTipResult> =>
@@ -460,7 +464,7 @@ export interface TalerErrorDetails {
 
 /**
  * Minimal information needed about a planchet for unblinding a signature.
- * 
+ *
  * Can be a withdrawal/tipping/refresh planchet.
  */
 export interface PlanchetUnblindInfo {
@@ -527,8 +531,8 @@ export interface DepositInfo {
   coinPub: string;
   coinPriv: string;
   spendAmount: AmountJson;
-  timestamp: Timestamp;
-  refundDeadline: Timestamp;
+  timestamp: TalerProtocolTimestamp;
+  refundDeadline: TalerProtocolTimestamp;
   merchantPub: string;
   feeDeposit: AmountJson;
   wireInfoHash: string;
diff --git a/packages/taler-wallet-cli/src/harness/harness.ts 
b/packages/taler-wallet-cli/src/harness/harness.ts
index 1500d774..63bb17fc 100644
--- a/packages/taler-wallet-cli/src/harness/harness.ts
+++ b/packages/taler-wallet-cli/src/harness/harness.ts
@@ -478,7 +478,7 @@ class BankServiceBase {
     protected globalTestState: GlobalTestState,
     protected bankConfig: BankConfig,
     protected configFile: string,
-  ) { }
+  ) {}
 }
 
 /**
@@ -920,7 +920,7 @@ export class FakeBankService {
     private globalTestState: GlobalTestState,
     private bankConfig: FakeBankConfig,
     private configFile: string,
-  ) { }
+  ) {}
 
   async start(): Promise<void> {
     this.proc = this.globalTestState.spawnService(
@@ -1175,7 +1175,7 @@ export class ExchangeService implements 
ExchangeServiceInterface {
     private exchangeConfig: ExchangeConfig,
     private configFilename: string,
     private keyPair: EddsaKeyPair,
-  ) { }
+  ) {}
 
   get name() {
     return this.exchangeConfig.name;
@@ -1276,7 +1276,7 @@ export class ExchangeService implements 
ExchangeServiceInterface {
             accTargetType,
             `${this.exchangeConfig.currency}:0.01`,
             `${this.exchangeConfig.currency}:0.01`,
-            // `${this.exchangeConfig.currency}:0.01`,
+            `${this.exchangeConfig.currency}:0.01`,
             "upload",
           ],
         );
@@ -1398,7 +1398,7 @@ export class MerchantApiClient {
   constructor(
     private baseUrl: string,
     public readonly auth: MerchantAuthConfiguration,
-  ) { }
+  ) {}
 
   async changeAuth(auth: MerchantAuthConfiguration): Promise<void> {
     const url = new URL("private/auth", this.baseUrl);
@@ -1591,7 +1591,7 @@ export class MerchantService implements 
MerchantServiceInterface {
     private globalState: GlobalTestState,
     private merchantConfig: MerchantConfig,
     private configFilename: string,
-  ) { }
+  ) {}
 
   private currentTimetravel: Duration | undefined;
 
@@ -1888,8 +1888,10 @@ export class WalletCli {
         const resp = await sh(
           self.globalTestState,
           `wallet-${self.name}`,
-          `taler-wallet-cli ${self.timetravelArg ?? ""
-          } --no-throttle -LTRACE --wallet-db '${self.dbfile
+          `taler-wallet-cli ${
+            self.timetravelArg ?? ""
+          } --no-throttle -LTRACE --wallet-db '${
+            self.dbfile
           }' api '${op}' ${shellWrap(JSON.stringify(payload))}`,
         );
         console.log("--- wallet core response ---");
diff --git a/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts 
b/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts
index a93a0ed2..dcb1a2b4 100644
--- a/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts
+++ b/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts
@@ -38,7 +38,7 @@ import {
   codecForAny,
   buildCodecForUnion,
   AmountString,
-  Timestamp,
+  AbsoluteTime,
   CoinPublicKeyString,
   EddsaPublicKeyString,
   codecForAmountString,
@@ -195,7 +195,7 @@ export interface RefundDetails {
   reason: string;
 
   // when was the refund approved
-  timestamp: Timestamp;
+  timestamp: AbsoluteTime;
 
   // Total amount that was refunded (minus a refund fee).
   amount: AmountString;
@@ -209,7 +209,7 @@ export interface TransactionWireTransfer {
   wtid: string;
 
   // execution time of the wire transfer
-  execution_time: Timestamp;
+  execution_time: AbsoluteTime;
 
   // Total amount that has been wire transferred
   // to the merchant
@@ -247,10 +247,10 @@ export interface ReserveStatusEntry {
   reserve_pub: string;
 
   // Timestamp when it was established
-  creation_time: Timestamp;
+  creation_time: AbsoluteTime;
 
   // Timestamp when it expires
-  expiration_time: Timestamp;
+  expiration_time: AbsoluteTime;
 
   // Initial amount as per reserve creation call
   merchant_initial_amount: AmountString;
@@ -281,7 +281,7 @@ export interface TipCreateConfirmation {
   tip_status_url: string;
 
   // when does the tip expire
-  tip_expiration: Timestamp;
+  tip_expiration: AbsoluteTime;
 }
 
 export interface TipCreateRequest {
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts
index 8e410975..8a11b79c 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts
@@ -32,6 +32,7 @@ import {
   BankApi,
   BankAccessApi,
   CreditDebitIndicator,
+  OperationFailedError,
 } from "@gnu-taler/taler-wallet-core";
 
 /**
@@ -106,8 +107,7 @@ export async function runBankApiTest(t: GlobalTestState) {
     const e = await t.assertThrowsAsync(async () => {
       await BankApi.registerAccount(bank, "user1", "pw1");
     });
-    t.assertAxiosError(e);
-    t.assertTrue(e.response?.status === 409);
+    t.assertTrue(e.details.httpStatusCode === 409);
   }
 
   let balResp = await BankAccessApi.getAccountBalance(bank, bankUser);
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
index ed07114e..e2b81ee6 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
@@ -18,11 +18,11 @@
  * Imports.
  */
 import {
+  AbsoluteTime,
   codecForExchangeKeysJson,
   DenominationPubKey,
   Duration,
   durationFromSpec,
-  stringifyTimestamp,
 } from "@gnu-taler/taler-util";
 import {
   NodeHttpLib,
@@ -174,27 +174,37 @@ export async function runExchangeTimetravelTest(t: 
GlobalTestState) {
   const denomPubs1 = keys1.denoms.map((x) => {
     return {
       denomPub: x.denom_pub,
-      expireDeposit: stringifyTimestamp(x.stamp_expire_deposit),
+      expireDeposit: AbsoluteTime.stringify(
+        AbsoluteTime.fromTimestamp(x.stamp_expire_deposit),
+      ),
     };
   });
 
   const denomPubs2 = keys2.denoms.map((x) => {
     return {
       denomPub: x.denom_pub,
-      expireDeposit: stringifyTimestamp(x.stamp_expire_deposit),
+      expireDeposit: AbsoluteTime.stringify(
+        AbsoluteTime.fromTimestamp(x.stamp_expire_deposit),
+      ),
     };
   });
   const dps2 = new Set(denomPubs2.map((x) => x.denomPub));
 
   console.log("=== KEYS RESPONSE 1 ===");
 
-  console.log("list issue date", stringifyTimestamp(keys1.list_issue_date));
+  console.log(
+    "list issue date",
+    AbsoluteTime.stringify(AbsoluteTime.fromTimestamp(keys1.list_issue_date)),
+  );
   console.log("num denoms", keys1.denoms.length);
   console.log("denoms", JSON.stringify(denomPubs1, undefined, 2));
 
   console.log("=== KEYS RESPONSE 2 ===");
 
-  console.log("list issue date", stringifyTimestamp(keys2.list_issue_date));
+  console.log(
+    "list issue date",
+    AbsoluteTime.stringify(AbsoluteTime.fromTimestamp(keys2.list_issue_date)),
+  );
   console.log("num denoms", keys2.denoms.length);
   console.log("denoms", JSON.stringify(denomPubs2, undefined, 2));
 
@@ -214,8 +224,8 @@ export async function runExchangeTimetravelTest(t: 
GlobalTestState) {
         `denomination with public key ${da.denomPub} is not present in new 
/keys response`,
       );
       console.log(
-        `the new /keys response was issued ${stringifyTimestamp(
-          keys2.list_issue_date,
+        `the new /keys response was issued ${AbsoluteTime.stringify(
+          AbsoluteTime.fromTimestamp(keys2.list_issue_date),
         )}`,
       );
       console.log(
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
index 33aad80d..8f7d7771 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
@@ -18,9 +18,8 @@
  * Imports.
  */
 import {
+  AbsoluteTime,
   ContractTerms,
-  getTimestampNow,
-  timestampTruncateToSecond,
 } from "@gnu-taler/taler-util";
 import {
   WalletApiOperation,
@@ -277,7 +276,7 @@ export async function runLibeufinBasicTest(t: 
GlobalTestState) {
     summary: "Buy me!",
     amount: "EUR:5",
     fulfillment_url: "taler://fulfillment-success/thx",
-    wire_transfer_deadline: timestampTruncateToSecond(getTimestampNow()),
+    wire_transfer_deadline: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
   };
 
   await makeTestPayment(t, { wallet, merchant, order });
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts
 
b/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts
index ef926c4a..2bef87b2 100644
--- 
a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts
+++ 
b/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts
@@ -25,7 +25,7 @@ import {
   MerchantApiClient,
   MerchantService,
   setupDb,
-  getPayto
+  getPayto,
 } from "../harness/harness.js";
 
 /**
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts
index 230fc942..574177c8 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts
@@ -18,7 +18,10 @@
  * Imports.
  */
 import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from 
"../harness/helpers.js";
+import {
+  createSimpleTestkudosEnvironment,
+  withdrawViaBank,
+} from "../harness/helpers.js";
 import { durationFromSpec } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
@@ -28,12 +31,8 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 export async function runRefundAutoTest(t: GlobalTestState) {
   // Set up test environment
 
-  const {
-    wallet,
-    bank,
-    exchange,
-    merchant,
-  } = await createSimpleTestkudosEnvironment(t);
+  const { wallet, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironment(t);
 
   // Withdraw digital cash into the wallet.
 
@@ -46,7 +45,7 @@ export async function runRefundAutoTest(t: GlobalTestState) {
       amount: "TESTKUDOS:5",
       fulfillment_url: "taler://fulfillment-success/thx",
       auto_refund: {
-        d_ms: 3000,
+        d_us: 3000 * 1000,
       },
     },
     refund_delay: durationFromSpec({ minutes: 5 }),
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts
index c6442a24..24600384 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts
@@ -24,10 +24,8 @@ import {
   applyTimeTravel,
 } from "../harness/helpers.js";
 import {
+  AbsoluteTime,
   durationFromSpec,
-  timestampAddDuration,
-  getTimestampNow,
-  timestampTruncateToSecond,
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 
@@ -55,9 +53,9 @@ export async function runRefundGoneTest(t: GlobalTestState) {
       summary: "Buy me!",
       amount: "TESTKUDOS:5",
       fulfillment_url: "taler://fulfillment-success/thx",
-      pay_deadline: timestampTruncateToSecond(
-        timestampAddDuration(
-          getTimestampNow(),
+      pay_deadline: AbsoluteTime.toTimestamp(
+        AbsoluteTime.addDuration(
+          AbsoluteTime.now(),
           durationFromSpec({
             minutes: 10,
           }),
diff --git a/packages/taler-wallet-core/src/bank-api-client.ts 
b/packages/taler-wallet-core/src/bank-api-client.ts
index a61ea2ee..128e9a7a 100644
--- a/packages/taler-wallet-core/src/bank-api-client.ts
+++ b/packages/taler-wallet-core/src/bank-api-client.ts
@@ -25,6 +25,7 @@ import {
   AmountString,
   buildCodecForObject,
   Codec,
+  codecForAny,
   codecForString,
   encodeCrock,
   getRandomBytes,
@@ -102,15 +103,16 @@ export namespace BankApi {
     const resp = await bank.http.postJson(url.href, { username, password });
     let paytoUri = `payto://x-taler-bank/localhost/${username}`;
     if (resp.status !== 200 && resp.status !== 202) {
-      logger.error(`${j2s(await resp.json())}`)
+      logger.error(`${j2s(await resp.json())}`);
       throw new Error();
     }
+    const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny());
+    // LibEuFin demobank returns payto URI in response
+    if (respJson.paytoUri) {
+      paytoUri = respJson.paytoUri;
+    }
     try {
       const respJson = await resp.json();
-      // LibEuFin demobank returns payto URI in response
-      if (respJson.paytoUri) {
-        paytoUri = respJson.paytoUri;
-      }
     } catch (e) {}
     return {
       password,
diff --git a/packages/taler-wallet-core/src/common.ts 
b/packages/taler-wallet-core/src/common.ts
index 957ba1ca..d37bbe6e 100644
--- a/packages/taler-wallet-core/src/common.ts
+++ b/packages/taler-wallet-core/src/common.ts
@@ -34,7 +34,8 @@ import {
   BalancesResponse,
   AmountJson,
   DenominationPubKey,
-  Timestamp,
+  AbsoluteTime,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import { CryptoApi } from "./crypto/workers/cryptoApi.js";
 import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from 
"./db.js";
@@ -165,22 +166,22 @@ export interface DenomInfo {
   /**
    * Validity start date of the denomination.
    */
-  stampStart: Timestamp;
+  stampStart: TalerProtocolTimestamp;
 
   /**
    * Date after which the currency can't be withdrawn anymore.
    */
-  stampExpireWithdraw: Timestamp;
+  stampExpireWithdraw: TalerProtocolTimestamp;
 
   /**
    * Date after the denomination officially doesn't exist anymore.
    */
-  stampExpireLegal: Timestamp;
+  stampExpireLegal: TalerProtocolTimestamp;
 
   /**
    * Data after which coins of this denomination can't be deposited anymore.
    */
-  stampExpireDeposit: Timestamp;
+  stampExpireDeposit: TalerProtocolTimestamp;
 }
 
 export type NotificationListener = (n: WalletNotification) => void;
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index 82039734..d91aa08a 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -207,7 +207,7 @@ export class CryptoApi {
       }
     };
     ws.terminationTimerHandle = timer.after(15 * 1000, destroy);
-    ws.terminationTimerHandle.unref();
+    //ws.terminationTimerHandle.unref();
   }
 
   handleWorkerError(ws: WorkerState, e: any): void {
diff --git 
a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index b51d499d..b2706788 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -67,13 +67,11 @@ import {
   setupWithdrawPlanchet,
   stringToBytes,
   TalerSignaturePurpose,
-  Timestamp,
-  timestampTruncateToSecond,
-  typedArrayConcat,
+  AbsoluteTime,
   BlindedDenominationSignature,
-  RsaUnblindedSignature,
   UnblindedSignature,
   PlanchetUnblindInfo,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import bigint from "big-integer";
 import { DenominationRecord, WireFee } from "../../db.js";
@@ -110,18 +108,16 @@ function amountToBuffer(amount: AmountJson): Uint8Array {
   return u8buf;
 }
 
-function timestampRoundedToBuffer(ts: Timestamp): Uint8Array {
+function timestampRoundedToBuffer(ts: TalerProtocolTimestamp): Uint8Array {
   const b = new ArrayBuffer(8);
   const v = new DataView(b);
-  const tsRounded = timestampTruncateToSecond(ts);
+  // The buffer we sign over represents the timestamp in microseconds.
   if (typeof v.setBigUint64 !== "undefined") {
-    const s = BigInt(tsRounded.t_ms) * BigInt(1000);
+    const s = BigInt(ts.t_s) * BigInt(1000 * 1000);
     v.setBigUint64(0, s);
   } else {
     const s =
-      tsRounded.t_ms === "never"
-        ? bigint.zero
-        : bigint(tsRounded.t_ms).times(1000);
+      ts.t_s === "never" ? bigint.zero : bigint(ts.t_s).multiply(1000 * 1000);
     const arr = s.toArray(2 ** 8).value;
     let offset = 8 - arr.length;
     for (let i = 0; i < arr.length; i++) {
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 2e76ab52..e9fe6a47 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -36,9 +36,10 @@ import {
   Product,
   RefreshReason,
   TalerErrorDetails,
-  Timestamp,
   UnblindedSignature,
   CoinEnvelope,
+  TalerProtocolTimestamp,
+  TalerProtocolDuration,
 } from "@gnu-taler/taler-util";
 import { RetryInfo } from "./util/retries.js";
 import { PayCoinSelection } from "./util/coinSelection.js";
@@ -152,7 +153,7 @@ export interface ReserveRecord {
   /**
    * Time when the reserve was created.
    */
-  timestampCreated: Timestamp;
+  timestampCreated: TalerProtocolTimestamp;
 
   /**
    * Time when the information about this reserve was posted to the bank.
@@ -161,14 +162,14 @@ export interface ReserveRecord {
    *
    * Set to undefined if that hasn't happened yet.
    */
-  timestampReserveInfoPosted: Timestamp | undefined;
+  timestampReserveInfoPosted: TalerProtocolTimestamp | undefined;
 
   /**
    * Time when the reserve was confirmed by the bank.
    *
    * Set to undefined if not confirmed yet.
    */
-  timestampBankConfirmed: Timestamp | undefined;
+  timestampBankConfirmed: TalerProtocolTimestamp | undefined;
 
   /**
    * Wire information (as payto URI) for the bank account that
@@ -217,11 +218,6 @@ export interface ReserveRecord {
    */
   operationStatus: OperationStatus;
 
-  /**
-   * Time of the last successful status query.
-   */
-  lastSuccessfulStatusQuery: Timestamp | undefined;
-
   /**
    * Retry info, in case the reserve needs to be processed again
    * later, either due to an error or because the wallet needs to
@@ -350,22 +346,22 @@ export interface DenominationRecord {
   /**
    * Validity start date of the denomination.
    */
-  stampStart: Timestamp;
+  stampStart: TalerProtocolTimestamp;
 
   /**
    * Date after which the currency can't be withdrawn anymore.
    */
-  stampExpireWithdraw: Timestamp;
+  stampExpireWithdraw: TalerProtocolTimestamp;
 
   /**
    * Date after the denomination officially doesn't exist anymore.
    */
-  stampExpireLegal: Timestamp;
+  stampExpireLegal: TalerProtocolTimestamp;
 
   /**
    * Data after which coins of this denomination can't be deposited anymore.
    */
-  stampExpireDeposit: Timestamp;
+  stampExpireDeposit: TalerProtocolTimestamp;
 
   /**
    * Signature by the exchange's master key over the denomination
@@ -407,7 +403,7 @@ export interface DenominationRecord {
    * Latest list issue date of the "/keys" response
    * that includes this denomination.
    */
-  listIssueDate: Timestamp;
+  listIssueDate: TalerProtocolTimestamp;
 }
 
 /**
@@ -441,7 +437,7 @@ export interface ExchangeDetailsRecord {
    */
   protocolVersion: string;
 
-  reserveClosingDelay: Duration;
+  reserveClosingDelay: TalerProtocolDuration;
 
   /**
    * Signing keys we got from the exchange, can also contain
@@ -478,7 +474,7 @@ export interface ExchangeDetailsRecord {
    *
    * Used during backup merging.
    */
-  termsOfServiceAcceptedTimestamp: Timestamp | undefined;
+  termsOfServiceAcceptedTimestamp: TalerProtocolTimestamp | undefined;
 
   wireInfo: WireInfo;
 }
@@ -503,7 +499,7 @@ export interface ExchangeDetailsPointer {
    * Timestamp when the (masterPublicKey, currency) pointer
    * has been updated.
    */
-  updateClock: Timestamp;
+  updateClock: TalerProtocolTimestamp;
 }
 
 /**
@@ -528,14 +524,14 @@ export interface ExchangeRecord {
   /**
    * Last time when the exchange was updated.
    */
-  lastUpdate: Timestamp | undefined;
+  lastUpdate: TalerProtocolTimestamp | undefined;
 
   /**
    * Next scheduled update for the exchange.
    *
    * (This field must always be present, so we can index on the timestamp.)
    */
-  nextUpdate: Timestamp;
+  nextUpdate: TalerProtocolTimestamp;
 
   /**
    * Next time that we should check if coins need to be refreshed.
@@ -543,7 +539,7 @@ export interface ExchangeRecord {
    * Updated whenever the exchange's denominations are updated or when
    * the refresh check has been done.
    */
-  nextRefreshCheck: Timestamp;
+  nextRefreshCheck: TalerProtocolTimestamp;
 
   /**
    * Last error (if any) for fetching updated information about the
@@ -793,7 +789,7 @@ export interface ProposalRecord {
    * Timestamp (in ms) of when the record
    * was created.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 
   /**
    * Private key for the nonce.
@@ -837,7 +833,7 @@ export interface TipRecord {
    * Has the user accepted the tip?  Only after the tip has been accepted coins
    * withdrawn from the tip may be used.
    */
-  acceptedTimestamp: Timestamp | undefined;
+  acceptedTimestamp: TalerProtocolTimestamp | undefined;
 
   /**
    * The tipped amount.
@@ -849,7 +845,7 @@ export interface TipRecord {
   /**
    * Timestamp, the tip can't be picked up anymore after this deadline.
    */
-  tipExpiration: Timestamp;
+  tipExpiration: TalerProtocolTimestamp;
 
   /**
    * The exchange that will sign our coins, chosen by the merchant.
@@ -884,13 +880,13 @@ export interface TipRecord {
    */
   merchantTipId: string;
 
-  createdTimestamp: Timestamp;
+  createdTimestamp: TalerProtocolTimestamp;
 
   /**
    * Timestamp for when the wallet finished picking up the tip
    * from the merchant.
    */
-  pickedUpTimestamp: Timestamp | undefined;
+  pickedUpTimestamp: TalerProtocolTimestamp | undefined;
 
   /**
    * Retry info, even present when the operation isn't active to allow indexing
@@ -959,12 +955,12 @@ export interface RefreshGroupRecord {
    */
   statusPerCoin: RefreshCoinStatus[];
 
-  timestampCreated: Timestamp;
+  timestampCreated: TalerProtocolTimestamp;
 
   /**
    * Timestamp when the refresh session finished.
    */
-  timestampFinished: Timestamp | undefined;
+  timestampFinished: TalerProtocolTimestamp | undefined;
 
   /**
    * No coins are pending, but at least one is frozen.
@@ -1025,12 +1021,12 @@ export interface WireFee {
   /**
    * Start date of the fee.
    */
-  startStamp: Timestamp;
+  startStamp: TalerProtocolTimestamp;
 
   /**
    * End date of the fee.
    */
-  endStamp: Timestamp;
+  endStamp: TalerProtocolTimestamp;
 
   /**
    * Signature made by the exchange master key.
@@ -1054,12 +1050,12 @@ export type WalletRefundItem =
 
 export interface WalletRefundItemCommon {
   // Execution time as claimed by the merchant
-  executionTime: Timestamp;
+  executionTime: TalerProtocolTimestamp;
 
   /**
    * Time when the wallet became aware of the refund.
    */
-  obtainedTime: Timestamp;
+  obtainedTime: TalerProtocolTimestamp;
 
   refundAmount: AmountJson;
 
@@ -1141,14 +1137,14 @@ export interface WalletContractData {
   orderId: string;
   merchantBaseUrl: string;
   summary: string;
-  autoRefund: Duration | undefined;
+  autoRefund: TalerProtocolDuration | undefined;
   maxWireFee: AmountJson;
   wireFeeAmortization: number;
-  payDeadline: Timestamp;
-  refundDeadline: Timestamp;
+  payDeadline: TalerProtocolTimestamp;
+  refundDeadline: TalerProtocolTimestamp;
   allowedAuditors: AllowedAuditorInfo[];
   allowedExchanges: AllowedExchangeInfo[];
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
   wireMethod: string;
   wireInfoHash: string;
   maxDepositFee: AmountJson;
@@ -1215,8 +1211,10 @@ export interface PurchaseRecord {
   /**
    * Timestamp of the first time that sending a payment to the merchant
    * for this purchase was successful.
+   *
+   * FIXME: Does this need to be a timestamp, doensn't boolean suffice?
    */
-  timestampFirstSuccessfulPay: Timestamp | undefined;
+  timestampFirstSuccessfulPay: TalerProtocolTimestamp | undefined;
 
   merchantPaySig: string | undefined;
 
@@ -1224,7 +1222,7 @@ export interface PurchaseRecord {
    * When was the purchase made?
    * Refers to the time that the user accepted.
    */
-  timestampAccept: Timestamp;
+  timestampAccept: TalerProtocolTimestamp;
 
   /**
    * Pending refunds for the purchase.  A refund is pending
@@ -1236,7 +1234,7 @@ export interface PurchaseRecord {
    * When was the last refund made?
    * Set to 0 if no refund was made on the purchase.
    */
-  timestampLastRefundStatus: Timestamp | undefined;
+  timestampLastRefundStatus: TalerProtocolTimestamp | undefined;
 
   /**
    * Last session signature that we submitted to /pay (if any).
@@ -1273,7 +1271,7 @@ export interface PurchaseRecord {
   /**
    * Continue querying the refund status until this deadline has expired.
    */
-  autoRefundDeadline: Timestamp | undefined;
+  autoRefundDeadline: TalerProtocolTimestamp | undefined;
 
   /**
    * Is the payment frozen?  I.e. did we encounter
@@ -1308,12 +1306,12 @@ export interface WalletBackupConfState {
   /**
    * Timestamp stored in the last backup.
    */
-  lastBackupTimestamp?: Timestamp;
+  lastBackupTimestamp?: TalerProtocolTimestamp;
 
   /**
    * Last time we tried to do a backup.
    */
-  lastBackupCheckTimestamp?: Timestamp;
+  lastBackupCheckTimestamp?: TalerProtocolTimestamp;
   lastBackupNonce?: string;
 }
 
@@ -1362,14 +1360,14 @@ export interface WithdrawalGroupRecord {
    * When was the withdrawal operation started started?
    * Timestamp in milliseconds.
    */
-  timestampStart: Timestamp;
+  timestampStart: TalerProtocolTimestamp;
 
   /**
    * When was the withdrawal operation completed?
    *
    * FIXME: We should probably drop this and introduce an OperationStatus 
field.
    */
-  timestampFinish?: Timestamp;
+  timestampFinish?: TalerProtocolTimestamp;
 
   /**
    * Operation status of the withdrawal group.
@@ -1429,9 +1427,9 @@ export interface RecoupGroupRecord {
    */
   recoupGroupId: string;
 
-  timestampStarted: Timestamp;
+  timestampStarted: TalerProtocolTimestamp;
 
-  timestampFinished: Timestamp | undefined;
+  timestampFinished: TalerProtocolTimestamp | undefined;
 
   /**
    * Public keys that identify the coins being recouped
@@ -1482,7 +1480,7 @@ export type BackupProviderState =
     }
   | {
       tag: BackupProviderStateTag.Ready;
-      nextBackupTimestamp: Timestamp;
+      nextBackupTimestamp: TalerProtocolTimestamp;
     }
   | {
       tag: BackupProviderStateTag.Retrying;
@@ -1529,7 +1527,7 @@ export interface BackupProviderRecord {
    * Does NOT correspond to the timestamp of the backup,
    * which only changes when the backup content changes.
    */
-  lastBackupCycleTimestamp?: Timestamp;
+  lastBackupCycleTimestamp?: TalerProtocolTimestamp;
 
   /**
    * Proposal that we're currently trying to pay for.
@@ -1594,9 +1592,9 @@ export interface DepositGroupRecord {
 
   depositedPerCoin: boolean[];
 
-  timestampCreated: Timestamp;
+  timestampCreated: TalerProtocolTimestamp;
 
-  timestampFinished: Timestamp | undefined;
+  timestampFinished: TalerProtocolTimestamp | undefined;
 
   operationStatus: OperationStatus;
 
@@ -1618,14 +1616,14 @@ export interface GhostDepositGroupRecord {
    * When multiple deposits for the same contract terms hash
    * have a different timestamp, we choose the earliest one.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 
   contractTermsHash: string;
 
   deposits: {
     coinPub: string;
     amount: AmountString;
-    timestamp: Timestamp;
+    timestamp: TalerProtocolTimestamp;
     depositFee: AmountString;
     merchantPub: string;
     coinSig: string;
diff --git a/packages/taler-wallet-core/src/dbless.ts 
b/packages/taler-wallet-core/src/dbless.ts
index 169b6ae0..854a3ea0 100644
--- a/packages/taler-wallet-core/src/dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -44,7 +44,7 @@ import {
   hashWire,
   Logger,
   parsePaytoUri,
-  Timestamp,
+  AbsoluteTime,
   UnblindedSignature,
 } from "@gnu-taler/taler-util";
 import { DenominationRecord } from "./db.js";
@@ -222,10 +222,11 @@ export async function depositCoin(args: {
   const depositPayto =
     args.depositPayto ?? "payto://x-taler-bank/localhost/foo";
   const wireSalt = encodeCrock(getRandomBytes(16));
+  const timestampNow = AbsoluteTime.toTimestamp(AbsoluteTime.now());
   const contractTermsHash = encodeCrock(getRandomBytes(64));
-  const depositTimestamp = Timestamp.truncateToSecond(Timestamp.now());
-  const refundDeadline = Timestamp.truncateToSecond(Timestamp.now());
-  const wireTransferDeadline = Timestamp.truncateToSecond(Timestamp.now());
+  const depositTimestamp = timestampNow;
+  const refundDeadline = timestampNow;
+  const wireTransferDeadline = timestampNow;
   const merchantPub = encodeCrock(getRandomBytes(32));
   const dp = await cryptoApi.signDepositPermission({
     coinPriv: coin.coinPriv,
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts 
b/packages/taler-wallet-core/src/operations/backup/export.ts
index 12b30941..35306da6 100644
--- a/packages/taler-wallet-core/src/operations/backup/export.ts
+++ b/packages/taler-wallet-core/src/operations/backup/export.ts
@@ -49,14 +49,13 @@ import {
   BackupWithdrawalGroup,
   canonicalizeBaseUrl,
   canonicalJson,
-  getTimestampNow,
   Logger,
-  timestampToIsoString,
   WalletBackupContentV1,
   hash,
   encodeCrock,
   getRandomBytes,
   stringToBytes,
+  AbsoluteTime,
 } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../../common.js";
 import {
@@ -455,7 +454,7 @@ export async function exportBackup(
         });
       });
 
-      const ts = getTimestampNow();
+      const ts = AbsoluteTime.toTimestamp(AbsoluteTime.now());
 
       if (!bs.lastBackupTimestamp) {
         bs.lastBackupTimestamp = ts;
@@ -496,9 +495,9 @@ export async function exportBackup(
         );
         bs.lastBackupNonce = encodeCrock(getRandomBytes(32));
         logger.trace(
-          `setting timestamp to ${timestampToIsoString(ts)} and nonce to ${
-            bs.lastBackupNonce
-          }`,
+          `setting timestamp to ${AbsoluteTime.toIsoString(
+            AbsoluteTime.fromTimestamp(ts),
+          )} and nonce to ${bs.lastBackupNonce}`,
         );
         await tx.config.put({
           key: WALLET_BACKUP_STATE_KEY,
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts 
b/packages/taler-wallet-core/src/operations/backup/import.ts
index 35b62c2e..4b17a5f3 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -20,7 +20,6 @@ import {
   Amounts,
   BackupDenomSel,
   WalletBackupContentV1,
-  getTimestampNow,
   BackupCoinSourceType,
   BackupProposalStatus,
   codecForContractTerms,
@@ -28,6 +27,8 @@ import {
   RefreshReason,
   BackupRefreshReason,
   DenomKeyType,
+  AbsoluteTime,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import {
   WalletContractData,
@@ -277,8 +278,8 @@ export async function importBackup(
           permanent: true,
           retryInfo: initRetryInfo(),
           lastUpdate: undefined,
-          nextUpdate: getTimestampNow(),
-          nextRefreshCheck: getTimestampNow(),
+          nextUpdate: TalerProtocolTimestamp.now(),
+          nextRefreshCheck: TalerProtocolTimestamp.now(),
         });
       }
 
@@ -465,7 +466,6 @@ export async function importBackup(
               senderWire: backupReserve.sender_wire,
               retryInfo: initRetryInfo(),
               lastError: undefined,
-              lastSuccessfulStatusQuery: { t_ms: "never" },
               initialWithdrawalGroupId:
                 backupReserve.initial_withdrawal_group_id,
               initialWithdrawalStarted:
@@ -752,7 +752,7 @@ export async function importBackup(
             noncePub:
               cryptoComp.proposalNoncePrivToPub[backupPurchase.nonce_priv],
             lastPayError: undefined,
-            autoRefundDeadline: { t_ms: "never" },
+            autoRefundDeadline: TalerProtocolTimestamp.never(),
             refundStatusRetryInfo: initRetryInfo(),
             lastRefundStatusError: undefined,
             timestampAccept: backupPurchase.timestamp_accept,
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts 
b/packages/taler-wallet-core/src/operations/backup/index.ts
index 2a1a774f..48eea56a 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -40,21 +40,19 @@ import {
   ConfirmPayResultType,
   DenomKeyType,
   durationFromSpec,
-  getTimestampNow,
   hashDenomPub,
   HttpStatusCode,
   j2s,
-  LibtoolVersion,
   Logger,
   notEmpty,
   PreparePayResultType,
   RecoveryLoadRequest,
   RecoveryMergeStrategy,
   TalerErrorDetails,
-  Timestamp,
-  timestampAddDuration,
+  AbsoluteTime,
   URL,
   WalletBackupContentV1,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import { gunzipSync, gzipSync } from "fflate";
 import { InternalWalletState } from "../../common.js";
@@ -250,11 +248,13 @@ interface BackupForProviderArgs {
   retryAfterPayment: boolean;
 }
 
-function getNextBackupTimestamp(): Timestamp {
+function getNextBackupTimestamp(): TalerProtocolTimestamp {
   // FIXME:  Randomize!
-  return timestampAddDuration(
-    getTimestampNow(),
-    durationFromSpec({ minutes: 5 }),
+  return AbsoluteTime.toTimestamp(
+    AbsoluteTime.addDuration(
+      AbsoluteTime.now(),
+      durationFromSpec({ minutes: 5 }),
+    ),
   );
 }
 
@@ -324,7 +324,7 @@ async function runBackupCycleForProvider(
         if (!prov) {
           return;
         }
-        prov.lastBackupCycleTimestamp = getTimestampNow();
+        prov.lastBackupCycleTimestamp = TalerProtocolTimestamp.now();
         prov.state = {
           tag: BackupProviderStateTag.Ready,
           nextBackupTimestamp: getNextBackupTimestamp(),
@@ -404,7 +404,7 @@ async function runBackupCycleForProvider(
           return;
         }
         prov.lastBackupHash = encodeCrock(currentBackupHash);
-        prov.lastBackupCycleTimestamp = getTimestampNow();
+        prov.lastBackupCycleTimestamp = TalerProtocolTimestamp.now();
         prov.state = {
           tag: BackupProviderStateTag.Ready,
           nextBackupTimestamp: getNextBackupTimestamp(),
@@ -641,7 +641,7 @@ export async function addBackupProvider(
         if (req.activate) {
           oldProv.state = {
             tag: BackupProviderStateTag.Ready,
-            nextBackupTimestamp: getTimestampNow(),
+            nextBackupTimestamp: TalerProtocolTimestamp.now(),
           };
           logger.info("setting existing backup provider to active");
           await tx.backupProviders.put(oldProv);
@@ -662,7 +662,7 @@ export async function addBackupProvider(
       if (req.activate) {
         state = {
           tag: BackupProviderStateTag.Ready,
-          nextBackupTimestamp: getTimestampNow(),
+          nextBackupTimestamp: TalerProtocolTimestamp.now(),
         };
       } else {
         state = {
@@ -701,8 +701,8 @@ export interface ProviderInfo {
    * Last communication issue with the provider.
    */
   lastError?: TalerErrorDetails;
-  lastSuccessfulBackupTimestamp?: Timestamp;
-  lastAttemptedBackupTimestamp?: Timestamp;
+  lastSuccessfulBackupTimestamp?: TalerProtocolTimestamp;
+  lastAttemptedBackupTimestamp?: TalerProtocolTimestamp;
   paymentProposalIds: string[];
   backupProblem?: BackupProblem;
   paymentStatus: ProviderPaymentStatus;
@@ -724,7 +724,7 @@ export interface BackupConflictingDeviceProblem {
   type: "backup-conflicting-device";
   otherDeviceId: string;
   myDeviceId: string;
-  backupTimestamp: Timestamp;
+  backupTimestamp: AbsoluteTime;
 }
 
 export type ProviderPaymentStatus =
@@ -774,12 +774,12 @@ export interface ProviderPaymentPending {
 
 export interface ProviderPaymentPaid {
   type: ProviderPaymentType.Paid;
-  paidUntil: Timestamp;
+  paidUntil: AbsoluteTime;
 }
 
 export interface ProviderPaymentTermsChanged {
   type: ProviderPaymentType.TermsChanged;
-  paidUntil: Timestamp;
+  paidUntil: AbsoluteTime;
   oldTerms: BackupProviderTerms;
   newTerms: BackupProviderTerms;
 }
@@ -811,8 +811,8 @@ async function getProviderPaymentInfo(
     if (status.paid) {
       return {
         type: ProviderPaymentType.Paid,
-        paidUntil: timestampAddDuration(
-          status.contractTerms.timestamp,
+        paidUntil: AbsoluteTime.addDuration(
+          AbsoluteTime.fromTimestamp(status.contractTerms.timestamp),
           durationFromSpec({ years: 1 }),
         ),
       };
@@ -915,7 +915,7 @@ async function backupRecoveryTheirs(
             paymentProposalIds: [],
             state: {
               tag: BackupProviderStateTag.Ready,
-              nextBackupTimestamp: getTimestampNow(),
+              nextBackupTimestamp: TalerProtocolTimestamp.now(),
             },
             uids: [encodeCrock(getRandomBytes(32))],
           });
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index a5d6c93c..4b976011 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -15,6 +15,7 @@
  */
 
 import {
+  AbsoluteTime,
   AmountJson,
   Amounts,
   buildCodecForObject,
@@ -27,21 +28,16 @@ import {
   ContractTerms,
   CreateDepositGroupRequest,
   CreateDepositGroupResponse,
-  DenomKeyType,
   durationFromSpec,
   encodeCrock,
   GetFeeForDepositRequest,
   getRandomBytes,
-  getTimestampNow,
   hashWire,
   Logger,
   NotificationType,
   parsePaytoUri,
   TalerErrorDetails,
-  Timestamp,
-  timestampAddDuration,
-  timestampIsBetween,
-  timestampTruncateToSecond,
+  TalerProtocolTimestamp,
   TrackDepositGroupRequest,
   TrackDepositGroupResponse,
   URL,
@@ -212,7 +208,7 @@ async function processDepositGroupImpl(
         }
       }
       if (allDeposited) {
-        dg.timestampFinished = getTimestampNow();
+        dg.timestampFinished = TalerProtocolTimestamp.now();
         dg.operationStatus = OperationStatus.Finished;
         delete dg.lastError;
         delete dg.retryInfo;
@@ -310,13 +306,8 @@ export async function getFeeForDeposit(
       }
     });
 
-  const timestamp = getTimestampNow();
-  const timestampRound = timestampTruncateToSecond(timestamp);
-  // const noncePair = await ws.cryptoApi.createEddsaKeypair();
-  // const merchantPair = await ws.cryptoApi.createEddsaKeypair();
-  // const wireSalt = encodeCrock(getRandomBytes(16));
-  // const wireHash = hashWire(req.depositPaytoUri, wireSalt);
-  // const wireHashLegacy = hashWireLegacy(req.depositPaytoUri, wireSalt);
+  const timestamp = AbsoluteTime.now();
+  const timestampRound = AbsoluteTime.toTimestamp(timestamp);
   const contractTerms: ContractTerms = {
     auditors: [],
     exchanges: exchangeInfos,
@@ -331,15 +322,14 @@ export async function getFeeForDeposit(
     wire_transfer_deadline: timestampRound,
     order_id: "",
     h_wire: "",
-    pay_deadline: timestampAddDuration(
-      timestampRound,
-      durationFromSpec({ hours: 1 }),
+    pay_deadline: AbsoluteTime.toTimestamp(
+      AbsoluteTime.addDuration(timestamp, durationFromSpec({ hours: 1 })),
     ),
     merchant: {
       name: "",
     },
     merchant_pub: "",
-    refund_deadline: { t_ms: 0 },
+    refund_deadline: TalerProtocolTimestamp.zero(),
   };
 
   const contractData = extractContractData(contractTerms, "", "");
@@ -399,8 +389,8 @@ export async function createDepositGroup(
       }
     });
 
-  const timestamp = getTimestampNow();
-  const timestampRound = timestampTruncateToSecond(timestamp);
+  const now = AbsoluteTime.now();
+  const nowRounded = AbsoluteTime.toTimestamp(now);
   const noncePair = await ws.cryptoApi.createEddsaKeypair();
   const merchantPair = await ws.cryptoApi.createEddsaKeypair();
   const wireSalt = encodeCrock(getRandomBytes(16));
@@ -412,24 +402,23 @@ export async function createDepositGroup(
     max_fee: Amounts.stringify(amount),
     max_wire_fee: Amounts.stringify(amount),
     wire_method: p.targetType,
-    timestamp: timestampRound,
+    timestamp: nowRounded,
     merchant_base_url: "",
     summary: "",
     nonce: noncePair.pub,
-    wire_transfer_deadline: timestampRound,
+    wire_transfer_deadline: nowRounded,
     order_id: "",
     // This is always the v2 wire hash, as we're the "merchant" and support v2.
     h_wire: wireHash,
     // Required for older exchanges.
-    pay_deadline: timestampAddDuration(
-      timestampRound,
-      durationFromSpec({ hours: 1 }),
+    pay_deadline: AbsoluteTime.toTimestamp(
+      AbsoluteTime.addDuration(now, durationFromSpec({ hours: 1 })),
     ),
     merchant: {
       name: "",
     },
     merchant_pub: merchantPair.pub,
-    refund_deadline: { t_ms: 0 },
+    refund_deadline: TalerProtocolTimestamp.zero(),
   };
 
   const contractTermsHash = await ws.cryptoApi.hashString(
@@ -482,7 +471,7 @@ export async function createDepositGroup(
     depositGroupId,
     noncePriv: noncePair.priv,
     noncePub: noncePair.pub,
-    timestampCreated: timestamp,
+    timestampCreated: AbsoluteTime.toTimestamp(now),
     timestampFinished: undefined,
     payCoinSelection: payCoinSel,
     payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
@@ -570,10 +559,10 @@ export async function getEffectiveDepositAmount(
         // about "find method not found on undefined" when the wireType
         // is not supported by the Exchange.
         const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => 
{
-          return timestampIsBetween(
-            getTimestampNow(),
-            x.startStamp,
-            x.endStamp,
+          return AbsoluteTime.isBetween(
+            AbsoluteTime.now(),
+            AbsoluteTime.fromTimestamp(x.startStamp),
+            AbsoluteTime.fromTimestamp(x.endStamp),
           );
         })?.wireFee;
         if (fee) {
@@ -656,10 +645,10 @@ export async function getTotalFeeForDepositAmount(
         // about "find method not found on undefined" when the wireType
         // is not supported by the Exchange.
         const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => 
{
-          return timestampIsBetween(
-            getTimestampNow(),
-            x.startStamp,
-            x.endStamp,
+          return AbsoluteTime.isBetween(
+            AbsoluteTime.now(),
+            AbsoluteTime.fromTimestamp(x.startStamp),
+            AbsoluteTime.fromTimestamp(x.endStamp),
           );
         })?.wireFee;
         if (fee) {
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 536d4e3b..df7eee76 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -28,8 +28,6 @@ import {
   durationFromSpec,
   ExchangeSignKeyJson,
   ExchangeWireJson,
-  getTimestampNow,
-  isTimestampExpired,
   Logger,
   NotificationType,
   parsePaytoUri,
@@ -37,13 +35,15 @@ import {
   TalerErrorCode,
   URL,
   TalerErrorDetails,
-  Timestamp,
+  AbsoluteTime,
   hashDenomPub,
   LibtoolVersion,
   codecForAny,
   DenominationPubKey,
   DenomKeyType,
   ExchangeKeysJson,
+  TalerProtocolTimestamp,
+  TalerProtocolDuration,
 } from "@gnu-taler/taler-util";
 import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util";
 import { CryptoApi } from "../crypto/workers/cryptoApi.js";
@@ -57,7 +57,7 @@ import {
   WireInfo,
 } from "../db.js";
 import {
-  getExpiryTimestamp,
+  getExpiry,
   HttpRequestLibrary,
   readSuccessResponseJsonOrThrow,
   readSuccessResponseTextOrThrow,
@@ -80,7 +80,7 @@ const logger = new Logger("exchanges.ts");
 function denominationRecordFromKeys(
   exchangeBaseUrl: string,
   exchangeMasterPub: string,
-  listIssueDate: Timestamp,
+  listIssueDate: TalerProtocolTimestamp,
   denomIn: ExchangeDenomination,
 ): DenominationRecord {
   let denomPub: DenominationPubKey;
@@ -132,7 +132,9 @@ async function handleExchangeUpdateError(
 }
 
 export function getExchangeRequestTimeout(): Duration {
-  return { d_ms: 5000 };
+  return Duration.fromSpec({
+    seconds: 5,
+  });
 }
 
 export interface ExchangeTosDownloadResult {
@@ -362,12 +364,11 @@ export async function updateExchangeFromUrl(
 async function provideExchangeRecord(
   ws: InternalWalletState,
   baseUrl: string,
-  now: Timestamp,
+  now: AbsoluteTime,
 ): Promise<{
   exchange: ExchangeRecord;
   exchangeDetails: ExchangeDetailsRecord | undefined;
 }> {
-
   return await ws.db
     .mktx((x) => ({
       exchanges: x.exchanges,
@@ -376,14 +377,14 @@ async function provideExchangeRecord(
     .runReadWrite(async (tx) => {
       let exchange = await tx.exchanges.get(baseUrl);
       if (!exchange) {
-        const r = {
+        const r: ExchangeRecord = {
           permanent: true,
           baseUrl: baseUrl,
           retryInfo: initRetryInfo(),
           detailsPointer: undefined,
           lastUpdate: undefined,
-          nextUpdate: now,
-          nextRefreshCheck: now,
+          nextUpdate: AbsoluteTime.toTimestamp(now),
+          nextRefreshCheck: AbsoluteTime.toTimestamp(now),
         };
         await tx.exchanges.put(r);
         exchange = r;
@@ -400,10 +401,10 @@ interface ExchangeKeysDownloadResult {
   currentDenominations: DenominationRecord[];
   protocolVersion: string;
   signingKeys: ExchangeSignKeyJson[];
-  reserveClosingDelay: Duration;
-  expiry: Timestamp;
+  reserveClosingDelay: TalerProtocolDuration;
+  expiry: TalerProtocolTimestamp;
   recoup: Recoup[];
-  listIssueDate: Timestamp;
+  listIssueDate: TalerProtocolTimestamp;
 }
 
 /**
@@ -475,9 +476,11 @@ async function downloadExchangeKeysInfo(
     protocolVersion: exchangeKeysJsonUnchecked.version,
     signingKeys: exchangeKeysJsonUnchecked.signkeys,
     reserveClosingDelay: exchangeKeysJsonUnchecked.reserve_closing_delay,
-    expiry: getExpiryTimestamp(resp, {
-      minDuration: durationFromSpec({ hours: 1 }),
-    }),
+    expiry: AbsoluteTime.toTimestamp(
+      getExpiry(resp, {
+        minDuration: durationFromSpec({ hours: 1 }),
+      }),
+    ),
     recoup: exchangeKeysJsonUnchecked.recoup ?? [],
     listIssueDate: exchangeKeysJsonUnchecked.list_issue_date,
   };
@@ -529,12 +532,20 @@ async function updateExchangeFromUrlImpl(
   exchangeDetails: ExchangeDetailsRecord;
 }> {
   logger.info(`updating exchange info for ${baseUrl}, forced: ${forceNow}`);
-  const now = getTimestampNow();
+  const now = AbsoluteTime.now();
   baseUrl = canonicalizeBaseUrl(baseUrl);
 
-  const { exchange, exchangeDetails } = await provideExchangeRecord(ws, 
baseUrl, now);
+  const { exchange, exchangeDetails } = await provideExchangeRecord(
+    ws,
+    baseUrl,
+    now,
+  );
 
-  if (!forceNow && exchangeDetails !== undefined && 
!isTimestampExpired(exchange.nextUpdate)) {
+  if (
+    !forceNow &&
+    exchangeDetails !== undefined &&
+    !AbsoluteTime.isExpired(AbsoluteTime.fromTimestamp(exchange.nextUpdate))
+  ) {
     logger.info("using existing exchange info");
     return { exchange, exchangeDetails };
   }
@@ -575,7 +586,8 @@ async function updateExchangeFromUrlImpl(
     timeout,
     acceptedFormat,
   );
-  const tosHasBeenAccepted = exchangeDetails?.termsOfServiceAcceptedEtag === 
tosDownload.tosEtag
+  const tosHasBeenAccepted =
+    exchangeDetails?.termsOfServiceAcceptedEtag === tosDownload.tosEtag;
 
   let recoupGroupId: string | undefined;
 
@@ -611,23 +623,25 @@ async function updateExchangeFromUrlImpl(
         exchangeBaseUrl: r.baseUrl,
         wireInfo,
         termsOfServiceText: tosDownload.tosText,
-        termsOfServiceAcceptedEtag: tosHasBeenAccepted ? tosDownload.tosEtag : 
undefined,
+        termsOfServiceAcceptedEtag: tosHasBeenAccepted
+          ? tosDownload.tosEtag
+          : undefined,
         termsOfServiceContentType: tosDownload.tosContentType,
         termsOfServiceLastEtag: tosDownload.tosEtag,
-        termsOfServiceAcceptedTimestamp: getTimestampNow(),
+        termsOfServiceAcceptedTimestamp: TalerProtocolTimestamp.now(),
       };
       // FIXME: only update if pointer got updated
       r.lastError = undefined;
       r.retryInfo = initRetryInfo();
-      r.lastUpdate = getTimestampNow();
+      r.lastUpdate = TalerProtocolTimestamp.now();
       r.nextUpdate = keysInfo.expiry;
       // New denominations might be available.
-      r.nextRefreshCheck = getTimestampNow();
+      r.nextRefreshCheck = TalerProtocolTimestamp.now();
       r.detailsPointer = {
         currency: details.currency,
         masterPublicKey: details.masterPublicKey,
         // FIXME: only change if pointer really changed
-        updateClock: getTimestampNow(),
+        updateClock: TalerProtocolTimestamp.now(),
         protocolVersionRange: keysInfo.protocolVersion,
       };
       await tx.exchanges.put(r);
diff --git a/packages/taler-wallet-core/src/operations/pay.ts 
b/packages/taler-wallet-core/src/operations/pay.ts
index 9844dc52..9521d544 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -27,7 +27,6 @@
 import {
   AmountJson,
   Amounts,
-  CheckPaymentResponse,
   codecForContractTerms,
   codecForMerchantPayResponse,
   codecForProposal,
@@ -41,11 +40,8 @@ import {
   durationMin,
   durationMul,
   encodeCrock,
-  getDurationRemaining,
   getRandomBytes,
-  getTimestampNow,
   HttpStatusCode,
-  isTimestampExpired,
   j2s,
   kdf,
   Logger,
@@ -57,9 +53,9 @@ import {
   stringToBytes,
   TalerErrorCode,
   TalerErrorDetails,
-  Timestamp,
-  timestampAddDuration,
+  AbsoluteTime,
   URL,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import { EXCHANGE_COINS_LOCK, InternalWalletState } from "../common.js";
 import {
@@ -172,7 +168,9 @@ function isSpendableCoin(coin: CoinRecord, denom: 
DenominationRecord): boolean {
   if (coin.status !== CoinStatus.Fresh) {
     return false;
   }
-  if (isTimestampExpired(denom.stampExpireDeposit)) {
+  if (
+    
AbsoluteTime.isExpired(AbsoluteTime.fromTimestamp(denom.stampExpireDeposit))
+  ) {
     return false;
   }
   return true;
@@ -187,7 +185,7 @@ export interface CoinSelectionRequest {
   /**
    * Timestamp of the contract.
    */
-  timestamp: Timestamp;
+  timestamp: TalerProtocolTimestamp;
 
   wireMethod: string;
 
@@ -422,7 +420,7 @@ async function recordConfirmPay(
     payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
     totalPayCost: payCostInfo,
     coinDepositPermissions,
-    timestampAccept: getTimestampNow(),
+    timestampAccept: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
     timestampLastRefundStatus: undefined,
     proposalId: proposal.proposalId,
     lastPayError: undefined,
@@ -784,7 +782,7 @@ async function processDownloadProposalImpl(
   } catch (e) {
     const err = makeErrorDetails(
       TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED,
-      "schema validation failed",
+      `schema validation failed: ${e}`,
       {},
     );
     await failProposalPermanently(ws, proposalId, err);
@@ -921,7 +919,7 @@ async function startDownloadProposal(
     noncePriv: priv,
     noncePub: pub,
     claimToken,
-    timestamp: getTimestampNow(),
+    timestamp: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
     merchantBaseUrl,
     orderId,
     proposalId: proposalId,
@@ -956,7 +954,7 @@ async function storeFirstPaySuccess(
   sessionId: string | undefined,
   paySig: string,
 ): Promise<void> {
-  const now = getTimestampNow();
+  const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
   await ws.db
     .mktx((x) => ({ purchases: x.purchases }))
     .runReadWrite(async (tx) => {
@@ -978,13 +976,16 @@ async function storeFirstPaySuccess(
       purchase.payRetryInfo = initRetryInfo();
       purchase.merchantPaySig = paySig;
       if (isFirst) {
-        const ar = purchase.download.contractData.autoRefund;
-        if (ar) {
+        const protoAr = purchase.download.contractData.autoRefund;
+        if (protoAr) {
+          const ar = Duration.fromTalerProtocolDuration(protoAr);
           logger.info("auto_refund present");
           purchase.refundQueryRequested = true;
           purchase.refundStatusRetryInfo = initRetryInfo();
           purchase.lastRefundStatusError = undefined;
-          purchase.autoRefundDeadline = timestampAddDuration(now, ar);
+          purchase.autoRefundDeadline = AbsoluteTime.toTimestamp(
+            AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
+          );
         }
       }
       await tx.purchases.put(purchase);
@@ -1150,7 +1151,7 @@ async function unblockBackup(
           if (bp.state.tag === BackupProviderStateTag.Retrying) {
             bp.state = {
               tag: BackupProviderStateTag.Ready,
-              nextBackupTimestamp: getTimestampNow(),
+              nextBackupTimestamp: TalerProtocolTimestamp.now(),
             };
           }
         });
diff --git a/packages/taler-wallet-core/src/operations/pending.ts 
b/packages/taler-wallet-core/src/operations/pending.ts
index 6d686fb3..fc76eeb1 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -35,7 +35,7 @@ import {
   PendingTaskType,
   ReserveType,
 } from "../pending-types.js";
-import { getTimestampNow, Timestamp } from "@gnu-taler/taler-util";
+import { AbsoluteTime } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../common.js";
 import { GetReadOnlyAccess } from "../util/query.js";
 
@@ -44,21 +44,25 @@ async function gatherExchangePending(
     exchanges: typeof WalletStoresV1.exchanges;
     exchangeDetails: typeof WalletStoresV1.exchangeDetails;
   }>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   await tx.exchanges.iter().forEachAsync(async (e) => {
     resp.pendingOperations.push({
       type: PendingTaskType.ExchangeUpdate,
       givesLifeness: false,
-      timestampDue: e.lastError ? e.retryInfo.nextRetry : e.nextUpdate,
+      timestampDue: e.lastError
+        ? e.retryInfo.nextRetry
+        : AbsoluteTime.fromTimestamp(e.nextUpdate),
       exchangeBaseUrl: e.baseUrl,
       lastError: e.lastError,
     });
 
     resp.pendingOperations.push({
       type: PendingTaskType.ExchangeCheckRefresh,
-      timestampDue: e.lastError ? e.retryInfo.nextRetry : e.nextRefreshCheck,
+      timestampDue: e.lastError
+        ? e.retryInfo.nextRetry
+        : AbsoluteTime.fromTimestamp(e.nextRefreshCheck),
       givesLifeness: false,
       exchangeBaseUrl: e.baseUrl,
     });
@@ -67,7 +71,7 @@ async function gatherExchangePending(
 
 async function gatherReservePending(
   tx: GetReadOnlyAccess<{ reserves: typeof WalletStoresV1.reserves }>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   const reserves = await tx.reserves.indexes.byStatus.getAll(
@@ -87,7 +91,7 @@ async function gatherReservePending(
         resp.pendingOperations.push({
           type: PendingTaskType.Reserve,
           givesLifeness: true,
-          timestampDue: reserve.retryInfo?.nextRetry ?? Timestamp.now(),
+          timestampDue: reserve.retryInfo?.nextRetry ?? AbsoluteTime.now(),
           stage: reserve.reserveStatus,
           timestampCreated: reserve.timestampCreated,
           reserveType,
@@ -105,7 +109,7 @@ async function gatherReservePending(
 
 async function gatherRefreshPending(
   tx: GetReadOnlyAccess<{ refreshGroups: typeof WalletStoresV1.refreshGroups 
}>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   const refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll(
@@ -136,7 +140,7 @@ async function gatherWithdrawalPending(
     withdrawalGroups: typeof WalletStoresV1.withdrawalGroups;
     planchets: typeof WalletStoresV1.planchets;
   }>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   const wsrs = await tx.withdrawalGroups.indexes.byStatus.getAll(
@@ -169,14 +173,14 @@ async function gatherWithdrawalPending(
 
 async function gatherProposalPending(
   tx: GetReadOnlyAccess<{ proposals: typeof WalletStoresV1.proposals }>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   await tx.proposals.iter().forEach((proposal) => {
     if (proposal.proposalStatus == ProposalStatus.Proposed) {
       // Nothing to do, user needs to choose.
     } else if (proposal.proposalStatus == ProposalStatus.Downloading) {
-      const timestampDue = proposal.retryInfo?.nextRetry ?? getTimestampNow();
+      const timestampDue = proposal.retryInfo?.nextRetry ?? AbsoluteTime.now();
       resp.pendingOperations.push({
         type: PendingTaskType.ProposalDownload,
         givesLifeness: true,
@@ -194,7 +198,7 @@ async function gatherProposalPending(
 
 async function gatherDepositPending(
   tx: GetReadOnlyAccess<{ depositGroups: typeof WalletStoresV1.depositGroups 
}>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   const dgs = await tx.depositGroups.indexes.byStatus.getAll(
@@ -204,7 +208,7 @@ async function gatherDepositPending(
     if (dg.timestampFinished) {
       return;
     }
-    const timestampDue = dg.retryInfo?.nextRetry ?? getTimestampNow();
+    const timestampDue = dg.retryInfo?.nextRetry ?? AbsoluteTime.now();
     resp.pendingOperations.push({
       type: PendingTaskType.Deposit,
       givesLifeness: true,
@@ -218,7 +222,7 @@ async function gatherDepositPending(
 
 async function gatherTipPending(
   tx: GetReadOnlyAccess<{ tips: typeof WalletStoresV1.tips }>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   await tx.tips.iter().forEach((tip) => {
@@ -240,7 +244,7 @@ async function gatherTipPending(
 
 async function gatherPurchasePending(
   tx: GetReadOnlyAccess<{ purchases: typeof WalletStoresV1.purchases }>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   await tx.purchases.iter().forEach((pr) => {
@@ -249,7 +253,7 @@ async function gatherPurchasePending(
       pr.abortStatus === AbortStatus.None &&
       !pr.payFrozen
     ) {
-      const timestampDue = pr.payRetryInfo?.nextRetry ?? getTimestampNow();
+      const timestampDue = pr.payRetryInfo?.nextRetry ?? AbsoluteTime.now();
       resp.pendingOperations.push({
         type: PendingTaskType.Pay,
         givesLifeness: true,
@@ -275,7 +279,7 @@ async function gatherPurchasePending(
 
 async function gatherRecoupPending(
   tx: GetReadOnlyAccess<{ recoupGroups: typeof WalletStoresV1.recoupGroups }>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   await tx.recoupGroups.iter().forEach((rg) => {
@@ -297,7 +301,7 @@ async function gatherBackupPending(
   tx: GetReadOnlyAccess<{
     backupProviders: typeof WalletStoresV1.backupProviders;
   }>,
-  now: Timestamp,
+  now: AbsoluteTime,
   resp: PendingOperationsResponse,
 ): Promise<void> {
   await tx.backupProviders.iter().forEach((bp) => {
@@ -305,7 +309,7 @@ async function gatherBackupPending(
       resp.pendingOperations.push({
         type: PendingTaskType.Backup,
         givesLifeness: false,
-        timestampDue: bp.state.nextBackupTimestamp,
+        timestampDue: AbsoluteTime.fromTimestamp(bp.state.nextBackupTimestamp),
         backupProviderBaseUrl: bp.baseUrl,
         lastError: undefined,
       });
@@ -325,7 +329,7 @@ async function gatherBackupPending(
 export async function getPendingOperations(
   ws: InternalWalletState,
 ): Promise<PendingOperationsResponse> {
-  const now = getTimestampNow();
+  const now = AbsoluteTime.now();
   return await ws.db
     .mktx((x) => ({
       backupProviders: x.backupProviders,
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts 
b/packages/taler-wallet-core/src/operations/recoup.ts
index 23d14f21..84a27966 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -27,11 +27,11 @@
 import {
   Amounts,
   codecForRecoupConfirmation,
-  getTimestampNow,
   j2s,
   NotificationType,
   RefreshReason,
   TalerErrorDetails,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
 import {
@@ -110,7 +110,7 @@ async function putGroupAsFinished(
   }
   if (allFinished) {
     logger.info("all recoups of recoup group are finished");
-    recoupGroup.timestampFinished = getTimestampNow();
+    recoupGroup.timestampFinished = TalerProtocolTimestamp.now();
     recoupGroup.retryInfo = initRetryInfo();
     recoupGroup.lastError = undefined;
     if (recoupGroup.scheduleRefreshCoins.length > 0) {
@@ -467,7 +467,7 @@ export async function createRecoupGroup(
     coinPubs: coinPubs,
     lastError: undefined,
     timestampFinished: undefined,
-    timestampStarted: getTimestampNow(),
+    timestampStarted: TalerProtocolTimestamp.now(),
     retryInfo: initRetryInfo(),
     recoupFinishedPerCoin: coinPubs.map(() => false),
     // Will be populated later
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index 8b6d8b2e..11f0f6c5 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -23,7 +23,7 @@ import {
   ExchangeRefreshRevealRequest,
   getRandomBytes,
   HttpStatusCode,
-  j2s,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import {
   CoinRecord,
@@ -42,11 +42,8 @@ import {
   fnutil,
   NotificationType,
   RefreshGroupId,
-  RefreshPlanchetInfo,
   RefreshReason,
-  stringifyTimestamp,
   TalerErrorDetails,
-  timestampToIsoString,
 } from "@gnu-taler/taler-util";
 import { AmountJson, Amounts } from "@gnu-taler/taler-util";
 import { amountToPretty } from "@gnu-taler/taler-util";
@@ -61,12 +58,7 @@ import {
   Duration,
   durationFromSpec,
   durationMul,
-  getTimestampNow,
-  isTimestampExpired,
-  Timestamp,
-  timestampAddDuration,
-  timestampDifference,
-  timestampMin,
+  AbsoluteTime,
   URL,
 } from "@gnu-taler/taler-util";
 import { guardOperationException } from "../errors.js";
@@ -139,7 +131,7 @@ function updateGroupStatus(rg: RefreshGroupRecord): void {
       rg.frozen = true;
       rg.retryInfo = initRetryInfo();
     } else {
-      rg.timestampFinished = getTimestampNow();
+      rg.timestampFinished = AbsoluteTime.toTimestamp(AbsoluteTime.now());
       rg.operationStatus = OperationStatus.Finished;
       rg.retryInfo = initRetryInfo();
     }
@@ -234,19 +226,6 @@ async function refreshCreateSession(
     availableDenoms,
   );
 
-  if (logger.shouldLogTrace()) {
-    logger.trace(`printing selected denominations for refresh`);
-    logger.trace(`current time: ${stringifyTimestamp(getTimestampNow())}`);
-    for (const denom of newCoinDenoms.selectedDenoms) {
-      logger.trace(`denom ${denom.denom}, count ${denom.count}`);
-      logger.trace(
-        `withdrawal expiration ${stringifyTimestamp(
-          denom.denom.stampExpireWithdraw,
-        )}`,
-      );
-    }
-  }
-
   if (newCoinDenoms.selectedDenoms.length === 0) {
     logger.trace(
       `not refreshing, available amount ${amountToPretty(
@@ -306,7 +285,9 @@ async function refreshCreateSession(
 }
 
 function getRefreshRequestTimeout(rg: RefreshGroupRecord): Duration {
-  return { d_ms: 5000 };
+  return Duration.fromSpec({
+    seconds: 5,
+  });
 }
 
 async function refreshMelt(
@@ -949,12 +930,12 @@ export async function createRefreshGroup(
     retryInfo: initRetryInfo(),
     inputPerCoin,
     estimatedOutputPerCoin,
-    timestampCreated: getTimestampNow(),
+    timestampCreated: TalerProtocolTimestamp.now(),
   };
 
   if (oldCoinPubs.length == 0) {
     logger.warn("created refresh group with zero coins");
-    refreshGroup.timestampFinished = getTimestampNow();
+    refreshGroup.timestampFinished = TalerProtocolTimestamp.now();
     refreshGroup.operationStatus = OperationStatus.Finished;
   }
 
@@ -974,25 +955,23 @@ export async function createRefreshGroup(
 /**
  * Timestamp after which the wallet would do the next check for an 
auto-refresh.
  */
-function getAutoRefreshCheckThreshold(d: DenominationRecord): Timestamp {
-  const delta = timestampDifference(
-    d.stampExpireWithdraw,
-    d.stampExpireDeposit,
-  );
+function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
+  const expireWithdraw = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
+  const expireDeposit = AbsoluteTime.fromTimestamp(d.stampExpireDeposit);
+  const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
   const deltaDiv = durationMul(delta, 0.75);
-  return timestampAddDuration(d.stampExpireWithdraw, deltaDiv);
+  return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);
 }
 
 /**
  * Timestamp after which the wallet would do an auto-refresh.
  */
-function getAutoRefreshExecuteThreshold(d: DenominationRecord): Timestamp {
-  const delta = timestampDifference(
-    d.stampExpireWithdraw,
-    d.stampExpireDeposit,
-  );
+function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime {
+  const expireWithdraw = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
+  const expireDeposit = AbsoluteTime.fromTimestamp(d.stampExpireDeposit);
+  const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
   const deltaDiv = durationMul(delta, 0.5);
-  return timestampAddDuration(d.stampExpireWithdraw, deltaDiv);
+  return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);
 }
 
 export async function autoRefresh(
@@ -1001,8 +980,8 @@ export async function autoRefresh(
 ): Promise<void> {
   logger.info(`doing auto-refresh check for '${exchangeBaseUrl}'`);
   await updateExchangeFromUrl(ws, exchangeBaseUrl, undefined, true);
-  let minCheckThreshold = timestampAddDuration(
-    getTimestampNow(),
+  let minCheckThreshold = AbsoluteTime.addDuration(
+    AbsoluteTime.now(),
     durationFromSpec({ days: 1 }),
   );
   await ws.db
@@ -1037,11 +1016,14 @@ export async function autoRefresh(
           continue;
         }
         const executeThreshold = getAutoRefreshExecuteThreshold(denom);
-        if (isTimestampExpired(executeThreshold)) {
+        if (AbsoluteTime.isExpired(executeThreshold)) {
           refreshCoins.push(coin);
         } else {
           const checkThreshold = getAutoRefreshCheckThreshold(denom);
-          minCheckThreshold = timestampMin(minCheckThreshold, checkThreshold);
+          minCheckThreshold = AbsoluteTime.min(
+            minCheckThreshold,
+            checkThreshold,
+          );
         }
       }
       if (refreshCoins.length > 0) {
@@ -1056,12 +1038,12 @@ export async function autoRefresh(
         );
       }
       logger.info(
-        `current wallet time: ${timestampToIsoString(getTimestampNow())}`,
+        `current wallet time: ${AbsoluteTime.toIsoString(AbsoluteTime.now())}`,
       );
       logger.info(
-        `next refresh check at ${timestampToIsoString(minCheckThreshold)}`,
+        `next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,
       );
-      exchange.nextRefreshCheck = minCheckThreshold;
+      exchange.nextRefreshCheck = AbsoluteTime.toTimestamp(minCheckThreshold);
       await tx.exchanges.put(exchange);
     });
 }
diff --git a/packages/taler-wallet-core/src/operations/refund.ts 
b/packages/taler-wallet-core/src/operations/refund.ts
index 106c7936..686d545d 100644
--- a/packages/taler-wallet-core/src/operations/refund.ts
+++ b/packages/taler-wallet-core/src/operations/refund.ts
@@ -32,7 +32,6 @@ import {
   codecForAbortResponse,
   codecForMerchantOrderRefundPickupResponse,
   CoinPublicKey,
-  getTimestampNow,
   Logger,
   MerchantCoinRefundFailureStatus,
   MerchantCoinRefundStatus,
@@ -43,9 +42,10 @@ import {
   TalerErrorCode,
   TalerErrorDetails,
   URL,
-  timestampAddDuration,
   codecForMerchantOrderStatusPaid,
-  isTimestampExpired,
+  AbsoluteTime,
+  TalerProtocolTimestamp,
+  Duration,
 } from "@gnu-taler/taler-util";
 import {
   AbortStatus,
@@ -170,7 +170,7 @@ async function applySuccessfulRefund(
 
   p.refunds[refundKey] = {
     type: RefundState.Applied,
-    obtainedTime: getTimestampNow(),
+    obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
     executionTime: r.execution_time,
     refundAmount: Amounts.parseOrThrow(r.refund_amount),
     refundFee: denom.feeRefund,
@@ -222,7 +222,7 @@ async function storePendingRefund(
 
   p.refunds[refundKey] = {
     type: RefundState.Pending,
-    obtainedTime: getTimestampNow(),
+    obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
     executionTime: r.execution_time,
     refundAmount: Amounts.parseOrThrow(r.refund_amount),
     refundFee: denom.feeRefund,
@@ -275,7 +275,7 @@ async function storeFailedRefund(
 
   p.refunds[refundKey] = {
     type: RefundState.Failed,
-    obtainedTime: getTimestampNow(),
+    obtainedTime: TalerProtocolTimestamp.now(),
     executionTime: r.execution_time,
     refundAmount: Amounts.parseOrThrow(r.refund_amount),
     refundFee: denom.feeRefund,
@@ -327,7 +327,7 @@ async function acceptRefunds(
   reason: RefundReason,
 ): Promise<void> {
   logger.trace("handling refunds", refunds);
-  const now = getTimestampNow();
+  const now = TalerProtocolTimestamp.now();
 
   await ws.db
     .mktx((x) => ({
@@ -401,7 +401,10 @@ async function acceptRefunds(
       if (
         p.timestampFirstSuccessfulPay &&
         p.autoRefundDeadline &&
-        p.autoRefundDeadline.t_ms > now.t_ms
+        AbsoluteTime.cmp(
+          AbsoluteTime.fromTimestamp(p.autoRefundDeadline),
+          AbsoluteTime.fromTimestamp(now),
+        ) > 0
       ) {
         queryDone = false;
       }
@@ -556,8 +559,10 @@ export async function applyRefund(
         ).amount,
       ).amount;
     } else {
-      amountRefundGone = Amounts.add(amountRefundGone, refund.refundAmount)
-        .amount;
+      amountRefundGone = Amounts.add(
+        amountRefundGone,
+        refund.refundAmount,
+      ).amount;
     }
   });
 
@@ -623,7 +628,9 @@ async function processPurchaseQueryRefundImpl(
     if (
       waitForAutoRefund &&
       purchase.autoRefundDeadline &&
-      !isTimestampExpired(purchase.autoRefundDeadline)
+      !AbsoluteTime.isExpired(
+        AbsoluteTime.fromTimestamp(purchase.autoRefundDeadline),
+      )
     ) {
       const requestUrl = new URL(
         `orders/${purchase.download.contractData.orderId}`,
@@ -731,11 +738,13 @@ async function processPurchaseQueryRefundImpl(
           purchase.payCoinSelection.coinContributions[i],
         ),
         rtransaction_id: 0,
-        execution_time: timestampAddDuration(
-          purchase.download.contractData.timestamp,
-          {
-            d_ms: 1000,
-          },
+        execution_time: AbsoluteTime.toTimestamp(
+          AbsoluteTime.addDuration(
+            AbsoluteTime.fromTimestamp(
+              purchase.download.contractData.timestamp,
+            ),
+            Duration.fromSpec({ seconds: 1 }),
+          ),
         ),
       });
     }
diff --git a/packages/taler-wallet-core/src/operations/reserves.ts 
b/packages/taler-wallet-core/src/operations/reserves.ts
index d91ce89f..ac948363 100644
--- a/packages/taler-wallet-core/src/operations/reserves.ts
+++ b/packages/taler-wallet-core/src/operations/reserves.ts
@@ -29,15 +29,13 @@ import {
   durationMin,
   encodeCrock,
   getRandomBytes,
-  getTimestampNow,
   j2s,
   Logger,
   NotificationType,
   randomBytes,
-  ReserveTransactionType,
   TalerErrorCode,
   TalerErrorDetails,
-  Timestamp,
+  AbsoluteTime,
   URL,
 } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../common.js";
@@ -172,7 +170,7 @@ export async function createReserve(
   req: CreateReserveRequest,
 ): Promise<CreateReserveResponse> {
   const keypair = await ws.cryptoApi.createEddsaKeypair();
-  const now = getTimestampNow();
+  const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
   const canonExchange = canonicalizeBaseUrl(req.exchange);
 
   let reserveStatus;
@@ -217,7 +215,6 @@ export async function createReserve(
     timestampReserveInfoPosted: undefined,
     bankInfo,
     reserveStatus,
-    lastSuccessfulStatusQuery: undefined,
     retryInfo: initRetryInfo(),
     lastError: undefined,
     currency: req.amount.currency,
@@ -403,7 +400,9 @@ async function registerReserveWithBank(
         default:
           return;
       }
-      r.timestampReserveInfoPosted = getTimestampNow();
+      r.timestampReserveInfoPosted = AbsoluteTime.toTimestamp(
+        AbsoluteTime.now(),
+      );
       r.reserveStatus = ReserveRecordStatus.WaitConfirmBank;
       r.operationStatus = OperationStatus.Pending;
       if (!r.bankInfo) {
@@ -472,7 +471,7 @@ async function processReserveBankStatus(
           default:
             return;
         }
-        const now = getTimestampNow();
+        const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
         r.timestampBankConfirmed = now;
         r.reserveStatus = ReserveRecordStatus.BankAborted;
         r.operationStatus = OperationStatus.Finished;
@@ -509,7 +508,7 @@ async function processReserveBankStatus(
           default:
             return;
         }
-        const now = getTimestampNow();
+        const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
         r.timestampBankConfirmed = now;
         r.reserveStatus = ReserveRecordStatus.QueryingStatus;
         r.operationStatus = OperationStatus.Pending;
@@ -683,7 +682,7 @@ async function updateReserve(
         exchangeBaseUrl: reserve.exchangeBaseUrl,
         reservePub: reserve.reservePub,
         rawWithdrawalAmount: remainingAmount,
-        timestampStart: getTimestampNow(),
+        timestampStart: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
         retryInfo: initRetryInfo(),
         lastError: undefined,
         denomsSel: denomSelectionInfoToState(denomSelInfo),
@@ -736,7 +735,7 @@ async function processReserveImpl(
     await resetReserveRetry(ws, reservePub);
   } else if (
     reserve.retryInfo &&
-    !Timestamp.isExpired(reserve.retryInfo.nextRetry)
+    !AbsoluteTime.isExpired(reserve.retryInfo.nextRetry)
   ) {
     logger.trace("processReserve retry not due yet");
     return;
diff --git a/packages/taler-wallet-core/src/operations/testing.ts 
b/packages/taler-wallet-core/src/operations/testing.ts
index 93f48fb8..23fee56c 100644
--- a/packages/taler-wallet-core/src/operations/testing.ts
+++ b/packages/taler-wallet-core/src/operations/testing.ts
@@ -229,8 +229,8 @@ async function createOrder(
       amount,
       summary,
       fulfillment_url: fulfillmentUrl,
-      refund_deadline: { t_ms: t * 1000 },
-      wire_transfer_deadline: { t_ms: t * 1000 },
+      refund_deadline: { t_s: t },
+      wire_transfer_deadline: { t_s: t },
     },
   };
   const resp = await http.postJson(reqUrl, orderReq, {
diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index a2a4e6f4..76512029 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -22,7 +22,6 @@ import {
   parseTipUri,
   codecForTipPickupGetResponse,
   Amounts,
-  getTimestampNow,
   TalerErrorDetails,
   NotificationType,
   TipPlanchetDetail,
@@ -32,6 +31,7 @@ import {
   DenomKeyType,
   BlindedDenominationSignature,
   codecForMerchantTipResponseV2,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js";
 import {
@@ -39,6 +39,7 @@ import {
   CoinRecord,
   CoinSourceType,
   CoinStatus,
+  TipRecord,
 } from "../db.js";
 import { j2s } from "@gnu-taler/taler-util";
 import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
@@ -115,14 +116,14 @@ export async function prepareTip(
     const secretSeed = encodeCrock(getRandomBytes(64));
     const denomSelUid = encodeCrock(getRandomBytes(32));
 
-    const newTipRecord = {
+    const newTipRecord: TipRecord = {
       walletTipId: walletTipId,
       acceptedTimestamp: undefined,
       tipAmountRaw: amount,
       tipExpiration: tipPickupStatus.expiration,
       exchangeBaseUrl: tipPickupStatus.exchange_url,
       merchantBaseUrl: res.merchantBaseUrl,
-      createdTimestamp: getTimestampNow(),
+      createdTimestamp: TalerProtocolTimestamp.now(),
       merchantTipId: res.merchantTipId,
       tipAmountEffective: Amounts.sub(
         amount,
@@ -397,7 +398,7 @@ async function processTipImpl(
       if (tr.pickedUpTimestamp) {
         return;
       }
-      tr.pickedUpTimestamp = getTimestampNow();
+      tr.pickedUpTimestamp = TalerProtocolTimestamp.now();
       tr.lastError = undefined;
       tr.retryInfo = initRetryInfo();
       await tx.tips.put(tr);
@@ -421,7 +422,7 @@ export async function acceptTip(
         logger.error("tip not found");
         return false;
       }
-      tipRecord.acceptedTimestamp = getTimestampNow();
+      tipRecord.acceptedTimestamp = TalerProtocolTimestamp.now();
       await tx.tips.put(tipRecord);
       return true;
     });
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 23ab3905..bc466f5a 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -18,12 +18,12 @@
  * Imports.
  */
 import {
+  AbsoluteTime,
   AmountJson,
   Amounts,
   Logger,
   OrderShortInfo,
   PaymentStatus,
-  timestampCmp,
   Transaction,
   TransactionsRequest,
   TransactionsResponse,
@@ -309,7 +309,7 @@ export async function getTransactions(
 
           for (const rk of Object.keys(pr.refunds)) {
             const refund = pr.refunds[rk];
-            const groupKey = `${refund.executionTime.t_ms}`;
+            const groupKey = `${refund.executionTime.t_s}`;
             refundGroupKeys.add(groupKey);
           }
 
@@ -333,7 +333,7 @@ export async function getTransactions(
             let amountEffective = 
Amounts.getZero(contractData.amount.currency);
             for (const rk of Object.keys(pr.refunds)) {
               const refund = pr.refunds[rk];
-              const myGroupKey = `${refund.executionTime.t_ms}`;
+              const myGroupKey = `${refund.executionTime.t_s}`;
               if (myGroupKey !== groupKey) {
                 continue;
               }
@@ -403,8 +403,18 @@ export async function getTransactions(
   const txPending = transactions.filter((x) => x.pending);
   const txNotPending = transactions.filter((x) => !x.pending);
 
-  txPending.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp));
-  txNotPending.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp));
+  txPending.sort((h1, h2) =>
+    AbsoluteTime.cmp(
+      AbsoluteTime.fromTimestamp(h1.timestamp),
+      AbsoluteTime.fromTimestamp(h2.timestamp),
+    ),
+  );
+  txNotPending.sort((h1, h2) =>
+    AbsoluteTime.cmp(
+      AbsoluteTime.fromTimestamp(h1.timestamp),
+      AbsoluteTime.fromTimestamp(h2.timestamp),
+    ),
+  );
 
   return { transactions: [...txNotPending, ...txPending] };
 }
@@ -485,11 +495,10 @@ export async function deleteTransaction(
           });
           return;
         }
-        const reserveRecord:
-          | ReserveRecord
-          | undefined = await 
tx.reserves.indexes.byInitialWithdrawalGroupId.get(
-          withdrawalGroupId,
-        );
+        const reserveRecord: ReserveRecord | undefined =
+          await tx.reserves.indexes.byInitialWithdrawalGroupId.get(
+            withdrawalGroupId,
+          );
         if (reserveRecord && !reserveRecord.initialWithdrawalStarted) {
           const reservePub = reserveRecord.reservePub;
           await tx.reserves.delete(reservePub);
diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts 
b/packages/taler-wallet-core/src/operations/withdraw.test.ts
index 02540848..e5894a3e 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.test.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts
@@ -62,16 +62,16 @@ test("withdrawal selection bug repro", (t) => {
       masterSig:
         
"4F0P456CNNTTWK8BFJHGM3JTD6FVVNZY8EP077GYAHDJ5Y81S5RQ3SMS925NXMDVG9A88JAAP0E2GDZBC21PP5NHFFVWHAW3AVT8J3R",
       stampExpireDeposit: {
-        t_ms: 1742909388000,
+        t_s: 1742909388,
       },
       stampExpireLegal: {
-        t_ms: 1900589388000,
+        t_s: 1900589388,
       },
       stampExpireWithdraw: {
-        t_ms: 1679837388000,
+        t_s: 1679837388,
       },
       stampStart: {
-        t_ms: 1585229388000,
+        t_s: 1585229388,
       },
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: {
@@ -79,7 +79,7 @@ test("withdrawal selection bug repro", (t) => {
         fraction: 0,
         value: 1000,
       },
-      listIssueDate: { t_ms: 0 },
+      listIssueDate: { t_s: 0 },
     },
     {
       denomPub: {
@@ -117,16 +117,16 @@ test("withdrawal selection bug repro", (t) => {
       masterSig:
         
"P99AW82W46MZ0AKW7Z58VQPXFNTJQM9DVTYPBDF6KVYF38PPVDAZTV7JQ8TY7HGEC7JJJAY4E7AY7J3W1WV10DAZZQHHKTAVTSRAC20",
       stampExpireDeposit: {
-        t_ms: 1742909388000,
+        t_s: 1742909388,
       },
       stampExpireLegal: {
-        t_ms: 1900589388000,
+        t_s: 1900589388,
       },
       stampExpireWithdraw: {
-        t_ms: 1679837388000,
+        t_s: 1679837388,
       },
       stampStart: {
-        t_ms: 1585229388000,
+        t_s: 1585229388,
       },
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: {
@@ -134,7 +134,7 @@ test("withdrawal selection bug repro", (t) => {
         fraction: 0,
         value: 10,
       },
-      listIssueDate: { t_ms: 0 },
+      listIssueDate: { t_s: 0 },
     },
     {
       denomPub: {
@@ -171,16 +171,16 @@ test("withdrawal selection bug repro", (t) => {
       masterSig:
         
"8S4VZGHE5WE0N5ZVCHYW9KZZR4YAKK15S46MV1HR1QB9AAMH3NWPW4DCR4NYGJK33Q8YNFY80SWNS6XKAP5DEVK933TM894FJ2VGE3G",
       stampExpireDeposit: {
-        t_ms: 1742909388000,
+        t_s: 1742909388,
       },
       stampExpireLegal: {
-        t_ms: 1900589388000,
+        t_s: 1900589388,
       },
       stampExpireWithdraw: {
-        t_ms: 1679837388000,
+        t_s: 1679837388,
       },
       stampStart: {
-        t_ms: 1585229388000,
+        t_s: 1585229388,
       },
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: {
@@ -188,7 +188,7 @@ test("withdrawal selection bug repro", (t) => {
         fraction: 0,
         value: 5,
       },
-      listIssueDate: { t_ms: 0 },
+      listIssueDate: { t_s: 0 },
     },
     {
       denomPub: {
@@ -226,16 +226,16 @@ test("withdrawal selection bug repro", (t) => {
       masterSig:
         
"E3AWGAG8VB42P3KXM8B04Z6M483SX59R3Y4T53C3NXCA2NPB6C7HVCMVX05DC6S58E9X40NGEBQNYXKYMYCF3ASY2C4WP1WCZ4ME610",
       stampExpireDeposit: {
-        t_ms: 1742909388000,
+        t_s: 1742909388,
       },
       stampExpireLegal: {
-        t_ms: 1900589388000,
+        t_s: 1900589388,
       },
       stampExpireWithdraw: {
-        t_ms: 1679837388000,
+        t_s: 1679837388,
       },
       stampStart: {
-        t_ms: 1585229388000,
+        t_s: 1585229388,
       },
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: {
@@ -243,7 +243,7 @@ test("withdrawal selection bug repro", (t) => {
         fraction: 0,
         value: 1,
       },
-      listIssueDate: { t_ms: 0 },
+      listIssueDate: { t_s: 0 },
     },
     {
       denomPub: {
@@ -280,16 +280,16 @@ test("withdrawal selection bug repro", (t) => {
       masterSig:
         
"0ES1RKV002XB4YP21SN0QB7RSDHGYT0XAE65JYN8AVJAA6H7JZFN7JADXT521DJS89XMGPZGR8GCXF1516Y0Q9QDV00E6NMFA6CF838",
       stampExpireDeposit: {
-        t_ms: 1742909388000,
+        t_s: 1742909388,
       },
       stampExpireLegal: {
-        t_ms: 1900589388000,
+        t_s: 1900589388,
       },
       stampExpireWithdraw: {
-        t_ms: 1679837388000,
+        t_s: 1679837388,
       },
       stampStart: {
-        t_ms: 1585229388000,
+        t_s: 1585229388,
       },
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: {
@@ -297,7 +297,7 @@ test("withdrawal selection bug repro", (t) => {
         fraction: 10000000,
         value: 0,
       },
-      listIssueDate: { t_ms: 0 },
+      listIssueDate: { t_s: 0 },
     },
     {
       denomPub: {
@@ -334,16 +334,16 @@ test("withdrawal selection bug repro", (t) => {
       masterSig:
         
"58QEB6C6N7602E572E3JYANVVJ9BRW0V9E2ZFDW940N47YVQDK9SAFPWBN5YGT3G1742AFKQ0CYR4DM2VWV0Z0T1XMEKWN6X2EZ9M0R",
       stampExpireDeposit: {
-        t_ms: 1742909388000,
+        t_s: 1742909388,
       },
       stampExpireLegal: {
-        t_ms: 1900589388000,
+        t_s: 1900589388,
       },
       stampExpireWithdraw: {
-        t_ms: 1679837388000,
+        t_s: 1679837388,
       },
       stampStart: {
-        t_ms: 1585229388000,
+        t_s: 1585229388,
       },
       verificationStatus: DenominationVerificationStatus.Unverified,
       value: {
@@ -351,7 +351,7 @@ test("withdrawal selection bug repro", (t) => {
         fraction: 0,
         value: 2,
       },
-      listIssueDate: { t_ms: 0 },
+      listIssueDate: { t_s: 0 },
     },
   ];
 
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index 392cecf0..e4c6f2a6 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -26,16 +26,12 @@ import {
   codecForWithdrawResponse,
   durationFromSpec,
   ExchangeListItem,
-  getDurationRemaining,
-  getTimestampNow,
   Logger,
   NotificationType,
   parseWithdrawUri,
   TalerErrorCode,
   TalerErrorDetails,
-  Timestamp,
-  timestampCmp,
-  timestampSubtractDuraction,
+  AbsoluteTime,
   WithdrawResponse,
   URL,
   WithdrawUriInfoResponse,
@@ -44,6 +40,8 @@ import {
   LibtoolVersion,
   UnblindedSignature,
   ExchangeWithdrawRequest,
+  Duration,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import {
   CoinRecord,
@@ -147,7 +145,7 @@ export interface ExchangeWithdrawDetails {
   /**
    * The earliest deposit expiration of the selected coins.
    */
-  earliestDepositExpiration: Timestamp;
+  earliestDepositExpiration: TalerProtocolTimestamp;
 
   /**
    * Number of currently offered denominations.
@@ -184,18 +182,20 @@ export interface ExchangeWithdrawDetails {
  * revocation and offered state.
  */
 export function isWithdrawableDenom(d: DenominationRecord): boolean {
-  const now = getTimestampNow();
-  const started = timestampCmp(now, d.stampStart) >= 0;
-  let lastPossibleWithdraw: Timestamp;
+  const now = AbsoluteTime.now();
+  const start = AbsoluteTime.fromTimestamp(d.stampStart);
+  const withdrawExpire = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
+  const started = AbsoluteTime.cmp(now, start) >= 0;
+  let lastPossibleWithdraw: AbsoluteTime;
   if (walletCoreDebugFlags.denomselAllowLate) {
-    lastPossibleWithdraw = d.stampExpireWithdraw;
+    lastPossibleWithdraw = start;
   } else {
-    lastPossibleWithdraw = timestampSubtractDuraction(
-      d.stampExpireWithdraw,
+    lastPossibleWithdraw = AbsoluteTime.subtractDuraction(
+      withdrawExpire,
       durationFromSpec({ minutes: 5 }),
     );
   }
-  const remaining = getDurationRemaining(lastPossibleWithdraw, now);
+  const remaining = Duration.getRemaining(lastPossibleWithdraw, now);
   const stillOkay = remaining.d_ms !== 0;
   return started && stillOkay && !d.isRevoked && d.isOffered;
 }
@@ -274,7 +274,7 @@ export function selectWithdrawalDenominations(
 /**
  * Get information about a withdrawal from
  * a taler://withdraw URI by asking the bank.
- * 
+ *
  * FIXME: Move into bank client.
  */
 export async function getBankWithdrawalInfo(
@@ -947,7 +947,7 @@ async function processWithdrawGroupImpl(
       logger.trace(`now withdrawn ${numFinished} of ${numTotalCoins} coins`);
       if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {
         finishedForFirstTime = true;
-        wg.timestampFinish = getTimestampNow();
+        wg.timestampFinish = TalerProtocolTimestamp.now();
         wg.operationStatus = OperationStatus.Finished;
         delete wg.lastError;
         wg.retryInfo = initRetryInfo();
@@ -999,7 +999,12 @@ export async function getExchangeWithdrawalInfo(
   for (let i = 1; i < selectedDenoms.selectedDenoms.length; i++) {
     const expireDeposit =
       selectedDenoms.selectedDenoms[i].denom.stampExpireDeposit;
-    if (expireDeposit.t_ms < earliestDepositExpiration.t_ms) {
+    if (
+      AbsoluteTime.cmp(
+        AbsoluteTime.fromTimestamp(expireDeposit),
+        AbsoluteTime.fromTimestamp(earliestDepositExpiration),
+      ) < 0
+    ) {
       earliestDepositExpiration = expireDeposit;
     }
   }
diff --git a/packages/taler-wallet-core/src/pending-types.ts 
b/packages/taler-wallet-core/src/pending-types.ts
index 911d0d8b..4b1434bb 100644
--- a/packages/taler-wallet-core/src/pending-types.ts
+++ b/packages/taler-wallet-core/src/pending-types.ts
@@ -27,7 +27,8 @@
 import {
   TalerErrorDetails,
   BalancesResponse,
-  Timestamp,
+  AbsoluteTime,
+  TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 import { ReserveRecordStatus } from "./db.js";
 import { RetryInfo } from "./util/retries.js";
@@ -112,7 +113,7 @@ export interface PendingReserveTask {
   type: PendingTaskType.Reserve;
   retryInfo: RetryInfo | undefined;
   stage: ReserveRecordStatus;
-  timestampCreated: Timestamp;
+  timestampCreated: TalerProtocolTimestamp;
   reserveType: ReserveType;
   reservePub: string;
   bankWithdrawConfirmUrl?: string;
@@ -135,7 +136,7 @@ export interface PendingRefreshTask {
 export interface PendingProposalDownloadTask {
   type: PendingTaskType.ProposalDownload;
   merchantBaseUrl: string;
-  proposalTimestamp: Timestamp;
+  proposalTimestamp: TalerProtocolTimestamp;
   proposalId: string;
   orderId: string;
   lastError?: TalerErrorDetails;
@@ -149,7 +150,7 @@ export interface PendingProposalDownloadTask {
 export interface PendingProposalChoiceOperation {
   type: PendingTaskType.ProposalChoice;
   merchantBaseUrl: string;
-  proposalTimestamp: Timestamp;
+  proposalTimestamp: AbsoluteTime;
   proposalId: string;
 }
 
@@ -231,7 +232,7 @@ export interface PendingTaskInfoCommon {
   /**
    * Timestamp when the pending operation should be executed next.
    */
-  timestampDue: Timestamp;
+  timestampDue: AbsoluteTime;
 
   /**
    * Retry info.  Currently used to stop the wallet after any operation
diff --git a/packages/taler-wallet-core/src/util/http.ts 
b/packages/taler-wallet-core/src/util/http.ts
index 43fe29bb..79afd570 100644
--- a/packages/taler-wallet-core/src/util/http.ts
+++ b/packages/taler-wallet-core/src/util/http.ts
@@ -28,10 +28,7 @@ import { OperationFailedError, makeErrorDetails } from 
"../errors.js";
 import {
   Logger,
   Duration,
-  Timestamp,
-  getTimestampNow,
-  timestampAddDuration,
-  timestampMax,
+  AbsoluteTime,
   TalerErrorDetails,
   Codec,
   j2s,
@@ -314,24 +311,24 @@ export async function readSuccessResponseTextOrThrow<T>(
 /**
  * Get the timestamp at which the response's content is considered expired.
  */
-export function getExpiryTimestamp(
+export function getExpiry(
   httpResponse: HttpResponse,
   opt: { minDuration?: Duration },
-): Timestamp {
+): AbsoluteTime {
   const expiryDateMs = new Date(
     httpResponse.headers.get("expiry") ?? "",
   ).getTime();
-  let t: Timestamp;
+  let t: AbsoluteTime;
   if (Number.isNaN(expiryDateMs)) {
-    t = getTimestampNow();
+    t = AbsoluteTime.now();
   } else {
     t = {
       t_ms: expiryDateMs,
     };
   }
   if (opt.minDuration) {
-    const t2 = timestampAddDuration(getTimestampNow(), opt.minDuration);
-    return timestampMax(t, t2);
+    const t2 = AbsoluteTime.addDuration(AbsoluteTime.now(), opt.minDuration);
+    return AbsoluteTime.max(t, t2);
   }
   return t;
 }
diff --git a/packages/taler-wallet-core/src/util/retries.ts 
b/packages/taler-wallet-core/src/util/retries.ts
index 8dec22be..4b78d38e 100644
--- a/packages/taler-wallet-core/src/util/retries.ts
+++ b/packages/taler-wallet-core/src/util/retries.ts
@@ -21,11 +21,11 @@
 /**
  * Imports.
  */
-import { Timestamp, Duration, getTimestampNow } from "@gnu-taler/taler-util";
+import { AbsoluteTime, Duration } from "@gnu-taler/taler-util";
 
 export interface RetryInfo {
-  firstTry: Timestamp;
-  nextRetry: Timestamp;
+  firstTry: AbsoluteTime;
+  nextRetry: AbsoluteTime;
   retryCounter: number;
 }
 
@@ -45,7 +45,7 @@ export function updateRetryInfoTimeout(
   r: RetryInfo,
   p: RetryPolicy = defaultRetryPolicy,
 ): void {
-  const now = getTimestampNow();
+  const now = AbsoluteTime.now();
   if (now.t_ms === "never") {
     throw Error("assertion failed");
   }
@@ -54,10 +54,14 @@ export function updateRetryInfoTimeout(
     return;
   }
 
-  const nextIncrement = p.backoffDelta.d_ms * Math.pow(p.backoffBase, 
r.retryCounter)
+  const nextIncrement =
+    p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
 
   const t =
-    now.t_ms + (p.maxTimeout.d_ms === "forever" ? nextIncrement : 
Math.min(p.maxTimeout.d_ms, nextIncrement));
+    now.t_ms +
+    (p.maxTimeout.d_ms === "forever"
+      ? nextIncrement
+      : Math.min(p.maxTimeout.d_ms, nextIncrement));
   r.nextRetry = { t_ms: t };
 }
 
@@ -73,13 +77,13 @@ export function getRetryDuration(
     return { d_ms: "forever" };
   }
   const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
-  return { d_ms: p.maxTimeout.d_ms === "forever" ? t : 
Math.min(p.maxTimeout.d_ms, t) };
+  return {
+    d_ms: p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t),
+  };
 }
 
-export function initRetryInfo(
-  p: RetryPolicy = defaultRetryPolicy,
-): RetryInfo {
-  const now = getTimestampNow();
+export function initRetryInfo(p: RetryPolicy = defaultRetryPolicy): RetryInfo {
+  const now = AbsoluteTime.now();
   const info = {
     firstTry: now,
     nextRetry: now,
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 32941756..bbff465a 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -64,9 +64,7 @@ import {
   durationMin,
   ExchangeListItem,
   ExchangesListRespose,
-  getDurationRemaining,
   GetExchangeTosResult,
-  isTimestampExpired,
   j2s,
   KnownBankAccounts,
   Logger,
@@ -76,11 +74,12 @@ import {
   PaytoUri,
   RefreshReason,
   TalerErrorCode,
-  Timestamp,
-  timestampMin,
+  AbsoluteTime,
   URL,
   WalletNotification,
+  Duration,
 } from "@gnu-taler/taler-util";
+import { timeStamp } from "console";
 import {
   DenomInfo,
   ExchangeOperations,
@@ -292,7 +291,7 @@ export async function runPending(
 ): Promise<void> {
   const pendingOpsResponse = await getPendingOperations(ws);
   for (const p of pendingOpsResponse.pendingOperations) {
-    if (!forceNow && !isTimestampExpired(p.timestampDue)) {
+    if (!forceNow && !AbsoluteTime.isExpired(p.timestampDue)) {
       continue;
     }
     try {
@@ -340,10 +339,10 @@ async function runTaskLoop(
     logger.trace(`pending operations: ${j2s(pending)}`);
     let numGivingLiveness = 0;
     let numDue = 0;
-    let minDue: Timestamp = { t_ms: "never" };
+    let minDue: AbsoluteTime = AbsoluteTime.never();
     for (const p of pending.pendingOperations) {
-      minDue = timestampMin(minDue, p.timestampDue);
-      if (isTimestampExpired(p.timestampDue)) {
+      minDue = AbsoluteTime.min(minDue, p.timestampDue);
+      if (AbsoluteTime.isExpired(p.timestampDue)) {
         numDue++;
       }
       if (p.givesLifeness) {
@@ -377,7 +376,7 @@ async function runTaskLoop(
         durationFromSpec({
           seconds: 5,
         }),
-        getDurationRemaining(minDue),
+        Duration.getRemaining(minDue),
       );
       logger.trace(`waiting for at most ${dt.d_ms} ms`);
       const timeout = ws.timerGroup.resolveAfter(dt);
@@ -394,7 +393,7 @@ async function runTaskLoop(
         `running ${pending.pendingOperations.length} pending operations`,
       );
       for (const p of pending.pendingOperations) {
-        if (!isTimestampExpired(p.timestampDue)) {
+        if (!AbsoluteTime.isExpired(p.timestampDue)) {
           continue;
         }
         try {
diff --git 
a/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx
 
b/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx
index 658a41aa..ea29d4a7 100644
--- 
a/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx
@@ -22,7 +22,11 @@
 import { PendingTransactionsView as TestedComponent } from 
"./PendingTransactions";
 import { Fragment, h, VNode } from "preact";
 import { createExample } from "../test-utils";
-import { Transaction, TransactionType } from "@gnu-taler/taler-util";
+import {
+  TalerProtocolTimestamp,
+  Transaction,
+  TransactionType,
+} from "@gnu-taler/taler-util";
 
 export default {
   title: "component/PendingTransactions",
@@ -34,9 +38,7 @@ export const OnePendingTransaction = 
createExample(TestedComponent, {
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
   ],
 });
@@ -46,23 +48,17 @@ export const ThreePendingTransactions = 
createExample(TestedComponent, {
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
   ],
 });
@@ -72,72 +68,52 @@ export const TenPendingTransactions = 
createExample(TestedComponent, {
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1)
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
     {
       amountEffective: "USD:10",
       type: TransactionType.Withdrawal,
-      timestamp: {
-        t_ms: 1,
-      },
+      timestamp: TalerProtocolTimestamp.fromSeconds(1),
     } as Transaction,
   ],
 });
diff --git 
a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx 
b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
index eed31beb..7923eb6a 100644
--- a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
+++ b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
@@ -1,4 +1,9 @@
-import { Amounts, NotificationType, Transaction } from "@gnu-taler/taler-util";
+import {
+  AbsoluteTime,
+  Amounts,
+  NotificationType,
+  Transaction,
+} from "@gnu-taler/taler-util";
 import { PendingTaskInfo } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, JSX } from "preact";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
@@ -70,7 +75,11 @@ export function PendingTransactionsView({
               <b>
                 {amount.currency} {Amounts.stringifyValue(amount)}
               </b>{" "}
-              - <Time timestamp={t.timestamp} format="dd MMMM yyyy" />
+              -{" "}
+              <Time
+                timestamp={AbsoluteTime.fromTimestamp(t.timestamp)}
+                format="dd MMMM yyyy"
+              />
             </Typography>
           ),
         };
diff --git a/packages/taler-wallet-webextension/src/components/Time.tsx 
b/packages/taler-wallet-webextension/src/components/Time.tsx
index 452b0833..902187aa 100644
--- a/packages/taler-wallet-webextension/src/components/Time.tsx
+++ b/packages/taler-wallet-webextension/src/components/Time.tsx
@@ -14,7 +14,7 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Timestamp } from "@gnu-taler/taler-util";
+import { AbsoluteTime } from "@gnu-taler/taler-util";
 import { formatISO, format } from "date-fns";
 import { h, VNode } from "preact";
 
@@ -22,7 +22,7 @@ export function Time({
   timestamp,
   format: formatString,
 }: {
-  timestamp: Timestamp | undefined;
+  timestamp: AbsoluteTime | undefined;
   format: string;
 }): VNode {
   return (
diff --git 
a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx 
b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
index 12ed4139..d7eae7bb 100644
--- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
+++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
@@ -18,7 +18,7 @@ import {
   AmountJson,
   Amounts,
   AmountString,
-  Timestamp,
+  AbsoluteTime,
   Transaction,
   TransactionType,
 } from "@gnu-taler/taler-util";
@@ -46,7 +46,7 @@ export function TransactionItem(props: { tx: Transaction }): 
VNode {
           amount={tx.amountEffective}
           debitCreditIndicator={"credit"}
           title={new URL(tx.exchangeBaseUrl).hostname}
-          timestamp={tx.timestamp}
+          timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"W"}
           pending={tx.pending}
         />
@@ -59,7 +59,7 @@ export function TransactionItem(props: { tx: Transaction }): 
VNode {
           debitCreditIndicator={"debit"}
           title={tx.info.merchant.name}
           subtitle={tx.info.summary}
-          timestamp={tx.timestamp}
+          timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"P"}
           pending={tx.pending}
         />
@@ -72,7 +72,7 @@ export function TransactionItem(props: { tx: Transaction }): 
VNode {
           debitCreditIndicator={"credit"}
           subtitle={tx.info.summary}
           title={tx.info.merchant.name}
-          timestamp={tx.timestamp}
+          timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"R"}
           pending={tx.pending}
         />
@@ -84,7 +84,7 @@ export function TransactionItem(props: { tx: Transaction }): 
VNode {
           amount={tx.amountEffective}
           debitCreditIndicator={"credit"}
           title={new URL(tx.merchantBaseUrl).hostname}
-          timestamp={tx.timestamp}
+          timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"T"}
           pending={tx.pending}
         />
@@ -96,7 +96,7 @@ export function TransactionItem(props: { tx: Transaction }): 
VNode {
           amount={tx.amountEffective}
           debitCreditIndicator={"credit"}
           title={new URL(tx.exchangeBaseUrl).hostname}
-          timestamp={tx.timestamp}
+          timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"R"}
           pending={tx.pending}
         />
@@ -108,7 +108,7 @@ export function TransactionItem(props: { tx: Transaction 
}): VNode {
           amount={tx.amountEffective}
           debitCreditIndicator={"debit"}
           title={tx.targetPaytoUri}
-          timestamp={tx.timestamp}
+          timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
           iconPath={"D"}
           pending={tx.pending}
         />
@@ -165,7 +165,7 @@ function TransactionLayout(props: TransactionLayoutProps): 
VNode {
 interface TransactionLayoutProps {
   debitCreditIndicator: "debit" | "credit" | "unknown";
   amount: AmountString | "unknown";
-  timestamp: Timestamp;
+  timestamp: AbsoluteTime;
   title: string;
   subtitle?: string;
   id: string;
diff --git a/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx
index 8da59951..e475a961 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { AbsoluteTime, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
 import { createExample } from "../test-utils";
 import { View as TestedComponent } from "./Tip";
 
@@ -33,9 +34,7 @@ export const Accepted = createExample(TestedComponent, {
     accepted: true,
     merchantBaseUrl: "",
     exchangeBaseUrl: "",
-    expirationTimestamp: {
-      t_ms: 0,
-    },
+    expirationTimestamp: TalerProtocolTimestamp.fromSeconds(1),
     tipAmountEffective: "USD:10",
     tipAmountRaw: "USD:5",
     walletTipId: "id",
@@ -47,9 +46,7 @@ export const NotYetAccepted = createExample(TestedComponent, {
     accepted: false,
     merchantBaseUrl: "http://merchant.url/";,
     exchangeBaseUrl: "http://exchange.url/";,
-    expirationTimestamp: {
-      t_ms: 0,
-    },
+    expirationTimestamp: TalerProtocolTimestamp.fromSeconds(1),
     tipAmountEffective: "USD:10",
     tipAmountRaw: "USD:5",
     walletTipId: "id",
diff --git a/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx 
b/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
index 738ece2f..3deea032 100644
--- a/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
@@ -20,7 +20,7 @@ import {
   CoinDumpJson,
   ExchangeListItem,
   NotificationType,
-  Timestamp,
+  AbsoluteTime,
   Translate,
 } from "@gnu-taler/taler-util";
 import { PendingTaskInfo } from "@gnu-taler/taler-wallet-core";
diff --git a/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
index b2771bc2..92536db8 100644
--- a/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
@@ -23,6 +23,7 @@ import { ProviderPaymentType } from 
"@gnu-taler/taler-wallet-core";
 import { addDays } from "date-fns";
 import { BackupView as TestedComponent } from "./BackupPage";
 import { createExample } from "../test-utils";
+import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
 
 export default {
   title: "wallet/backup/list",
@@ -40,9 +41,8 @@ export const LotOfProviders = createExample(TestedComponent, {
       active: true,
       name: "sync.demo",
       syncProviderBaseUrl: "http://sync.taler:9967/";,
-      lastSuccessfulBackupTimestamp: {
-        t_ms: 1625063925078,
-      },
+      lastSuccessfulBackupTimestamp:
+        TalerProtocolTimestamp.fromSeconds(1625063925),
       paymentProposalIds: [
         "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
       ],
@@ -62,9 +62,8 @@ export const LotOfProviders = createExample(TestedComponent, {
       active: true,
       name: "sync.demo",
       syncProviderBaseUrl: "http://sync.taler:9967/";,
-      lastSuccessfulBackupTimestamp: {
-        t_ms: 1625063925078,
-      },
+      lastSuccessfulBackupTimestamp:
+        TalerProtocolTimestamp.fromSeconds(1625063925),
       paymentProposalIds: [
         "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
       ],
@@ -172,9 +171,8 @@ export const OneProvider = createExample(TestedComponent, {
       active: true,
       name: "sync.demo",
       syncProviderBaseUrl: "http://sync.taler:9967/";,
-      lastSuccessfulBackupTimestamp: {
-        t_ms: 1625063925078,
-      },
+      lastSuccessfulBackupTimestamp:
+        TalerProtocolTimestamp.fromSeconds(1625063925),
       paymentProposalIds: [
         "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
       ],
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
index e1d34748..8c12201b 100644
--- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -14,7 +14,7 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 
-import { Timestamp, Translate } from "@gnu-taler/taler-util";
+import { AbsoluteTime, Translate } from "@gnu-taler/taler-util";
 import {
   ProviderInfo,
   ProviderPaymentPaid,
@@ -104,7 +104,13 @@ export function BackupView({
           <BackupLayout
             key={idx}
             status={provider.paymentStatus}
-            timestamp={provider.lastSuccessfulBackupTimestamp}
+            timestamp={
+              provider.lastSuccessfulBackupTimestamp
+                ? AbsoluteTime.fromTimestamp(
+                    provider.lastSuccessfulBackupTimestamp,
+                  )
+                : undefined
+            }
             id={provider.syncProviderBaseUrl}
             active={provider.active}
             title={provider.name}
@@ -144,7 +150,7 @@ export function BackupView({
 
 interface TransactionLayoutProps {
   status: ProviderPaymentStatus;
-  timestamp?: Timestamp;
+  timestamp?: AbsoluteTime;
   title: string;
   id: string;
   active: boolean;
@@ -192,7 +198,7 @@ function BackupLayout(props: TransactionLayoutProps): VNode 
{
   );
 }
 
-function ExpirationText({ until }: { until: Timestamp }): VNode {
+function ExpirationText({ until }: { until: AbsoluteTime }): VNode {
   const { i18n } = useTranslationContext();
   return (
     <Fragment>
@@ -207,13 +213,13 @@ function ExpirationText({ until }: { until: Timestamp }): 
VNode {
   );
 }
 
-function colorByTimeToExpire(d: Timestamp): string {
+function colorByTimeToExpire(d: AbsoluteTime): string {
   if (d.t_ms === "never") return "rgb(28, 184, 65)";
   const months = differenceInMonths(d.t_ms, new Date());
   return months > 1 ? "rgb(28, 184, 65)" : "rgb(223, 117, 20)";
 }
 
-function daysUntil(d: Timestamp): string {
+function daysUntil(d: AbsoluteTime): string {
   if (d.t_ms === "never") return "";
   const duration = intervalToDuration({
     start: d.t_ms,
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index bd52995d..8138e63d 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -21,6 +21,7 @@
 
 import {
   PaymentStatus,
+  TalerProtocolTimestamp,
   TransactionCommon,
   TransactionDeposit,
   TransactionPayment,
@@ -45,9 +46,9 @@ const commonTransaction = () =>
     amountRaw: "USD:10",
     amountEffective: "USD:9",
     pending: false,
-    timestamp: {
-      t_ms: new Date().getTime() - count++ * 1000 * 60 * 60 * 7,
-    },
+    timestamp: TalerProtocolTimestamp.fromSeconds(
+      new Date().getTime() - count++ * 60 * 60 * 7,
+    ),
     transactionId: "12",
   } as TransactionCommon);
 
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.tsx
index 02fc0a76..5d00e2e0 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -126,7 +126,7 @@ export function HistoryView({
     .filter((t) => t.amountRaw.split(":")[0] === selectedCurrency)
     .reduce((rv, x) => {
       const theDate =
-        x.timestamp.t_ms === "never" ? 0 : normalizeToDay(x.timestamp.t_ms);
+        x.timestamp.t_s === "never" ? 0 : normalizeToDay(x.timestamp.t_s * 
1000);
       if (theDate) {
         (rv[theDate] = rv[theDate] || []).push(x);
       }
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx
index a170620a..c4c070fa 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
 import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core";
 import { createExample } from "../test-utils";
 import { ProviderView as TestedComponent } from "./ProviderDetailPage";
@@ -38,9 +39,8 @@ export const Active = createExample(TestedComponent, {
     active: true,
     name: "sync.demo",
     syncProviderBaseUrl: "http://sync.taler:9967/";,
-    lastSuccessfulBackupTimestamp: {
-      t_ms: 1625063925078,
-    },
+    lastSuccessfulBackupTimestamp:
+      TalerProtocolTimestamp.fromSeconds(1625063925),
     paymentProposalIds: [
       "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
     ],
@@ -63,12 +63,10 @@ export const ActiveErrorSync = 
createExample(TestedComponent, {
     active: true,
     name: "sync.demo",
     syncProviderBaseUrl: "http://sync.taler:9967/";,
-    lastSuccessfulBackupTimestamp: {
-      t_ms: 1625063925078,
-    },
-    lastAttemptedBackupTimestamp: {
-      t_ms: 1625063925078,
-    },
+    lastSuccessfulBackupTimestamp:
+      TalerProtocolTimestamp.fromSeconds(1625063925),
+    lastAttemptedBackupTimestamp:
+      TalerProtocolTimestamp.fromSeconds(1625063925078),
     paymentProposalIds: [
       "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
     ],
@@ -97,9 +95,8 @@ export const ActiveBackupProblemUnreadable = 
createExample(TestedComponent, {
     active: true,
     name: "sync.demo",
     syncProviderBaseUrl: "http://sync.taler:9967/";,
-    lastSuccessfulBackupTimestamp: {
-      t_ms: 1625063925078,
-    },
+    lastSuccessfulBackupTimestamp:
+      TalerProtocolTimestamp.fromSeconds(1625063925),
     paymentProposalIds: [
       "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
     ],
@@ -125,9 +122,8 @@ export const ActiveBackupProblemDevice = 
createExample(TestedComponent, {
     active: true,
     name: "sync.demo",
     syncProviderBaseUrl: "http://sync.taler:9967/";,
-    lastSuccessfulBackupTimestamp: {
-      t_ms: 1625063925078,
-    },
+    lastSuccessfulBackupTimestamp:
+      TalerProtocolTimestamp.fromSeconds(1625063925078),
     paymentProposalIds: [
       "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
     ],
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
index 765f34a0..afd9612e 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
@@ -15,6 +15,7 @@
 */
 
 import * as utils from "@gnu-taler/taler-util";
+import { AbsoluteTime } from "@gnu-taler/taler-util";
 import {
   ProviderInfo,
   ProviderPaymentStatus,
@@ -122,7 +123,9 @@ export function ProviderView({
       </Fragment>
     );
   }
-  const lb = info.lastSuccessfulBackupTimestamp;
+  const lb = info.lastSuccessfulBackupTimestamp
+    ? AbsoluteTime.fromTimestamp(info.lastSuccessfulBackupTimestamp)
+    : undefined;
   const isPaid =
     info.paymentStatus.type === ProviderPaymentType.Paid ||
     info.paymentStatus.type === ProviderPaymentType.TermsChanged;
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
index 7b6ac1fd..f0293842 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
@@ -21,6 +21,7 @@
 
 import {
   PaymentStatus,
+  TalerProtocolTimestamp,
   TransactionCommon,
   TransactionDeposit,
   TransactionPayment,
@@ -53,9 +54,7 @@ const commonTransaction = {
   amountRaw: "KUDOS:11",
   amountEffective: "KUDOS:9.2",
   pending: false,
-  timestamp: {
-    t_ms: new Date().getTime(),
-  },
+  timestamp: TalerProtocolTimestamp.now(),
   transactionId: "12",
 } as TransactionCommon;
 
@@ -144,18 +143,14 @@ export const Withdraw = createExample(TestedComponent, {
 export const WithdrawOneMinuteAgo = createExample(TestedComponent, {
   transaction: {
     ...exampleData.withdraw,
-    timestamp: {
-      t_ms: new Date().getTime() - 60 * 1000,
-    },
+    timestamp: TalerProtocolTimestamp.fromSeconds(new Date().getTime() - 60),
   },
 });
 
 export const WithdrawOneMinuteAgoAndPending = createExample(TestedComponent, {
   transaction: {
     ...exampleData.withdraw,
-    timestamp: {
-      t_ms: new Date().getTime() - 60 * 1000,
-    },
+    timestamp: TalerProtocolTimestamp.fromSeconds(new Date().getTime() - 60),
     pending: true,
   },
 });
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index b367416f..0e58aaf6 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -15,6 +15,7 @@
  */
 
 import {
+  AbsoluteTime,
   AmountLike,
   Amounts,
   NotificationType,
@@ -137,9 +138,9 @@ export function TransactionView({
   }): VNode {
     const showRetry =
       transaction.error !== undefined ||
-      transaction.timestamp.t_ms === "never" ||
+      transaction.timestamp.t_s === "never" ||
       (transaction.pending &&
-        differenceInSeconds(new Date(), transaction.timestamp.t_ms) > 10);
+        differenceInSeconds(new Date(), transaction.timestamp.t_s * 1000) > 
10);
 
     return (
       <Fragment>
@@ -218,7 +219,7 @@ export function TransactionView({
         <h2>
           <i18n.Translate>Withdrawal</i18n.Translate>
         </h2>
-        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
+        <Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} 
format="dd MMMM yyyy, HH:mm" />
         {transaction.pending ? (
           transaction.withdrawalDetails.type ===
           WithdrawalType.ManualTransfer ? (
@@ -342,7 +343,7 @@ export function TransactionView({
         <h2>
           <i18n.Translate>Payment</i18n.Translate>
         </h2>
-        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
+        <Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} 
format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -425,7 +426,7 @@ export function TransactionView({
         <h2>
           <i18n.Translate>Deposit</i18n.Translate>
         </h2>
-        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
+        <Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} 
format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -459,7 +460,7 @@ export function TransactionView({
         <h2>
           <i18n.Translate>Refresh</i18n.Translate>
         </h2>
-        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
+        <Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} 
format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -493,7 +494,7 @@ export function TransactionView({
         <h2>
           <i18n.Translate>Tip</i18n.Translate>
         </h2>
-        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
+        <Time timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)} 
format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -527,7 +528,10 @@ export function TransactionView({
         <h2>
           <i18n.Translate>Refund</i18n.Translate>
         </h2>
-        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
+        <Time
+          timestamp={AbsoluteTime.fromTimestamp(transaction.timestamp)}
+          format="dd MMMM yyyy, HH:mm"
+        />
         <br />
         <Part
           big

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