gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 02/03: wallet-core: introduce easier syntax for tran


From: gnunet
Subject: [taler-wallet-core] 02/03: wallet-core: introduce easier syntax for transactions
Date: Tue, 13 Sep 2022 16:10:47 +0200

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

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

commit 48540f62644b4c2e4e96095b11e202cb62e3e93d
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Sep 13 13:25:41 2022 +0200

    wallet-core: introduce easier syntax for transactions
---
 packages/taler-wallet-core/src/db-utils.ts         |  16 +-
 packages/taler-wallet-core/src/db.ts               |  97 +++++++-----
 .../src/operations/backup/export.ts                |  28 ++--
 .../src/operations/backup/import.ts                |  32 ++--
 .../src/operations/backup/index.ts                 |  47 +++---
 .../src/operations/backup/state.ts                 |  12 +-
 .../taler-wallet-core/src/operations/balance.ts    |   7 +-
 .../taler-wallet-core/src/operations/deposits.ts   |  56 ++-----
 .../taler-wallet-core/src/operations/exchanges.ts  |  50 +++----
 packages/taler-wallet-core/src/operations/pay.ts   |  91 +++++-------
 .../src/operations/peer-to-peer.ts                 |  52 +++----
 .../taler-wallet-core/src/operations/pending.ts    |  30 ++--
 .../taler-wallet-core/src/operations/recoup.ts     |  60 ++------
 .../taler-wallet-core/src/operations/refresh.ts    |  63 ++------
 .../taler-wallet-core/src/operations/refund.ts     |  37 ++---
 .../taler-wallet-core/src/operations/testing.ts    |   2 +-
 packages/taler-wallet-core/src/operations/tip.ts   |  30 +---
 .../src/operations/transactions.ts                 |  78 ++++------
 .../taler-wallet-core/src/operations/withdraw.ts   | 163 ++++++++-------------
 packages/taler-wallet-core/src/util/query.ts       |  94 ++++++------
 packages/taler-wallet-core/src/util/retries.ts     |   3 +-
 packages/taler-wallet-core/src/wallet.ts           |  47 ++----
 22 files changed, 428 insertions(+), 667 deletions(-)

diff --git a/packages/taler-wallet-core/src/db-utils.ts 
b/packages/taler-wallet-core/src/db-utils.ts
index 9f18ed9b..de54719c 100644
--- a/packages/taler-wallet-core/src/db-utils.ts
+++ b/packages/taler-wallet-core/src/db-utils.ts
@@ -46,9 +46,13 @@ function upgradeFromStoreMap(
 ): void {
   if (oldVersion === 0) {
     for (const n in storeMap) {
-      const swi: StoreWithIndexes<StoreDescriptor<unknown>, any> = storeMap[n];
+      const swi: StoreWithIndexes<
+        any,
+        StoreDescriptor<unknown>,
+        any
+      > = storeMap[n];
       const storeDesc: StoreDescriptor<unknown> = swi.store;
-      const s = db.createObjectStore(storeDesc.name, {
+      const s = db.createObjectStore(swi.storeName, {
         autoIncrement: storeDesc.autoIncrement,
         keyPath: storeDesc.keyPath,
       });
@@ -117,9 +121,7 @@ export async function openTalerDatabase(
   const metaDb = new DbAccess(metaDbHandle, walletMetadataStore);
   let currentMainVersion: string | undefined;
   await metaDb
-    .mktx((x) => ({
-      metaConfig: x.metaConfig,
-    }))
+    .mktx((stores) => [stores.metaConfig])
     .runReadWrite(async (tx) => {
       const dbVersionRecord = await tx.metaConfig.get(CURRENT_DB_CONFIG_KEY);
       if (!dbVersionRecord) {
@@ -141,9 +143,7 @@ export async function openTalerDatabase(
         // We consider this a pre-release
         // development version, no migration is done.
         await metaDb
-          .mktx((x) => ({
-            metaConfig: x.metaConfig,
-          }))
+          .mktx((stores) => [stores.metaConfig])
           .runReadWrite(async (tx) => {
             await tx.metaConfig.put({
               key: CURRENT_DB_CONFIG_KEY,
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 1052e302..832bbb9a 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1079,7 +1079,7 @@ export interface PurchaseRecord {
   /**
    * Pending refunds for the purchase.  A refund is pending
    * when the merchant reports a transient error from the exchange.
-   * 
+   *
    * FIXME: Put this into a separate object store?
    */
   refunds: { [refundKey: string]: WalletRefundItem };
@@ -1733,7 +1733,8 @@ export interface OperationAttemptLongpollResult {
 
 export const WalletStoresV1 = {
   coins: describeStore(
-    describeContents<CoinRecord>("coins", {
+    "coins",
+    describeContents<CoinRecord>({
       keyPath: "coinPub",
     }),
     {
@@ -1743,17 +1744,20 @@ export const WalletStoresV1 = {
     },
   ),
   reserves: describeStore(
-    describeContents<ReserveRecord>("reserves", {
+    "reserves",
+    describeContents<ReserveRecord>({
       keyPath: "reservePub",
     }),
     {},
   ),
   config: describeStore(
-    describeContents<ConfigRecord>("config", { keyPath: "key" }),
+    "config",
+    describeContents<ConfigRecord>({ keyPath: "key" }),
     {},
   ),
   auditorTrust: describeStore(
-    describeContents<AuditorTrustRecord>("auditorTrust", {
+    "auditorTrust",
+    describeContents<AuditorTrustRecord>({
       keyPath: ["currency", "auditorBaseUrl"],
     }),
     {
@@ -1764,7 +1768,8 @@ export const WalletStoresV1 = {
     },
   ),
   exchangeTrust: describeStore(
-    describeContents<ExchangeTrustRecord>("exchangeTrust", {
+    "exchangeTrust",
+    describeContents<ExchangeTrustRecord>({
       keyPath: ["currency", "exchangeBaseUrl"],
     }),
     {
@@ -1775,7 +1780,8 @@ export const WalletStoresV1 = {
     },
   ),
   denominations: describeStore(
-    describeContents<DenominationRecord>("denominations", {
+    "denominations",
+    describeContents<DenominationRecord>({
       keyPath: ["exchangeBaseUrl", "denomPubHash"],
     }),
     {
@@ -1783,19 +1789,22 @@ export const WalletStoresV1 = {
     },
   ),
   exchanges: describeStore(
-    describeContents<ExchangeRecord>("exchanges", {
+    "exchanges",
+    describeContents<ExchangeRecord>({
       keyPath: "baseUrl",
     }),
     {},
   ),
   exchangeDetails: describeStore(
-    describeContents<ExchangeDetailsRecord>("exchangeDetails", {
+    "exchangeDetails",
+    describeContents<ExchangeDetailsRecord>({
       keyPath: ["exchangeBaseUrl", "currency", "masterPublicKey"],
     }),
     {},
   ),
   proposals: describeStore(
-    describeContents<ProposalRecord>("proposals", { keyPath: "proposalId" }),
+    "proposals",
+    describeContents<ProposalRecord>({ keyPath: "proposalId" }),
     {
       byUrlAndOrderId: describeIndex("byUrlAndOrderId", [
         "merchantBaseUrl",
@@ -1804,7 +1813,8 @@ export const WalletStoresV1 = {
     },
   ),
   refreshGroups: describeStore(
-    describeContents<RefreshGroupRecord>("refreshGroups", {
+    "refreshGroups",
+    describeContents<RefreshGroupRecord>({
       keyPath: "refreshGroupId",
     }),
     {
@@ -1812,13 +1822,15 @@ export const WalletStoresV1 = {
     },
   ),
   recoupGroups: describeStore(
-    describeContents<RecoupGroupRecord>("recoupGroups", {
+    "recoupGroups",
+    describeContents<RecoupGroupRecord>({
       keyPath: "recoupGroupId",
     }),
     {},
   ),
   purchases: describeStore(
-    describeContents<PurchaseRecord>("purchases", { keyPath: "proposalId" }),
+    "purchases",
+    describeContents<PurchaseRecord>({ keyPath: "proposalId" }),
     {
       byFulfillmentUrl: describeIndex(
         "byFulfillmentUrl",
@@ -1831,7 +1843,8 @@ export const WalletStoresV1 = {
     },
   ),
   tips: describeStore(
-    describeContents<TipRecord>("tips", { keyPath: "walletTipId" }),
+    "tips",
+    describeContents<TipRecord>({ keyPath: "walletTipId" }),
     {
       byMerchantTipIdAndBaseUrl: describeIndex("byMerchantTipIdAndBaseUrl", [
         "merchantTipId",
@@ -1840,7 +1853,8 @@ export const WalletStoresV1 = {
     },
   ),
   withdrawalGroups: describeStore(
-    describeContents<WithdrawalGroupRecord>("withdrawalGroups", {
+    "withdrawalGroups",
+    describeContents<WithdrawalGroupRecord>({
       keyPath: "withdrawalGroupId",
     }),
     {
@@ -1853,7 +1867,8 @@ export const WalletStoresV1 = {
     },
   ),
   planchets: describeStore(
-    describeContents<PlanchetRecord>("planchets", { keyPath: "coinPub" }),
+    "planchets",
+    describeContents<PlanchetRecord>({ keyPath: "coinPub" }),
     {
       byGroupAndIndex: describeIndex("byGroupAndIndex", [
         "withdrawalGroupId",
@@ -1864,13 +1879,15 @@ export const WalletStoresV1 = {
     },
   ),
   bankWithdrawUris: describeStore(
-    describeContents<BankWithdrawUriRecord>("bankWithdrawUris", {
+    "bankWithdrawUris",
+    describeContents<BankWithdrawUriRecord>({
       keyPath: "talerWithdrawUri",
     }),
     {},
   ),
   backupProviders: describeStore(
-    describeContents<BackupProviderRecord>("backupProviders", {
+    "backupProviders",
+    describeContents<BackupProviderRecord>({
       keyPath: "baseUrl",
     }),
     {
@@ -1884,7 +1901,8 @@ export const WalletStoresV1 = {
     },
   ),
   depositGroups: describeStore(
-    describeContents<DepositGroupRecord>("depositGroups", {
+    "depositGroups",
+    describeContents<DepositGroupRecord>({
       keyPath: "depositGroupId",
     }),
     {
@@ -1892,29 +1910,34 @@ export const WalletStoresV1 = {
     },
   ),
   tombstones: describeStore(
-    describeContents<TombstoneRecord>("tombstones", { keyPath: "id" }),
+    "tombstones",
+    describeContents<TombstoneRecord>({ keyPath: "id" }),
     {},
   ),
   operationRetries: describeStore(
-    describeContents<OperationRetryRecord>("operationRetries", {
+    "operationRetries",
+    describeContents<OperationRetryRecord>({
       keyPath: "id",
     }),
     {},
   ),
   ghostDepositGroups: describeStore(
-    describeContents<GhostDepositGroupRecord>("ghostDepositGroups", {
+    "ghostDepositGroups",
+    describeContents<GhostDepositGroupRecord>({
       keyPath: "contractTermsHash",
     }),
     {},
   ),
   balancesPerCurrency: describeStore(
-    describeContents<BalancePerCurrencyRecord>("balancesPerCurrency", {
+    "balancesPerCurrency",
+    describeContents<BalancePerCurrencyRecord>({
       keyPath: "currency",
     }),
     {},
   ),
   peerPushPaymentIncoming: describeStore(
-    describeContents<PeerPushPaymentIncomingRecord>("peerPushPaymentIncoming", 
{
+    "peerPushPaymentIncoming",
+    describeContents<PeerPushPaymentIncomingRecord>({
       keyPath: "peerPushPaymentIncomingId",
     }),
     {
@@ -1925,7 +1948,8 @@ export const WalletStoresV1 = {
     },
   ),
   peerPullPaymentIncoming: describeStore(
-    describeContents<PeerPullPaymentIncomingRecord>("peerPullPaymentIncoming", 
{
+    "peerPullPaymentIncoming",
+    describeContents<PeerPullPaymentIncomingRecord>({
       keyPath: "peerPullPaymentIncomingId",
     }),
     {
@@ -1936,21 +1960,17 @@ export const WalletStoresV1 = {
     },
   ),
   peerPullPaymentInitiations: describeStore(
-    describeContents<PeerPullPaymentInitiationRecord>(
-      "peerPullPaymentInitiations",
-      {
-        keyPath: "pursePub",
-      },
-    ),
+    "peerPullPaymentInitiations",
+    describeContents<PeerPullPaymentInitiationRecord>({
+      keyPath: "pursePub",
+    }),
     {},
   ),
   peerPushPaymentInitiations: describeStore(
-    describeContents<PeerPushPaymentInitiationRecord>(
-      "peerPushPaymentInitiations",
-      {
-        keyPath: "pursePub",
-      },
-    ),
+    "peerPushPaymentInitiations",
+    describeContents<PeerPushPaymentInitiationRecord>({
+      keyPath: "pursePub",
+    }),
     {},
   ),
 };
@@ -1962,7 +1982,8 @@ export interface MetaConfigRecord {
 
 export const walletMetadataStore = {
   metaConfig: describeStore(
-    describeContents<MetaConfigRecord>("metaConfig", { keyPath: "key" }),
+    "metaConfig",
+    describeContents<MetaConfigRecord>({ keyPath: "key" }),
     {},
   ),
 };
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts 
b/packages/taler-wallet-core/src/operations/backup/export.ts
index c77ce1a8..fb1fbf90 100644
--- a/packages/taler-wallet-core/src/operations/backup/export.ts
+++ b/packages/taler-wallet-core/src/operations/backup/export.ts
@@ -76,20 +76,20 @@ export async function exportBackup(
 ): Promise<WalletBackupContentV1> {
   await provideBackupState(ws);
   return ws.db
-    .mktx((x) => ({
-      config: x.config,
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      coins: x.coins,
-      denominations: x.denominations,
-      purchases: x.purchases,
-      proposals: x.proposals,
-      refreshGroups: x.refreshGroups,
-      backupProviders: x.backupProviders,
-      tips: x.tips,
-      recoupGroups: x.recoupGroups,
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [
+      x.config,
+      x.exchanges,
+      x.exchangeDetails,
+      x.coins,
+      x.denominations,
+      x.purchases,
+      x.proposals,
+      x.refreshGroups,
+      x.backupProviders,
+      x.tips,
+      x.recoupGroups,
+      x.withdrawalGroups,
+    ])
     .runReadWrite(async (tx) => {
       const bs = await getWalletBackupState(ws, tx);
 
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts 
b/packages/taler-wallet-core/src/operations/backup/import.ts
index e8683265..8f5d019d 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -224,22 +224,22 @@ export async function importBackup(
   logger.info(`importing backup ${j2s(backupBlobArg)}`);
 
   return ws.db
-    .mktx((x) => ({
-      config: x.config,
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      coins: x.coins,
-      denominations: x.denominations,
-      purchases: x.purchases,
-      proposals: x.proposals,
-      refreshGroups: x.refreshGroups,
-      backupProviders: x.backupProviders,
-      tips: x.tips,
-      recoupGroups: x.recoupGroups,
-      withdrawalGroups: x.withdrawalGroups,
-      tombstones: x.tombstones,
-      depositGroups: x.depositGroups,
-    }))
+    .mktx((x) => [
+      x.config,
+      x.exchangeDetails,
+      x.exchanges,
+      x.coins,
+      x.denominations,
+      x.purchases,
+      x.proposals,
+      x.refreshGroups,
+      x.backupProviders,
+      x.tips,
+      x.recoupGroups,
+      x.withdrawalGroups,
+      x.tombstones,
+      x.depositGroups,
+    ])
     .runReadWrite(async (tx) => {
       // FIXME: validate schema!
       const backupBlob = backupBlobArg as WalletBackupContentV1;
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts 
b/packages/taler-wallet-core/src/operations/backup/index.ts
index 56871104..db003af8 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -264,7 +264,7 @@ async function runBackupCycleForProvider(
   args: BackupForProviderArgs,
 ): Promise<OperationAttemptResult> {
   const provider = await ws.db
-    .mktx((x) => ({ backupProviders: x.backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadOnly(async (tx) => {
       return tx.backupProviders.get(args.backupProviderBaseUrl);
     });
@@ -322,9 +322,9 @@ async function runBackupCycleForProvider(
 
   if (resp.status === HttpStatusCode.NotModified) {
     await ws.db
-      .mktx((x) => ({ backupProvider: x.backupProviders }))
+      .mktx((x) => [x.backupProviders])
       .runReadWrite(async (tx) => {
-        const prov = await tx.backupProvider.get(provider.baseUrl);
+        const prov = await tx.backupProviders.get(provider.baseUrl);
         if (!prov) {
           return;
         }
@@ -333,7 +333,7 @@ async function runBackupCycleForProvider(
           tag: BackupProviderStateTag.Ready,
           nextBackupTimestamp: getNextBackupTimestamp(),
         };
-        await tx.backupProvider.put(prov);
+        await tx.backupProviders.put(prov);
       });
     return {
       type: OperationAttemptResultType.Finished,
@@ -367,10 +367,7 @@ async function runBackupCycleForProvider(
     // FIXME: check if the provider is overcharging us!
 
     await ws.db
-      .mktx((x) => ({
-        backupProviders: x.backupProviders,
-        operationRetries: x.operationRetries,
-      }))
+      .mktx((x) => [x.backupProviders, x.operationRetries])
       .runReadWrite(async (tx) => {
         const provRec = await tx.backupProviders.get(provider.baseUrl);
         checkDbInvariant(!!provRec);
@@ -407,7 +404,7 @@ async function runBackupCycleForProvider(
 
   if (resp.status === HttpStatusCode.NoContent) {
     await ws.db
-      .mktx((x) => ({ backupProviders: x.backupProviders }))
+      .mktx((x) => [x.backupProviders])
       .runReadWrite(async (tx) => {
         const prov = await tx.backupProviders.get(provider.baseUrl);
         if (!prov) {
@@ -435,12 +432,9 @@ async function runBackupCycleForProvider(
     const cryptoData = await computeBackupCryptoData(ws.cryptoApi, blob);
     await importBackup(ws, blob, cryptoData);
     await ws.db
-      .mktx((x) => ({
-        backupProvider: x.backupProviders,
-        operationRetries: x.operationRetries,
-      }))
+      .mktx((x) => [x.backupProviders, x.operationRetries])
       .runReadWrite(async (tx) => {
-        const prov = await tx.backupProvider.get(provider.baseUrl);
+        const prov = await tx.backupProviders.get(provider.baseUrl);
         if (!prov) {
           logger.warn("backup provider not found anymore");
           return;
@@ -453,7 +447,7 @@ async function runBackupCycleForProvider(
         prov.state = {
           tag: BackupProviderStateTag.Retrying,
         };
-        await tx.backupProvider.put(prov);
+        await tx.backupProviders.put(prov);
       });
     logger.info("processed existing backup");
     // Now upload our own, merged backup.
@@ -480,7 +474,7 @@ export async function processBackupForProvider(
   backupProviderBaseUrl: string,
 ): Promise<OperationAttemptResult> {
   const provider = await ws.db
-    .mktx((x) => ({ backupProviders: x.backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadOnly(async (tx) => {
       return await tx.backupProviders.get(backupProviderBaseUrl);
     });
@@ -509,7 +503,7 @@ export async function removeBackupProvider(
   req: RemoveBackupProviderRequest,
 ): Promise<void> {
   await ws.db
-    .mktx(({ backupProviders }) => ({ backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadWrite(async (tx) => {
       await tx.backupProviders.delete(req.provider);
     });
@@ -539,7 +533,7 @@ export async function runBackupCycle(
   req: RunBackupCycleRequest,
 ): Promise<void> {
   const providers = await ws.db
-    .mktx((x) => ({ backupProviders: x.backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadOnly(async (tx) => {
       if (req.providers) {
         const rs = await Promise.all(
@@ -605,7 +599,7 @@ export async function addBackupProvider(
   await provideBackupState(ws);
   const canonUrl = canonicalizeBaseUrl(req.backupProviderBaseUrl);
   await ws.db
-    .mktx((x) => ({ backupProviders: x.backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadWrite(async (tx) => {
       const oldProv = await tx.backupProviders.get(canonUrl);
       if (oldProv) {
@@ -628,7 +622,7 @@ export async function addBackupProvider(
     codecForSyncTermsOfServiceResponse(),
   );
   await ws.db
-    .mktx((x) => ({ backupProviders: x.backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadWrite(async (tx) => {
       let state: BackupProviderState;
       if (req.activate) {
@@ -807,10 +801,7 @@ export async function getBackupInfo(
 ): Promise<BackupInfo> {
   const backupConfig = await provideBackupState(ws);
   const providerRecords = await ws.db
-    .mktx((x) => ({
-      backupProviders: x.backupProviders,
-      operationRetries: x.operationRetries,
-    }))
+    .mktx((x) => [x.backupProviders, x.operationRetries])
     .runReadOnly(async (tx) => {
       return await tx.backupProviders.iter().mapAsync(async (bp) => {
         const opId = RetryTags.forBackup(bp);
@@ -853,7 +844,7 @@ export async function getBackupRecovery(
 ): Promise<BackupRecovery> {
   const bs = await provideBackupState(ws);
   const providers = await ws.db
-    .mktx((x) => ({ backupProviders: x.backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadOnly(async (tx) => {
       return await tx.backupProviders.iter().toArray();
     });
@@ -874,7 +865,7 @@ async function backupRecoveryTheirs(
   br: BackupRecovery,
 ) {
   await ws.db
-    .mktx((x) => ({ config: x.config, backupProviders: x.backupProviders }))
+    .mktx((x) => [x.config, x.backupProviders])
     .runReadWrite(async (tx) => {
       let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
         WALLET_BACKUP_STATE_KEY,
@@ -924,7 +915,7 @@ export async function loadBackupRecovery(
 ): Promise<void> {
   const bs = await provideBackupState(ws);
   const providers = await ws.db
-    .mktx((x) => ({ backupProviders: x.backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadOnly(async (tx) => {
       return await tx.backupProviders.iter().toArray();
     });
@@ -954,7 +945,7 @@ export async function exportBackupEncrypted(
   await provideBackupState(ws);
   const blob = await exportBackup(ws);
   const bs = await ws.db
-    .mktx((x) => ({ config: x.config }))
+    .mktx((x) => [x.config])
     .runReadOnly(async (tx) => {
       return await getWalletBackupState(ws, tx);
     });
diff --git a/packages/taler-wallet-core/src/operations/backup/state.ts 
b/packages/taler-wallet-core/src/operations/backup/state.ts
index 293f5613..2efd9be8 100644
--- a/packages/taler-wallet-core/src/operations/backup/state.ts
+++ b/packages/taler-wallet-core/src/operations/backup/state.ts
@@ -29,9 +29,7 @@ export async function provideBackupState(
   ws: InternalWalletState,
 ): Promise<WalletBackupConfState> {
   const bs: ConfigRecord | undefined = await ws.db
-    .mktx((x) => ({
-      config: x.config,
-    }))
+    .mktx((stores) => [stores.config])
     .runReadOnly(async (tx) => {
       return await tx.config.get(WALLET_BACKUP_STATE_KEY);
     });
@@ -47,9 +45,7 @@ export async function provideBackupState(
   // and be based on hostname
   const deviceId = `wallet-core-${encodeCrock(d)}`;
   return await ws.db
-    .mktx((x) => ({
-      config: x.config,
-    }))
+    .mktx((x) => [x.config])
     .runReadWrite(async (tx) => {
       let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
         WALLET_BACKUP_STATE_KEY,
@@ -87,9 +83,7 @@ export async function setWalletDeviceId(
 ): Promise<void> {
   await provideBackupState(ws);
   await ws.db
-    .mktx((x) => ({
-      config: x.config,
-    }))
+    .mktx((x) => [x.config])
     .runReadWrite(async (tx) => {
       let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
         WALLET_BACKUP_STATE_KEY,
diff --git a/packages/taler-wallet-core/src/operations/balance.ts 
b/packages/taler-wallet-core/src/operations/balance.ts
index 4590f505..44357fdf 100644
--- a/packages/taler-wallet-core/src/operations/balance.ts
+++ b/packages/taler-wallet-core/src/operations/balance.ts
@@ -139,12 +139,7 @@ export async function getBalances(
   logger.trace("starting to compute balance");
 
   const wbal = await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      refreshGroups: x.refreshGroups,
-      purchases: x.purchases,
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.coins, x.refreshGroups, x.purchases, x.withdrawalGroups])
     .runReadOnly(async (tx) => {
       return getBalancesInsideTransaction(ws, tx);
     });
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index 6eed12a3..5838be76 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -83,9 +83,7 @@ export async function processDepositGroup(
   } = {},
 ): Promise<OperationAttemptResult> {
   const depositGroup = await ws.db
-    .mktx((x) => ({
-      depositGroups: x.depositGroups,
-    }))
+    .mktx((x) => [x.depositGroups])
     .runReadOnly(async (tx) => {
       return tx.depositGroups.get(depositGroupId);
     });
@@ -141,7 +139,7 @@ export async function processDepositGroup(
     });
     await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
     await ws.db
-      .mktx((x) => ({ depositGroups: x.depositGroups }))
+      .mktx((x) => [x.depositGroups])
       .runReadWrite(async (tx) => {
         const dg = await tx.depositGroups.get(depositGroupId);
         if (!dg) {
@@ -153,9 +151,7 @@ export async function processDepositGroup(
   }
 
   await ws.db
-    .mktx((x) => ({
-      depositGroups: x.depositGroups,
-    }))
+    .mktx((x) => [x.depositGroups])
     .runReadWrite(async (tx) => {
       const dg = await tx.depositGroups.get(depositGroupId);
       if (!dg) {
@@ -185,9 +181,7 @@ export async function trackDepositGroup(
     body: any;
   }[] = [];
   const depositGroup = await ws.db
-    .mktx((x) => ({
-      depositGroups: x.depositGroups,
-    }))
+    .mktx((x) => [x.depositGroups])
     .runReadOnly(async (tx) => {
       return tx.depositGroups.get(req.depositGroupId);
     });
@@ -247,10 +241,7 @@ export async function getFeeForDeposit(
   const exchangeInfos: { url: string; master_pub: string }[] = [];
 
   await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadOnly(async (tx) => {
       const allExchanges = await tx.exchanges.iter().toArray();
       for (const e of allExchanges) {
@@ -315,10 +306,7 @@ export async function prepareDepositGroup(
   const exchangeInfos: { url: string; master_pub: string }[] = [];
 
   await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadOnly(async (tx) => {
       const allExchanges = await tx.exchanges.iter().toArray();
       for (const e of allExchanges) {
@@ -417,10 +405,7 @@ export async function createDepositGroup(
   const exchangeInfos: { url: string; master_pub: string }[] = [];
 
   await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadOnly(async (tx) => {
       const allExchanges = await tx.exchanges.iter().toArray();
       for (const e of allExchanges) {
@@ -532,12 +517,13 @@ export async function createDepositGroup(
   };
 
   await ws.db
-    .mktx((x) => ({
-      depositGroups: x.depositGroups,
-      coins: x.coins,
-      refreshGroups: x.refreshGroups,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [
+      x.depositGroups,
+      x.coins,
+      x.recoupGroups,
+      x.denominations,
+      x.refreshGroups,
+    ])
     .runReadWrite(async (tx) => {
       await applyCoinSpend(
         ws,
@@ -565,12 +551,7 @@ export async function getEffectiveDepositAmount(
   const exchangeSet: Set<string> = new Set();
 
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      denominations: x.denominations,
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.coins, x.denominations, x.exchanges, x.exchangeDetails])
     .runReadOnly(async (tx) => {
       for (let i = 0; i < pcs.coinPubs.length; i++) {
         const coin = await tx.coins.get(pcs.coinPubs[i]);
@@ -637,12 +618,7 @@ export async function getTotalFeesForDepositAmount(
   const exchangeSet: Set<string> = new Set();
 
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      denominations: x.denominations,
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.coins, x.denominations, x.exchanges, x.exchangeDetails])
     .runReadOnly(async (tx) => {
       for (let i = 0; i < pcs.coinPubs.length; i++) {
         const coin = await tx.coins.get(pcs.coinPubs[i]);
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 1021da6b..50497844 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -161,10 +161,7 @@ export async function getExchangeDetails(
 }
 
 getExchangeDetails.makeContext = (db: DbAccess<typeof WalletStoresV1>) =>
-  db.mktx((x) => ({
-    exchanges: x.exchanges,
-    exchangeDetails: x.exchangeDetails,
-  }));
+  db.mktx((x) => [x.exchanges, x.exchangeDetails]);
 
 export async function updateExchangeTermsOfService(
   ws: InternalWalletState,
@@ -172,10 +169,7 @@ export async function updateExchangeTermsOfService(
   tos: ExchangeTosDownloadResult,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadWrite(async (tx) => {
       const d = await getExchangeDetails(tx, exchangeBaseUrl);
       if (d) {
@@ -193,10 +187,7 @@ export async function acceptExchangeTermsOfService(
   etag: string | undefined,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadWrite(async (tx) => {
       const d = await getExchangeDetails(tx, exchangeBaseUrl);
       if (d) {
@@ -326,10 +317,7 @@ async function provideExchangeRecord(
   exchangeDetails: ExchangeDetailsRecord | undefined;
 }> {
   return await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadWrite(async (tx) => {
       let exchange = await tx.exchanges.get(baseUrl);
       if (!exchange) {
@@ -569,14 +557,14 @@ export async function updateExchangeFromUrlHandler(
   logger.trace("updating exchange info in database");
 
   const updated = await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      denominations: x.denominations,
-      coins: x.coins,
-      refreshGroups: x.refreshGroups,
-      recoupGroups: x.recoupGroups,
-    }))
+    .mktx((x) => [
+      x.exchanges,
+      x.exchangeDetails,
+      x.denominations,
+      x.coins,
+      x.refreshGroups,
+      x.recoupGroups,
+    ])
     .runReadWrite(async (tx) => {
       const r = await tx.exchanges.get(baseUrl);
       if (!r) {
@@ -770,12 +758,12 @@ export async function getExchangeTrust(
   let isAudited = false;
 
   return await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      exchangesTrustStore: x.exchangeTrust,
-      auditorTrust: x.auditorTrust,
-    }))
+    .mktx((x) => [
+      x.exchanges,
+      x.exchangeDetails,
+      x.exchangeTrust,
+      x.auditorTrust,
+    ])
     .runReadOnly(async (tx) => {
       const exchangeDetails = await getExchangeDetails(
         tx,
@@ -786,7 +774,7 @@ export async function getExchangeTrust(
         throw Error(`exchange ${exchangeInfo.baseUrl} details not available`);
       }
       const exchangeTrustRecord =
-        await tx.exchangesTrustStore.indexes.byExchangeMasterPub.get(
+        await tx.exchangeTrust.indexes.byExchangeMasterPub.get(
           exchangeDetails.masterPublicKey,
         );
       if (
diff --git a/packages/taler-wallet-core/src/operations/pay.ts 
b/packages/taler-wallet-core/src/operations/pay.ts
index 9e63cd51..322e9048 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -120,7 +120,7 @@ export async function getTotalPaymentCost(
   pcs: PayCoinSelection,
 ): Promise<AmountJson> {
   return ws.db
-    .mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
+    .mktx((x) => [x.coins, x.denominations])
     .runReadOnly(async (tx) => {
       const costs: AmountJson[] = [];
       for (let i = 0; i < pcs.coinPubs.length; i++) {
@@ -222,12 +222,7 @@ export async function getCandidatePayCoins(
   const wireFeesPerExchange: Record<string, AmountJson> = {};
 
   await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      denominations: x.denominations,
-      coins: x.coins,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations, x.coins])
     .runReadOnly(async (tx) => {
       const exchanges = await tx.exchanges.iter().toArray();
       for (const exchange of exchanges) {
@@ -459,13 +454,13 @@ async function recordConfirmPay(
   };
 
   await ws.db
-    .mktx((x) => ({
-      proposals: x.proposals,
-      purchases: x.purchases,
-      coins: x.coins,
-      refreshGroups: x.refreshGroups,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [
+      x.proposals,
+      x.purchases,
+      x.coins,
+      x.refreshGroups,
+      x.denominations,
+    ])
     .runReadWrite(async (tx) => {
       const p = await tx.proposals.get(proposal.proposalId);
       if (p) {
@@ -489,7 +484,7 @@ async function failProposalPermanently(
   err: TalerErrorDetail,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({ proposals: x.proposals }))
+    .mktx((x) => [x.proposals])
     .runReadWrite(async (tx) => {
       const p = await tx.proposals.get(proposalId);
       if (!p) {
@@ -567,13 +562,10 @@ export async function processDownloadProposal(
   proposalId: string,
   options: {} = {},
 ): Promise<OperationAttemptResult> {
-
-  const res = ws.db.mktx2((x) => [x.auditorTrust, x.coins])
-
   const proposal = await ws.db
-    .mktx((x) => ({ proposals: x.proposals }))
+    .mktx((x) => [x.proposals])
     .runReadOnly(async (tx) => {
-      return tx.proposals.get(proposalId);
+      return await tx.proposals.get(proposalId);
     });
 
   if (!proposal) {
@@ -608,7 +600,7 @@ export async function processDownloadProposal(
 
   const opId = RetryTags.forProposalClaim(proposal);
   const retryRecord = await ws.db
-    .mktx((x) => ({ operationRetries: x.operationRetries }))
+    .mktx((x) => [x.operationRetries])
     .runReadOnly(async (tx) => {
       return tx.operationRetries.get(opId);
     });
@@ -748,7 +740,7 @@ export async function processDownloadProposal(
   logger.trace(`extracted contract data: ${j2s(contractData)}`);
 
   await ws.db
-    .mktx((x) => ({ proposals: x.proposals, purchases: x.purchases }))
+    .mktx((x) => [x.purchases, x.proposals])
     .runReadWrite(async (tx) => {
       const p = await tx.proposals.get(proposalId);
       if (!p) {
@@ -807,7 +799,7 @@ async function startDownloadProposal(
   noncePriv: string | undefined,
 ): Promise<string> {
   const oldProposal = await ws.db
-    .mktx((x) => ({ proposals: x.proposals }))
+    .mktx((x) => [x.proposals])
     .runReadOnly(async (tx) => {
       return tx.proposals.indexes.byUrlAndOrderId.get([
         merchantBaseUrl,
@@ -855,7 +847,7 @@ async function startDownloadProposal(
   };
 
   await ws.db
-    .mktx((x) => ({ proposals: x.proposals }))
+    .mktx((x) => [x.proposals])
     .runReadWrite(async (tx) => {
       const existingRecord = await tx.proposals.indexes.byUrlAndOrderId.get([
         merchantBaseUrl,
@@ -880,7 +872,7 @@ async function storeFirstPaySuccess(
 ): Promise<void> {
   const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
   await ws.db
-    .mktx((x) => ({ purchases: x.purchases }))
+    .mktx((x) => [x.purchases])
     .runReadWrite(async (tx) => {
       const purchase = await tx.purchases.get(proposalId);
 
@@ -916,7 +908,7 @@ async function storePayReplaySuccess(
   sessionId: string | undefined,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({ purchases: x.purchases }))
+    .mktx((x) => [x.purchases])
     .runReadWrite(async (tx) => {
       const purchase = await tx.purchases.get(proposalId);
 
@@ -950,9 +942,9 @@ async function handleInsufficientFunds(
   logger.trace("handling insufficient funds, trying to re-select coins");
 
   const proposal = await ws.db
-    .mktx((x) => ({ purchaes: x.purchases }))
+    .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
-      return tx.purchaes.get(proposalId);
+      return tx.purchases.get(proposalId);
     });
   if (!proposal) {
     return;
@@ -990,7 +982,7 @@ async function handleInsufficientFunds(
   const prevPayCoins: PreviousPayCoins = [];
 
   await ws.db
-    .mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
+    .mktx((x) => [x.coins, x.denominations])
     .runReadOnly(async (tx) => {
       for (let i = 0; i < proposal.payCoinSelection.coinPubs.length; i++) {
         const coinPub = proposal.payCoinSelection.coinPubs[i];
@@ -1036,12 +1028,7 @@ async function handleInsufficientFunds(
   logger.trace("re-selected coins");
 
   await ws.db
-    .mktx((x) => ({
-      purchases: x.purchases,
-      coins: x.coins,
-      denominations: x.denominations,
-      refreshGroups: x.refreshGroups,
-    }))
+    .mktx((x) => [x.purchases, x.coins, x.denominations, x.refreshGroups])
     .runReadWrite(async (tx) => {
       const p = await tx.purchases.get(proposalId);
       if (!p) {
@@ -1060,7 +1047,7 @@ async function unblockBackup(
   proposalId: string,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({ backupProviders: x.backupProviders }))
+    .mktx((x) => [x.backupProviders])
     .runReadWrite(async (tx) => {
       await tx.backupProviders.indexes.byPaymentProposalId
         .iter(proposalId)
@@ -1081,7 +1068,7 @@ export async function checkPaymentByProposalId(
   sessionId?: string,
 ): Promise<PreparePayResult> {
   let proposal = await ws.db
-    .mktx((x) => ({ proposals: x.proposals }))
+    .mktx((x) => [x.proposals])
     .runReadOnly(async (tx) => {
       return tx.proposals.get(proposalId);
     });
@@ -1095,7 +1082,7 @@ export async function checkPaymentByProposalId(
     }
     logger.trace("using existing purchase for same product");
     proposal = await ws.db
-      .mktx((x) => ({ proposals: x.proposals }))
+      .mktx((x) => [x.proposals])
       .runReadOnly(async (tx) => {
         return tx.proposals.get(existingProposalId);
       });
@@ -1118,7 +1105,7 @@ export async function checkPaymentByProposalId(
 
   // First check if we already paid for it.
   const purchase = await ws.db
-    .mktx((x) => ({ purchases: x.purchases }))
+    .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
       return tx.purchases.get(proposalId);
     });
@@ -1176,7 +1163,7 @@ export async function checkPaymentByProposalId(
       "automatically re-submitting payment with different session ID",
     );
     await ws.db
-      .mktx((x) => ({ purchases: x.purchases }))
+      .mktx((x) => [x.purchases])
       .runReadWrite(async (tx) => {
         const p = await tx.purchases.get(proposalId);
         if (!p) {
@@ -1230,7 +1217,7 @@ export async function getContractTermsDetails(
   proposalId: string,
 ): Promise<WalletContractData> {
   const proposal = await ws.db
-    .mktx((x) => ({ proposals: x.proposals }))
+    .mktx((x) => [x.proposals])
     .runReadOnly(async (tx) => {
       return tx.proposals.get(proposalId);
     });
@@ -1296,7 +1283,7 @@ export async function generateDepositPermissions(
     denom: DenominationRecord;
   }> = [];
   await ws.db
-    .mktx((x) => ({ coins: x.coins, denominations: x.denominations }))
+    .mktx((x) => [x.coins, x.denominations])
     .runReadOnly(async (tx) => {
       for (let i = 0; i < payCoinSel.coinPubs.length; i++) {
         const coin = await tx.coins.get(payCoinSel.coinPubs[i]);
@@ -1359,7 +1346,7 @@ export async function runPayForConfirmPay(
   switch (res.type) {
     case OperationAttemptResultType.Finished: {
       const purchase = await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
+        .mktx((x) => [x.purchases])
         .runReadOnly(async (tx) => {
           return tx.purchases.get(proposalId);
         });
@@ -1399,7 +1386,7 @@ export async function confirmPay(
     `executing confirmPay with proposalId ${proposalId} and sessionIdOverride 
${sessionIdOverride}`,
   );
   const proposal = await ws.db
-    .mktx((x) => ({ proposals: x.proposals }))
+    .mktx((x) => [x.proposals])
     .runReadOnly(async (tx) => {
       return tx.proposals.get(proposalId);
     });
@@ -1414,7 +1401,7 @@ export async function confirmPay(
   }
 
   const existingPurchase = await ws.db
-    .mktx((x) => ({ purchases: x.purchases }))
+    .mktx((x) => [x.purchases])
     .runReadWrite(async (tx) => {
       const purchase = await tx.purchases.get(proposalId);
       if (
@@ -1508,7 +1495,7 @@ export async function processPurchasePay(
   } = {},
 ): Promise<OperationAttemptResult> {
   const purchase = await ws.db
-    .mktx((x) => ({ purchases: x.purchases }))
+    .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
       return tx.purchases.get(proposalId);
     });
@@ -1568,20 +1555,12 @@ export async function processPurchasePay(
     );
 
     logger.trace(`got resp ${JSON.stringify(resp)}`);
-
-    const payOpId = RetryTags.forPay(purchase);
-    const payRetryRecord = await ws.db
-      .mktx((x) => ({ operationRetries: x.operationRetries }))
-      .runReadOnly(async (tx) => {
-        return await tx.operationRetries.get(payOpId);
-      });
-
     if (resp.status === HttpStatusCode.BadRequest) {
       const errDetails = await readUnexpectedResponseDetails(resp);
       logger.warn("unexpected 400 response for /pay");
       logger.warn(j2s(errDetails));
       await ws.db
-        .mktx((x) => ({ purchases: x.purchases }))
+        .mktx((x) => [x.purchases])
         .runReadWrite(async (tx) => {
           const purch = await tx.purchases.get(proposalId);
           if (!purch) {
@@ -1683,7 +1662,7 @@ export async function refuseProposal(
   proposalId: string,
 ): Promise<void> {
   const success = await ws.db
-    .mktx((x) => ({ proposals: x.proposals }))
+    .mktx((x) => [x.proposals])
     .runReadWrite(async (tx) => {
       const proposal = await tx.proposals.get(proposalId);
       if (!proposal) {
diff --git a/packages/taler-wallet-core/src/operations/peer-to-peer.ts 
b/packages/taler-wallet-core/src/operations/peer-to-peer.ts
index 9ce460ad..59dad3d5 100644
--- a/packages/taler-wallet-core/src/operations/peer-to-peer.ts
+++ b/packages/taler-wallet-core/src/operations/peer-to-peer.ts
@@ -242,13 +242,14 @@ export async function initiatePeerToPeerPush(
   });
 
   const coinSelRes: PeerCoinSelection | undefined = await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      coins: x.coins,
-      denominations: x.denominations,
-      refreshGroups: x.refreshGroups,
-      peerPushPaymentInitiations: x.peerPushPaymentInitiations,
-    }))
+    .mktx((x) => [
+      x.exchanges,
+      x.coins,
+      x.denominations,
+      x.refreshGroups,
+      x.peerPullPaymentInitiations,
+      x.peerPushPaymentInitiations,
+    ])
     .runReadWrite(async (tx) => {
       const sel = await selectPeerCoins(ws, tx, instructedAmount);
       if (!sel) {
@@ -401,9 +402,7 @@ export async function checkPeerPushPayment(
   const peerPushPaymentIncomingId = encodeCrock(getRandomBytes(32));
 
   await ws.db
-    .mktx((x) => ({
-      peerPushPaymentIncoming: x.peerPushPaymentIncoming,
-    }))
+    .mktx((x) => [x.peerPushPaymentIncoming])
     .runReadWrite(async (tx) => {
       await tx.peerPushPaymentIncoming.add({
         peerPushPaymentIncomingId,
@@ -456,10 +455,7 @@ async function getMergeReserveInfo(
   const newReservePair = await ws.cryptoApi.createEddsaKeypair({});
 
   const mergeReserveInfo: MergeReserveInfo = await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.exchanges, x.withdrawalGroups])
     .runReadWrite(async (tx) => {
       const ex = await tx.exchanges.get(req.exchangeBaseUrl);
       checkDbInvariant(!!ex);
@@ -482,7 +478,7 @@ export async function acceptPeerPushPayment(
   req: AcceptPeerPushPaymentRequest,
 ): Promise<void> {
   const peerInc = await ws.db
-    .mktx((x) => ({ peerPushPaymentIncoming: x.peerPushPaymentIncoming }))
+    .mktx((x) => [x.peerPushPaymentIncoming])
     .runReadOnly(async (tx) => {
       return tx.peerPushPaymentIncoming.get(req.peerPushPaymentIncomingId);
     });
@@ -564,7 +560,7 @@ export async function acceptPeerPullPayment(
   req: AcceptPeerPullPaymentRequest,
 ): Promise<void> {
   const peerPullInc = await ws.db
-    .mktx((x) => ({ peerPullPaymentIncoming: x.peerPullPaymentIncoming }))
+    .mktx((x) => [x.peerPullPaymentIncoming])
     .runReadOnly(async (tx) => {
       return tx.peerPullPaymentIncoming.get(req.peerPullPaymentIncomingId);
     });
@@ -579,13 +575,13 @@ export async function acceptPeerPullPayment(
     peerPullInc.contractTerms.amount,
   );
   const coinSelRes: PeerCoinSelection | undefined = await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      coins: x.coins,
-      denominations: x.denominations,
-      refreshGroups: x.refreshGroups,
-      peerPullPaymentIncoming: x.peerPullPaymentIncoming,
-    }))
+    .mktx((x) => [
+      x.exchanges,
+      x.coins,
+      x.denominations,
+      x.refreshGroups,
+      x.peerPullPaymentIncoming,
+    ])
     .runReadWrite(async (tx) => {
       const sel = await selectPeerCoins(ws, tx, instructedAmount);
       if (!sel) {
@@ -689,9 +685,7 @@ export async function checkPeerPullPayment(
   const peerPullPaymentIncomingId = encodeCrock(getRandomBytes(32));
 
   await ws.db
-    .mktx((x) => ({
-      peerPullPaymentIncoming: x.peerPullPaymentIncoming,
-    }))
+    .mktx((x) => [x.peerPullPaymentIncoming])
     .runReadWrite(async (tx) => {
       await tx.peerPullPaymentIncoming.add({
         peerPullPaymentIncomingId,
@@ -775,11 +769,9 @@ export async function initiatePeerRequestForPay(
   });
 
   await ws.db
-    .mktx((x) => ({
-      peerPullPaymentInitiation: x.peerPullPaymentInitiations,
-    }))
+    .mktx((x) => [x.peerPullPaymentInitiations])
     .runReadWrite(async (tx) => {
-      await tx.peerPullPaymentInitiation.put({
+      await tx.peerPullPaymentInitiations.put({
         amount: req.amount,
         contractTerms,
         exchangeBaseUrl: req.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/operations/pending.ts 
b/packages/taler-wallet-core/src/operations/pending.ts
index 7d5a5bfd..9ba532ab 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -357,21 +357,21 @@ export async function getPendingOperations(
 ): Promise<PendingOperationsResponse> {
   const now = AbsoluteTime.now();
   return await ws.db
-    .mktx((x) => ({
-      backupProviders: x.backupProviders,
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      refreshGroups: x.refreshGroups,
-      coins: x.coins,
-      withdrawalGroups: x.withdrawalGroups,
-      proposals: x.proposals,
-      tips: x.tips,
-      purchases: x.purchases,
-      planchets: x.planchets,
-      depositGroups: x.depositGroups,
-      recoupGroups: x.recoupGroups,
-      operationRetries: x.operationRetries,
-    }))
+    .mktx((x) => [
+      x.backupProviders,
+      x.exchanges,
+      x.exchangeDetails,
+      x.refreshGroups,
+      x.coins,
+      x.withdrawalGroups,
+      x.proposals,
+      x.tips,
+      x.purchases,
+      x.planchets,
+      x.depositGroups,
+      x.recoupGroups,
+      x.operationRetries,
+    ])
     .runReadWrite(async (tx) => {
       const resp: PendingOperationsResponse = {
         pendingOperations: [],
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts 
b/packages/taler-wallet-core/src/operations/recoup.ts
index 387c23f4..100bbc07 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -96,12 +96,12 @@ async function recoupTipCoin(
   // Thus we just put the coin to sleep.
   // FIXME: somehow report this to the user
   await ws.db
-    .mktx((x) => ({
-      recoupGroups: x.recoupGroups,
-      denominations: WalletStoresV1.denominations,
-      refreshGroups: WalletStoresV1.refreshGroups,
-      coins: WalletStoresV1.coins,
-    }))
+    .mktx((stores) => [
+      stores.recoupGroups,
+      stores.denominations,
+      stores.refreshGroups,
+      stores.coins,
+    ])
     .runReadWrite(async (tx) => {
       const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
       if (!recoupGroup) {
@@ -123,9 +123,7 @@ async function recoupWithdrawCoin(
 ): Promise<void> {
   const reservePub = cs.reservePub;
   const denomInfo = await ws.db
-    .mktx((x) => ({
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.denominations])
     .runReadOnly(async (tx) => {
       const denomInfo = await ws.getDenomInfo(
         ws,
@@ -169,12 +167,7 @@ async function recoupWithdrawCoin(
   // FIXME: verify that our expectations about the amount match
 
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      denominations: x.denominations,
-      recoupGroups: x.recoupGroups,
-      refreshGroups: x.refreshGroups,
-    }))
+    .mktx((x) => [x.coins, x.denominations, x.recoupGroups, x.refreshGroups])
     .runReadWrite(async (tx) => {
       const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
       if (!recoupGroup) {
@@ -207,10 +200,7 @@ async function recoupRefreshCoin(
   cs: RefreshCoinSource,
 ): Promise<void> {
   const d = await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.coins, x.denominations])
     .runReadOnly(async (tx) => {
       const denomInfo = await ws.getDenomInfo(
         ws,
@@ -257,12 +247,7 @@ async function recoupRefreshCoin(
   }
 
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      denominations: x.denominations,
-      recoupGroups: x.recoupGroups,
-      refreshGroups: x.refreshGroups,
-    }))
+    .mktx((x) => [x.coins, x.denominations, x.recoupGroups, x.refreshGroups])
     .runReadWrite(async (tx) => {
       const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
       if (!recoupGroup) {
@@ -319,9 +304,7 @@ export async function processRecoupGroupHandler(
 ): Promise<OperationAttemptResult> {
   const forceNow = options.forceNow ?? false;
   let recoupGroup = await ws.db
-    .mktx((x) => ({
-      recoupGroups: x.recoupGroups,
-    }))
+    .mktx((x) => [x.recoupGroups])
     .runReadOnly(async (tx) => {
       return tx.recoupGroups.get(recoupGroupId);
     });
@@ -343,9 +326,7 @@ export async function processRecoupGroupHandler(
   await Promise.all(ps);
 
   recoupGroup = await ws.db
-    .mktx((x) => ({
-      recoupGroups: x.recoupGroups,
-    }))
+    .mktx((x) => [x.recoupGroups])
     .runReadOnly(async (tx) => {
       return tx.recoupGroups.get(recoupGroupId);
     });
@@ -366,10 +347,7 @@ export async function processRecoupGroupHandler(
   for (let i = 0; i < recoupGroup.coinPubs.length; i++) {
     const coinPub = recoupGroup.coinPubs[i];
     await ws.db
-      .mktx((x) => ({
-        coins: x.coins,
-        reserves: x.reserves,
-      }))
+      .mktx((x) => [x.coins, x.reserves])
       .runReadOnly(async (tx) => {
         const coin = await tx.coins.get(coinPub);
         if (!coin) {
@@ -414,12 +392,7 @@ export async function processRecoupGroupHandler(
   }
 
   await ws.db
-    .mktx((x) => ({
-      recoupGroups: x.recoupGroups,
-      denominations: WalletStoresV1.denominations,
-      refreshGroups: WalletStoresV1.refreshGroups,
-      coins: WalletStoresV1.coins,
-    }))
+    .mktx((x) => [x.recoupGroups, x.denominations, x.refreshGroups, x.coins])
     .runReadWrite(async (tx) => {
       const rg2 = await tx.recoupGroups.get(recoupGroupId);
       if (!rg2) {
@@ -497,10 +470,7 @@ async function processRecoup(
   coinIdx: number,
 ): Promise<void> {
   const coin = await ws.db
-    .mktx((x) => ({
-      recoupGroups: x.recoupGroups,
-      coins: x.coins,
-    }))
+    .mktx((x) => [x.recoupGroups, x.coins])
     .runReadOnly(async (tx) => {
       const recoupGroup = await tx.recoupGroups.get(recoupGroupId);
       if (!recoupGroup) {
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index 64b85a04..719093bd 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -155,10 +155,7 @@ async function refreshCreateSession(
   );
 
   const d = await ws.db
-    .mktx((x) => ({
-      refreshGroups: x.refreshGroups,
-      coins: x.coins,
-    }))
+    .mktx((x) => [x.refreshGroups, x.coins])
     .runReadWrite(async (tx) => {
       const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
       if (!refreshGroup) {
@@ -197,9 +194,7 @@ async function refreshCreateSession(
   // to update and filter withdrawable denoms.
 
   const { availableAmount, availableDenoms } = await ws.db
-    .mktx((x) => ({
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.denominations])
     .runReadOnly(async (tx) => {
       const oldDenom = await ws.getDenomInfo(
         ws,
@@ -237,10 +232,7 @@ async function refreshCreateSession(
       )} too small`,
     );
     await ws.db
-      .mktx((x) => ({
-        coins: x.coins,
-        refreshGroups: x.refreshGroups,
-      }))
+      .mktx((x) => [x.coins, x.refreshGroups])
       .runReadWrite(async (tx) => {
         const rg = await tx.refreshGroups.get(refreshGroupId);
         if (!rg) {
@@ -259,10 +251,7 @@ async function refreshCreateSession(
 
   // Store refresh session for this coin in the database.
   await ws.db
-    .mktx((x) => ({
-      refreshGroups: x.refreshGroups,
-      coins: x.coins,
-    }))
+    .mktx((x) => [x.refreshGroups, x.coins])
     .runReadWrite(async (tx) => {
       const rg = await tx.refreshGroups.get(refreshGroupId);
       if (!rg) {
@@ -300,11 +289,7 @@ async function refreshMelt(
   coinIndex: number,
 ): Promise<void> {
   const d = await ws.db
-    .mktx((x) => ({
-      refreshGroups: x.refreshGroups,
-      coins: x.coins,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.refreshGroups, x.coins, x.denominations])
     .runReadWrite(async (tx) => {
       const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
       if (!refreshGroup) {
@@ -414,9 +399,7 @@ async function refreshMelt(
   if (resp.status === HttpStatusCode.NotFound) {
     const errDetails = await readUnexpectedResponseDetails(resp);
     await ws.db
-      .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-      }))
+      .mktx((x) => [x.refreshGroups])
       .runReadWrite(async (tx) => {
         const rg = await tx.refreshGroups.get(refreshGroupId);
         if (!rg) {
@@ -446,9 +429,7 @@ async function refreshMelt(
   refreshSession.norevealIndex = norevealIndex;
 
   await ws.db
-    .mktx((x) => ({
-      refreshGroups: x.refreshGroups,
-    }))
+    .mktx((x) => [x.refreshGroups])
     .runReadWrite(async (tx) => {
       const rg = await tx.refreshGroups.get(refreshGroupId);
       if (!rg) {
@@ -538,11 +519,7 @@ async function refreshReveal(
 ): Promise<void> {
   logger.info("doing refresh reveal");
   const d = await ws.db
-    .mktx((x) => ({
-      refreshGroups: x.refreshGroups,
-      coins: x.coins,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.refreshGroups, x.coins, x.denominations])
     .runReadOnly(async (tx) => {
       const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
       if (!refreshGroup) {
@@ -703,10 +680,7 @@ async function refreshReveal(
   }
 
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      refreshGroups: x.refreshGroups,
-    }))
+    .mktx((x) => [x.coins, x.refreshGroups])
     .runReadWrite(async (tx) => {
       const rg = await tx.refreshGroups.get(refreshGroupId);
       if (!rg) {
@@ -740,12 +714,8 @@ export async function processRefreshGroup(
   logger.info(`processing refresh group ${refreshGroupId}`);
 
   const refreshGroup = await ws.db
-    .mktx((x) => ({
-      refreshGroups: x.refreshGroups,
-    }))
-    .runReadOnly(async (tx) => {
-      return tx.refreshGroups.get(refreshGroupId);
-    });
+    .mktx((x) => [x.refreshGroups])
+    .runReadOnly(async (tx) => tx.refreshGroups.get(refreshGroupId));
   if (!refreshGroup) {
     return {
       type: OperationAttemptResultType.Finished,
@@ -801,7 +771,7 @@ async function processRefreshSession(
     `processing refresh session for coin ${coinIndex} of group 
${refreshGroupId}`,
   );
   let refreshGroup = await ws.db
-    .mktx((x) => ({ refreshGroups: x.refreshGroups }))
+    .mktx((x) => [x.refreshGroups])
     .runReadOnly(async (tx) => {
       return tx.refreshGroups.get(refreshGroupId);
     });
@@ -814,7 +784,7 @@ async function processRefreshSession(
   if (!refreshGroup.refreshSessionPerCoin[coinIndex]) {
     await refreshCreateSession(ws, refreshGroupId, coinIndex);
     refreshGroup = await ws.db
-      .mktx((x) => ({ refreshGroups: x.refreshGroups }))
+      .mktx((x) => [x.refreshGroups])
       .runReadOnly(async (tx) => {
         return tx.refreshGroups.get(refreshGroupId);
       });
@@ -981,12 +951,7 @@ export async function autoRefresh(
     durationFromSpec({ days: 1 }),
   );
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      denominations: x.denominations,
-      refreshGroups: x.refreshGroups,
-      exchanges: x.exchanges,
-    }))
+    .mktx((x) => [x.coins, x.denominations, x.refreshGroups, x.exchanges])
     .runReadWrite(async (tx) => {
       const exchange = await tx.exchanges.get(exchangeBaseUrl);
       if (!exchange) {
diff --git a/packages/taler-wallet-core/src/operations/refund.ts 
b/packages/taler-wallet-core/src/operations/refund.ts
index bc8c185d..5ee0680d 100644
--- a/packages/taler-wallet-core/src/operations/refund.ts
+++ b/packages/taler-wallet-core/src/operations/refund.ts
@@ -78,9 +78,7 @@ export async function prepareRefund(
   }
 
   const purchase = await ws.db
-    .mktx((x) => ({
-      purchases: x.purchases,
-    }))
+    .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
       return tx.purchases.indexes.byMerchantUrlAndOrderId.get([
         parseResult.merchantBaseUrl,
@@ -335,12 +333,7 @@ async function acceptRefunds(
   const now = TalerProtocolTimestamp.now();
 
   await ws.db
-    .mktx((x) => ({
-      purchases: x.purchases,
-      coins: x.coins,
-      denominations: x.denominations,
-      refreshGroups: x.refreshGroups,
-    }))
+    .mktx((x) => [x.purchases, x.coins, x.denominations, x.refreshGroups])
     .runReadWrite(async (tx) => {
       const p = await tx.purchases.get(proposalId);
       if (!p) {
@@ -517,9 +510,7 @@ export async function applyRefund(
   }
 
   const purchase = await ws.db
-    .mktx((x) => ({
-      purchases: x.purchases,
-    }))
+    .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
       return tx.purchases.indexes.byMerchantUrlAndOrderId.get([
         parseResult.merchantBaseUrl,
@@ -544,9 +535,7 @@ export async function applyRefundFromPurchaseId(
 
   logger.info("processing purchase for refund");
   const success = await ws.db
-    .mktx((x) => ({
-      purchases: x.purchases,
-    }))
+    .mktx((x) => [x.purchases])
     .runReadWrite(async (tx) => {
       const p = await tx.purchases.get(proposalId);
       if (!p) {
@@ -569,9 +558,7 @@ export async function applyRefundFromPurchaseId(
   }
 
   const purchase = await ws.db
-    .mktx((x) => ({
-      purchases: x.purchases,
-    }))
+    .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
       return tx.purchases.get(proposalId);
     });
@@ -642,7 +629,7 @@ async function queryAndSaveAwaitingRefund(
     Amounts.cmp(refundAwaiting, purchase.refundAwaiting) !== 0
   ) {
     await ws.db
-      .mktx((x) => ({ purchases: x.purchases }))
+      .mktx((x) => [x.purchases])
       .runReadWrite(async (tx) => {
         const p = await tx.purchases.get(purchase.proposalId);
         if (!p) {
@@ -667,9 +654,7 @@ export async function processPurchaseQueryRefund(
 ): Promise<OperationAttemptResult> {
   const waitForAutoRefund = options.waitForAutoRefund ?? false;
   const purchase = await ws.db
-    .mktx((x) => ({
-      purchases: x.purchases,
-    }))
+    .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
       return tx.purchases.get(proposalId);
     });
@@ -729,9 +714,7 @@ export async function processPurchaseQueryRefund(
     const abortingCoins: AbortingCoin[] = [];
 
     await ws.db
-      .mktx((x) => ({
-        coins: x.coins,
-      }))
+      .mktx((x) => [x.coins])
       .runReadOnly(async (tx) => {
         for (let i = 0; i < purchase.payCoinSelection.coinPubs.length; i++) {
           const coinPub = purchase.payCoinSelection.coinPubs[i];
@@ -796,9 +779,7 @@ export async function abortFailedPayWithRefund(
   proposalId: string,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({
-      purchases: x.purchases,
-    }))
+    .mktx((x) => [x.purchases])
     .runReadWrite(async (tx) => {
       const purchase = await tx.purchases.get(proposalId);
       if (!purchase) {
diff --git a/packages/taler-wallet-core/src/operations/testing.ts 
b/packages/taler-wallet-core/src/operations/testing.ts
index 5c54d22c..e2a0c7db 100644
--- a/packages/taler-wallet-core/src/operations/testing.ts
+++ b/packages/taler-wallet-core/src/operations/testing.ts
@@ -467,7 +467,7 @@ export async function testPay(
     throw Error("payment not done");
   }
   const purchase = await ws.db
-    .mktx((x) => ({ purchases: x.purchases }))
+    .mktx((x) => [x.purchases])
     .runReadOnly(async (tx) => {
       return tx.purchases.get(result.proposalId);
     });
diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index f19be91b..04da2b98 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -71,9 +71,7 @@ export async function prepareTip(
   }
 
   let tipRecord = await ws.db
-    .mktx((x) => ({
-      tips: x.tips,
-    }))
+    .mktx((x) => [x.tips])
     .runReadOnly(async (tx) => {
       return tx.tips.indexes.byMerchantTipIdAndBaseUrl.get([
         res.merchantTipId,
@@ -100,13 +98,13 @@ export async function prepareTip(
     await updateExchangeFromUrl(ws, tipPickupStatus.exchange_url);
 
     //FIXME: is this needed? withdrawDetails is not used
-    // * if the intention is to update the exchange information in the 
database 
+    // * if the intention is to update the exchange information in the database
     //   maybe we can use another name. `get` seems like a pure-function
     const withdrawDetails = await getExchangeWithdrawalInfo(
       ws,
       tipPickupStatus.exchange_url,
       amount,
-      undefined
+      undefined,
     );
 
     const walletTipId = encodeCrock(getRandomBytes(32));
@@ -136,9 +134,7 @@ export async function prepareTip(
       denomSelUid,
     };
     await ws.db
-      .mktx((x) => ({
-        tips: x.tips,
-      }))
+      .mktx((x) => [x.tips])
       .runReadWrite(async (tx) => {
         await tx.tips.put(newTipRecord);
       });
@@ -166,9 +162,7 @@ export async function processTip(
   } = {},
 ): Promise<OperationAttemptResult> {
   const tipRecord = await ws.db
-    .mktx((x) => ({
-      tips: x.tips,
-    }))
+    .mktx((x) => [x.tips])
     .runReadOnly(async (tx) => {
       return tx.tips.get(walletTipId);
     });
@@ -196,9 +190,7 @@ export async function processTip(
 
   for (const dh of denomsForWithdraw.selectedDenoms) {
     const denom = await ws.db
-      .mktx((x) => ({
-        denominations: x.denominations,
-      }))
+      .mktx((x) => [x.denominations])
       .runReadOnly(async (tx) => {
         return tx.denominations.get([
           tipRecord.exchangeBaseUrl,
@@ -324,11 +316,7 @@ export async function processTip(
   }
 
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      tips: x.tips,
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.coins, x.tips, x.withdrawalGroups])
     .runReadWrite(async (tx) => {
       const tr = await tx.tips.get(walletTipId);
       if (!tr) {
@@ -355,9 +343,7 @@ export async function acceptTip(
   tipId: string,
 ): Promise<void> {
   const found = await ws.db
-    .mktx((x) => ({
-      tips: x.tips,
-    }))
+    .mktx((x) => [x.tips])
     .runReadWrite(async (tx) => {
       const tipRecord = await tx.tips.get(tipId);
       if (!tipRecord) {
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 5a96fc6f..5be24c57 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -126,24 +126,24 @@ export async function getTransactions(
   const transactions: Transaction[] = [];
 
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      denominations: x.denominations,
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      proposals: x.proposals,
-      purchases: x.purchases,
-      refreshGroups: x.refreshGroups,
-      tips: x.tips,
-      withdrawalGroups: x.withdrawalGroups,
-      planchets: x.planchets,
-      recoupGroups: x.recoupGroups,
-      depositGroups: x.depositGroups,
-      tombstones: x.tombstones,
-      peerPushPaymentInitiations: x.peerPushPaymentInitiations,
-      peerPullPaymentIncoming: x.peerPullPaymentIncoming,
-      operationRetries: x.operationRetries,
-    }))
+    .mktx((x) => [
+      x.coins,
+      x.denominations,
+      x.depositGroups,
+      x.exchangeDetails,
+      x.exchanges,
+      x.operationRetries,
+      x.peerPullPaymentIncoming,
+      x.peerPushPaymentInitiations,
+      x.planchets,
+      x.proposals,
+      x.purchases,
+      x.recoupGroups,
+      x.recoupGroups,
+      x.tips,
+      x.tombstones,
+      x.withdrawalGroups,
+    ])
     .runReadOnly(async (tx) => {
       tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
         const amount = Amounts.parseOrThrow(pi.amount);
@@ -609,10 +609,7 @@ export async function deleteTransaction(
   ) {
     const withdrawalGroupId = rest[0];
     await ws.db
-      .mktx((x) => ({
-        withdrawalGroups: x.withdrawalGroups,
-        tombstones: x.tombstones,
-      }))
+      .mktx((x) => [x.withdrawalGroups, x.tombstones])
       .runReadWrite(async (tx) => {
         const withdrawalGroupRecord = await tx.withdrawalGroups.get(
           withdrawalGroupId,
@@ -628,11 +625,7 @@ export async function deleteTransaction(
   } else if (type === TransactionType.Payment) {
     const proposalId = rest[0];
     await ws.db
-      .mktx((x) => ({
-        proposals: x.proposals,
-        purchases: x.purchases,
-        tombstones: x.tombstones,
-      }))
+      .mktx((x) => [x.proposals, x.purchases, x.tombstones])
       .runReadWrite(async (tx) => {
         let found = false;
         const proposal = await tx.proposals.get(proposalId);
@@ -654,10 +647,7 @@ export async function deleteTransaction(
   } else if (type === TransactionType.Refresh) {
     const refreshGroupId = rest[0];
     await ws.db
-      .mktx((x) => ({
-        refreshGroups: x.refreshGroups,
-        tombstones: x.tombstones,
-      }))
+      .mktx((x) => [x.refreshGroups, x.tombstones])
       .runReadWrite(async (tx) => {
         const rg = await tx.refreshGroups.get(refreshGroupId);
         if (rg) {
@@ -670,10 +660,7 @@ export async function deleteTransaction(
   } else if (type === TransactionType.Tip) {
     const tipId = rest[0];
     await ws.db
-      .mktx((x) => ({
-        tips: x.tips,
-        tombstones: x.tombstones,
-      }))
+      .mktx((x) => [x.tips, x.tombstones])
       .runReadWrite(async (tx) => {
         const tipRecord = await tx.tips.get(tipId);
         if (tipRecord) {
@@ -686,10 +673,7 @@ export async function deleteTransaction(
   } else if (type === TransactionType.Deposit) {
     const depositGroupId = rest[0];
     await ws.db
-      .mktx((x) => ({
-        depositGroups: x.depositGroups,
-        tombstones: x.tombstones,
-      }))
+      .mktx((x) => [x.depositGroups, x.tombstones])
       .runReadWrite(async (tx) => {
         const tipRecord = await tx.depositGroups.get(depositGroupId);
         if (tipRecord) {
@@ -704,11 +688,7 @@ export async function deleteTransaction(
     const executionTimeStr = rest[1];
 
     await ws.db
-      .mktx((x) => ({
-        proposals: x.proposals,
-        purchases: x.purchases,
-        tombstones: x.tombstones,
-      }))
+      .mktx((x) => [x.proposals, x.purchases, x.tombstones])
       .runReadWrite(async (tx) => {
         const purchase = await tx.purchases.get(proposalId);
         if (purchase) {
@@ -726,10 +706,7 @@ export async function deleteTransaction(
   } else if (type === TransactionType.PeerPullDebit) {
     const peerPullPaymentIncomingId = rest[0];
     await ws.db
-      .mktx((x) => ({
-        peerPullPaymentIncoming: x.peerPullPaymentIncoming,
-        tombstones: x.tombstones,
-      }))
+      .mktx((x) => [x.peerPullPaymentIncoming, x.tombstones])
       .runReadWrite(async (tx) => {
         const debit = await tx.peerPullPaymentIncoming.get(
           peerPullPaymentIncomingId,
@@ -747,10 +724,7 @@ export async function deleteTransaction(
   } else if (type === TransactionType.PeerPushDebit) {
     const pursePub = rest[0];
     await ws.db
-      .mktx((x) => ({
-        peerPushPaymentInitiations: x.peerPushPaymentInitiations,
-        tombstones: x.tombstones,
-      }))
+      .mktx((x) => [x.peerPushPaymentInitiations, x.tombstones])
       .runReadWrite(async (tx) => {
         const debit = await tx.peerPushPaymentInitiations.get(pursePub);
         if (debit) {
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index ce5863b3..1b838377 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -195,9 +195,9 @@ export interface ExchangeWithdrawDetails {
   /**
    * If the exchange supports age-restricted coins it will return
    * the array of ages.
-   * 
+   *
    */
-  ageRestrictionOptions?: number[],
+  ageRestrictionOptions?: number[];
 }
 
 /**
@@ -248,7 +248,7 @@ export function selectWithdrawalDenominations(
   for (const d of denoms) {
     let count = 0;
     const cost = Amounts.add(d.value, d.feeWithdraw).amount;
-    for (; ;) {
+    for (;;) {
       if (Amounts.cmp(remaining, cost) < 0) {
         break;
       }
@@ -412,7 +412,7 @@ export async function getCandidateWithdrawalDenoms(
   exchangeBaseUrl: string,
 ): Promise<DenominationRecord[]> {
   return await ws.db
-    .mktx((x) => ({ denominations: x.denominations }))
+    .mktx((x) => [x.denominations])
     .runReadOnly(async (tx) => {
       const allDenoms = await 
tx.denominations.indexes.byExchangeBaseUrl.getAll(
         exchangeBaseUrl,
@@ -434,9 +434,7 @@ async function processPlanchetGenerate(
   coinIdx: number,
 ): Promise<void> {
   let planchet = await ws.db
-    .mktx((x) => ({
-      planchets: x.planchets,
-    }))
+    .mktx((x) => [x.planchets])
     .runReadOnly(async (tx) => {
       return tx.planchets.indexes.byGroupAndIndex.get([
         withdrawalGroup.withdrawalGroupId,
@@ -462,9 +460,7 @@ async function processPlanchetGenerate(
   const denomPubHash = maybeDenomPubHash;
 
   const denom = await ws.db
-    .mktx((x) => ({
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.denominations])
     .runReadOnly(async (tx) => {
       return ws.getDenomInfo(
         ws,
@@ -500,7 +496,7 @@ async function processPlanchetGenerate(
     lastError: undefined,
   };
   await ws.db
-    .mktx((x) => ({ planchets: x.planchets }))
+    .mktx((x) => [x.planchets])
     .runReadWrite(async (tx) => {
       const p = await tx.planchets.indexes.byGroupAndIndex.get([
         withdrawalGroup.withdrawalGroupId,
@@ -529,12 +525,12 @@ async function processPlanchetExchangeRequest(
     `processing planchet exchange request 
${withdrawalGroup.withdrawalGroupId}/${coinIdx}`,
   );
   const d = await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-      planchets: x.planchets,
-      exchanges: x.exchanges,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [
+      x.withdrawalGroups,
+      x.planchets,
+      x.exchanges,
+      x.denominations,
+    ])
     .runReadOnly(async (tx) => {
       let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
         withdrawalGroup.withdrawalGroupId,
@@ -599,7 +595,7 @@ async function processPlanchetExchangeRequest(
     logger.trace("withdrawal request failed", e);
     logger.trace(e);
     await ws.db
-      .mktx((x) => ({ planchets: x.planchets }))
+      .mktx((x) => [x.planchets])
       .runReadWrite(async (tx) => {
         let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
           withdrawalGroup.withdrawalGroupId,
@@ -631,12 +627,12 @@ async function processPlanchetExchangeBatchRequest(
     .map((x) => x.count)
     .reduce((a, b) => a + b);
   const d = await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-      planchets: x.planchets,
-      exchanges: x.exchanges,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [
+      x.withdrawalGroups,
+      x.planchets,
+      x.exchanges,
+      x.denominations,
+    ])
     .runReadOnly(async (tx) => {
       const reqBody: { planchets: ExchangeWithdrawRequest[] } = {
         planchets: [],
@@ -705,11 +701,7 @@ async function processPlanchetVerifyAndStoreCoin(
   resp: WithdrawResponse,
 ): Promise<void> {
   const d = await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-      planchets: x.planchets,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.withdrawalGroups, x.planchets, x.denominations])
     .runReadOnly(async (tx) => {
       let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
         withdrawalGroup.withdrawalGroupId,
@@ -768,7 +760,7 @@ async function processPlanchetVerifyAndStoreCoin(
 
   if (!isValid) {
     await ws.db
-      .mktx((x) => ({ planchets: x.planchets }))
+      .mktx((x) => [x.planchets])
       .runReadWrite(async (tx) => {
         let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
           withdrawalGroup.withdrawalGroupId,
@@ -823,11 +815,7 @@ async function processPlanchetVerifyAndStoreCoin(
   // withdrawal succeeded.  If so, mark the withdrawal
   // group as finished.
   const firstSuccess = await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      withdrawalGroups: x.withdrawalGroups,
-      planchets: x.planchets,
-    }))
+    .mktx((x) => [x.coins, x.withdrawalGroups, x.planchets])
     .runReadWrite(async (tx) => {
       const p = await tx.planchets.get(planchetCoinPub);
       if (!p || p.withdrawalDone) {
@@ -858,10 +846,7 @@ export async function updateWithdrawalDenoms(
     `updating denominations used for withdrawal for ${exchangeBaseUrl}`,
   );
   const exchangeDetails = await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadOnly(async (tx) => {
       return ws.exchangeOps.getExchangeDetails(tx, exchangeBaseUrl);
     });
@@ -890,7 +875,8 @@ export async function updateWithdrawalDenoms(
         denom.verificationStatus === DenominationVerificationStatus.Unverified
       ) {
         logger.trace(
-          `Validating denomination (${current + 1}/${denominations.length
+          `Validating denomination (${current + 1}/${
+            denominations.length
           }) signature of ${denom.denomPubHash}`,
         );
         let valid = false;
@@ -919,7 +905,7 @@ export async function updateWithdrawalDenoms(
     if (updatedDenominations.length > 0) {
       logger.trace("writing denomination batch to db");
       await ws.db
-        .mktx((x) => ({ denominations: x.denominations }))
+        .mktx((x) => [x.denominations])
         .runReadWrite(async (tx) => {
           for (let i = 0; i < updatedDenominations.length; i++) {
             const denom = updatedDenominations[i];
@@ -973,7 +959,7 @@ async function queryReserve(
     if (
       resp.status === 404 &&
       result.talerErrorResponse.code ===
-      TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
+        TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
     ) {
       ws.notify({
         type: NotificationType.ReserveNotYetFound,
@@ -988,9 +974,7 @@ async function queryReserve(
   logger.trace(`got reserve status ${j2s(result.response)}`);
 
   await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.withdrawalGroups])
     .runReadWrite(async (tx) => {
       const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
       if (!wg) {
@@ -1011,7 +995,7 @@ export async function processWithdrawalGroup(
 ): Promise<OperationAttemptResult> {
   logger.trace("processing withdrawal group", withdrawalGroupId);
   const withdrawalGroup = await ws.db
-    .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
+    .mktx((x) => [x.withdrawalGroups])
     .runReadOnly(async (tx) => {
       return tx.withdrawalGroups.get(withdrawalGroupId);
     });
@@ -1080,7 +1064,7 @@ export async function processWithdrawalGroup(
 
   if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) {
     await ws.db
-      .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
+      .mktx((x) => [x.withdrawalGroups])
       .runReadWrite(async (tx) => {
         const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
         if (!wg) {
@@ -1148,11 +1132,7 @@ export async function processWithdrawalGroup(
   let errorsPerCoin: Record<number, TalerErrorDetail> = {};
 
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      withdrawalGroups: x.withdrawalGroups,
-      planchets: x.planchets,
-    }))
+    .mktx((x) => [x.coins, x.withdrawalGroups, x.planchets])
     .runReadWrite(async (tx) => {
       const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
       if (!wg) {
@@ -1202,7 +1182,9 @@ export async function processWithdrawalGroup(
   };
 }
 
-const AGE_MASK_GROUPS = "8:10:12:14:16:18".split(":").map(n => parseInt(n, 10))
+const AGE_MASK_GROUPS = "8:10:12:14:16:18"
+  .split(":")
+  .map((n) => parseInt(n, 10));
 
 export async function getExchangeWithdrawalInfo(
   ws: InternalWalletState,
@@ -1237,14 +1219,14 @@ export async function getExchangeWithdrawalInfo(
     exchange,
   );
 
-  let hasDenomWithAgeRestriction = false
+  let hasDenomWithAgeRestriction = false;
 
   let earliestDepositExpiration: TalerProtocolTimestamp | undefined;
   for (let i = 0; i < selectedDenoms.selectedDenoms.length; i++) {
     const ds = selectedDenoms.selectedDenoms[i];
     // FIXME: Do in one transaction!
     const denom = await ws.db
-      .mktx((x) => ({ denominations: x.denominations }))
+      .mktx((x) => [x.denominations])
       .runReadOnly(async (tx) => {
         return ws.getDenomInfo(ws, tx, exchangeBaseUrl, ds.denomPubHash);
       });
@@ -1262,13 +1244,14 @@ export async function getExchangeWithdrawalInfo(
     ) {
       earliestDepositExpiration = expireDeposit;
     }
-    hasDenomWithAgeRestriction = hasDenomWithAgeRestriction || 
denom.denomPub.age_mask > 0
+    hasDenomWithAgeRestriction =
+      hasDenomWithAgeRestriction || denom.denomPub.age_mask > 0;
   }
 
   checkLogicInvariant(!!earliestDepositExpiration);
 
   const possibleDenoms = await ws.db
-    .mktx((x) => ({ denominations: x.denominations }))
+    .mktx((x) => [x.denominations])
     .runReadOnly(async (tx) => {
       const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
         exchangeBaseUrl,
@@ -1290,7 +1273,7 @@ export async function getExchangeWithdrawalInfo(
     ) {
       logger.warn(
         `wallet's support for exchange protocol version 
${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
-        `(exchange has ${exchangeDetails.protocolVersion}), checking for 
updates`,
+          `(exchange has ${exchangeDetails.protocolVersion}), checking for 
updates`,
       );
     }
   }
@@ -1325,7 +1308,9 @@ export async function getExchangeWithdrawalInfo(
     withdrawalAmountRaw: Amounts.stringify(instructedAmount),
     // TODO: remove hardcoding, this should be calculated from the 
denominations info
     // force enabled for testing
-    ageRestrictionOptions: hasDenomWithAgeRestriction ? AGE_MASK_GROUPS : 
undefined
+    ageRestrictionOptions: hasDenomWithAgeRestriction
+      ? AGE_MASK_GROUPS
+      : undefined,
   };
   return ret;
 }
@@ -1369,11 +1354,7 @@ export async function getWithdrawalDetailsForUri(
   const exchanges: ExchangeListItem[] = [];
 
   await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations])
     .runReadOnly(async (tx) => {
       const exchangeRecords = await tx.exchanges.iter().toArray();
       for (const r of exchangeRecords) {
@@ -1409,11 +1390,7 @@ export async function getFundingPaytoUrisTx(
   withdrawalGroupId: string,
 ): Promise<string[]> {
   return await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails, x.withdrawalGroups])
     .runReadWrite((tx) => getFundingPaytoUris(tx, withdrawalGroupId));
 }
 
@@ -1461,9 +1438,7 @@ async function getWithdrawalGroupRecordTx(
   },
 ): Promise<WithdrawalGroupRecord | undefined> {
   return await db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.withdrawalGroups])
     .runReadOnly(async (tx) => {
       return tx.withdrawalGroups.get(req.withdrawalGroupId);
     });
@@ -1490,9 +1465,7 @@ async function registerReserveWithBank(
   withdrawalGroupId: string,
 ): Promise<void> {
   const withdrawalGroup = await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.withdrawalGroups])
     .runReadOnly(async (tx) => {
       return await tx.withdrawalGroups.get(withdrawalGroupId);
     });
@@ -1526,9 +1499,7 @@ async function registerReserveWithBank(
     codecForBankWithdrawalOperationPostResponse(),
   );
   await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.withdrawalGroups])
     .runReadWrite(async (tx) => {
       const r = await tx.withdrawalGroups.get(withdrawalGroupId);
       if (!r) {
@@ -1606,9 +1577,7 @@ async function processReserveBankStatus(
   if (status.aborted) {
     logger.info("bank aborted the withdrawal");
     await ws.db
-      .mktx((x) => ({
-        withdrawalGroups: x.withdrawalGroups,
-      }))
+      .mktx((x) => [x.withdrawalGroups])
       .runReadWrite(async (tx) => {
         const r = await tx.withdrawalGroups.get(withdrawalGroupId);
         if (!r) {
@@ -1648,9 +1617,7 @@ async function processReserveBankStatus(
   }
 
   await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.withdrawalGroups])
     .runReadWrite(async (tx) => {
       const r = await tx.withdrawalGroups.get(withdrawalGroupId);
       if (!r) {
@@ -1753,13 +1720,13 @@ export async function internalCreateWithdrawalGroup(
   );
 
   await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-      reserves: x.reserves,
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      exchangeTrust: x.exchangeTrust,
-    }))
+    .mktx((x) => [
+      x.withdrawalGroups,
+      x.reserves,
+      x.exchanges,
+      x.exchangeDetails,
+      x.exchangeTrust,
+    ])
     .runReadWrite(async (tx) => {
       await tx.withdrawalGroups.add(withdrawalGroup);
       await tx.reserves.put({
@@ -1790,7 +1757,7 @@ export async function acceptWithdrawalFromUri(
   },
 ): Promise<AcceptWithdrawalResponse> {
   const existingWithdrawalGroup = await ws.db
-    .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
+    .mktx((x) => [x.withdrawalGroups])
     .runReadOnly(async (tx) => {
       return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get(
         req.talerWithdrawUri,
@@ -1899,12 +1866,12 @@ export async function createManualWithdrawal(
   const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
 
   const exchangePaytoUris = await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      exchangeTrust: x.exchangeTrust,
-    }))
+    .mktx((x) => [
+      x.withdrawalGroups,
+      x.exchanges,
+      x.exchangeDetails,
+      x.exchangeTrust,
+    ])
     .runReadWrite(async (tx) => {
       return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId);
     });
diff --git a/packages/taler-wallet-core/src/util/query.ts 
b/packages/taler-wallet-core/src/util/query.ts
index 65b67eff..02595925 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -36,6 +36,7 @@ import {
 } from "@gnu-taler/idb-bridge";
 import { Logger } from "@gnu-taler/taler-util";
 import { performanceNow } from "./timer.js";
+import { access } from "fs";
 
 const logger = new Logger("query.ts");
 
@@ -280,7 +281,6 @@ export interface IndexDescriptor {
 
 export interface StoreDescriptor<RecordType> {
   _dummy: undefined & RecordType;
-  name: string;
   keyPath?: IDBKeyPath | IDBKeyPath[];
   autoIncrement?: boolean;
 }
@@ -291,10 +291,9 @@ export interface StoreOptions {
 }
 
 export function describeContents<RecordType = never>(
-  name: string,
   options: StoreOptions,
 ): StoreDescriptor<RecordType> {
-  return { name, keyPath: options.keyPath, _dummy: undefined as any };
+  return { keyPath: options.keyPath, _dummy: undefined as any };
 }
 
 export function describeIndex(
@@ -345,9 +344,11 @@ export interface StoreReadWriteAccessor<RecordType, 
IndexMap> {
 }
 
 export interface StoreWithIndexes<
+  StoreName extends string,
   SD extends StoreDescriptor<unknown>,
   IndexMap,
 > {
+  storeName: StoreName;
   store: SD;
   indexMap: IndexMap;
 
@@ -362,11 +363,17 @@ export type GetRecordType<T> = T extends 
StoreDescriptor<infer X> ? X : unknown;
 
 const storeWithIndexesSymbol = Symbol("StoreWithIndexesMark");
 
-export function describeStore<SD extends StoreDescriptor<unknown>, IndexMap>(
+export function describeStore<
+  StoreName extends string,
+  SD extends StoreDescriptor<unknown>,
+  IndexMap,
+>(
+  name: StoreName,
   s: SD,
   m: IndexMap,
-): StoreWithIndexes<SD, IndexMap> {
+): StoreWithIndexes<StoreName, SD, IndexMap> {
   return {
+    storeName: name,
     store: s,
     indexMap: m,
     mark: storeWithIndexesSymbol,
@@ -375,6 +382,7 @@ export function describeStore<SD extends 
StoreDescriptor<unknown>, IndexMap>(
 
 export type GetReadOnlyAccess<BoundStores> = {
   [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes<
+    infer SN,
     infer SD,
     infer IM
   >
@@ -384,6 +392,7 @@ export type GetReadOnlyAccess<BoundStores> = {
 
 export type GetReadWriteAccess<BoundStores> = {
   [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes<
+    infer SN,
     infer SD,
     infer IM
   >
@@ -404,8 +413,12 @@ export interface TransactionContext<BoundStores> {
   runReadOnly<T>(f: ReadOnlyTransactionFunction<BoundStores, T>): Promise<T>;
 }
 
-type CheckDescriptor<T> = T extends StoreWithIndexes<infer SD, infer IM>
-  ? StoreWithIndexes<SD, IM>
+type CheckDescriptor<T> = T extends StoreWithIndexes<
+  infer SN,
+  infer SD,
+  infer IM
+>
+  ? StoreWithIndexes<SN, SD, IM>
   : unknown;
 
 type GetPickerType<F, SM> = F extends (x: SM) => infer Out
@@ -477,13 +490,13 @@ function runTx<Arg, Res>(
 
 function makeReadContext(
   tx: IDBTransaction,
-  storePick: { [n: string]: StoreWithIndexes<any, any> },
+  storePick: { [n: string]: StoreWithIndexes<any, any, any> },
 ): any {
   const ctx: { [s: string]: StoreReadOnlyAccessor<any, any> } = {};
   for (const storeAlias in storePick) {
     const indexes: { [s: string]: IndexReadOnlyAccessor<any> } = {};
     const swi = storePick[storeAlias];
-    const storeName = swi.store.name;
+    const storeName = swi.storeName;
     for (const indexAlias in storePick[storeAlias].indexMap) {
       const indexDescriptor: IndexDescriptor =
         storePick[storeAlias].indexMap[indexAlias];
@@ -526,13 +539,13 @@ function makeReadContext(
 
 function makeWriteContext(
   tx: IDBTransaction,
-  storePick: { [n: string]: StoreWithIndexes<any, any> },
+  storePick: { [n: string]: StoreWithIndexes<any, any, any> },
 ): any {
   const ctx: { [s: string]: StoreReadWriteAccessor<any, any> } = {};
   for (const storeAlias in storePick) {
     const indexes: { [s: string]: IndexReadWriteAccessor<any> } = {};
     const swi = storePick[storeAlias];
-    const storeName = swi.store.name;
+    const storeName = swi.storeName;
     for (const indexAlias in storePick[storeAlias].indexMap) {
       const indexDescriptor: IndexDescriptor =
         storePick[storeAlias].indexMap[indexAlias];
@@ -585,25 +598,11 @@ function makeWriteContext(
   return ctx;
 }
 
-const storeList = [
-  { name: "foo" as const, value: 1 as const },
-  { name: "bar" as const, value: 2 as const },
-];
-// => { foo: { value: 1}, bar: {value: 2} }
-
-type StoreList = typeof storeList;
-
-type StoreNames = StoreList[number] extends { name: infer I } ? I : never;
-
-type H = StoreList[number] & { name: "foo"};
-
-type Cleanup<V> = V extends { name: infer N, value: infer X} ? {name: N, 
value: X} : never;
-
-type G = {
-  [X in StoreNames]: {
-    X: StoreList[number] & { name: X };
-  };
-};
+type StoreNamesOf<X> = X extends { [x: number]: infer F }
+  ? F extends { storeName: infer I }
+    ? I
+    : never
+  : never;
 
 /**
  * Type-safe access to a database with a particular store map.
@@ -617,36 +616,41 @@ export class DbAccess<StoreMap> {
     return this.db;
   }
 
-  mktx2<
+  /**
+   * Run a transaction with selected object stores.
+   *
+   * The {@link namePicker} must be a function that selects a list of object
+   * stores from all available object stores.
+   */
+  mktx<
     StoreNames extends keyof StoreMap,
     Stores extends StoreMap[StoreNames],
     StoreList extends Stores[],
-  >(namePicker: (x: StoreMap) => StoreList): StoreList {
-    return namePicker(this.stores);
-  }
-
-  mktx<
-    PickerType extends (x: StoreMap) => unknown,
-    BoundStores extends GetPickerType<PickerType, StoreMap>,
-  >(f: PickerType): TransactionContext<BoundStores> {
-    const storePick = f(this.stores) as any;
+    BoundStores extends {
+      [X in StoreNamesOf<StoreList>]: StoreList[number] & { storeName: X };
+    },
+  >(namePicker: (x: StoreMap) => StoreList): TransactionContext<BoundStores> {
+    const storePick = namePicker(this.stores) as any;
     if (typeof storePick !== "object" || storePick === null) {
       throw Error();
     }
     const storeNames: string[] = [];
-    for (const storeAlias of Object.keys(storePick)) {
-      const swi = (storePick as any)[storeAlias] as StoreWithIndexes<any, any>;
+    const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } =
+      {};
+    for (const swiPicked of storePick) {
+      const swi = swiPicked as StoreWithIndexes<any, any, any>;
       if (swi.mark !== storeWithIndexesSymbol) {
         throw Error("invalid store descriptor returned from selector 
function");
       }
-      storeNames.push(swi.store.name);
+      storeNames.push(swi.storeName);
+      accessibleStores[swi.storeName] = swi;
     }
 
     const runReadOnly = <T>(
       txf: ReadOnlyTransactionFunction<BoundStores, T>,
     ): Promise<T> => {
       const tx = this.db.transaction(storeNames, "readonly");
-      const readContext = makeReadContext(tx, storePick);
+      const readContext = makeReadContext(tx, accessibleStores);
       return runTx(tx, readContext, txf);
     };
 
@@ -654,7 +658,7 @@ export class DbAccess<StoreMap> {
       txf: ReadWriteTransactionFunction<BoundStores, T>,
     ): Promise<T> => {
       const tx = this.db.transaction(storeNames, "readwrite");
-      const writeContext = makeWriteContext(tx, storePick);
+      const writeContext = makeWriteContext(tx, accessibleStores);
       return runTx(tx, writeContext, txf);
     };
 
diff --git a/packages/taler-wallet-core/src/util/retries.ts 
b/packages/taler-wallet-core/src/util/retries.ts
index 3a41e834..4763bbc4 100644
--- a/packages/taler-wallet-core/src/util/retries.ts
+++ b/packages/taler-wallet-core/src/util/retries.ts
@@ -201,8 +201,9 @@ export async function scheduleRetry(
   errorDetail?: TalerErrorDetail,
 ): Promise<void> {
   return await ws.db
-    .mktx((x) => ({ operationRetries: x.operationRetries }))
+    .mktx((x) => [x.operationRetries])
     .runReadWrite(async (tx) => {
+      tx.operationRetries
       scheduleRetryInTx(ws, tx, opId, errorDetail);
     });
 }
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index f041d9aa..58e11e90 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -328,7 +328,7 @@ export async function storeOperationError(
   e: TalerErrorDetail,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({ operationRetries: x.operationRetries }))
+    .mktx((x) => [x.operationRetries])
     .runReadWrite(async (tx) => {
       const retryRecord = await tx.operationRetries.get(pendingTaskId);
       if (!retryRecord) {
@@ -345,7 +345,7 @@ export async function storeOperationFinished(
   pendingTaskId: string,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({ operationRetries: x.operationRetries }))
+    .mktx((x) => [x.operationRetries])
     .runReadWrite(async (tx) => {
       await tx.operationRetries.delete(pendingTaskId);
     });
@@ -356,7 +356,7 @@ export async function storeOperationPending(
   pendingTaskId: string,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({ operationRetries: x.operationRetries }))
+    .mktx((x) => [x.operationRetries])
     .runReadWrite(async (tx) => {
       const retryRecord = await tx.operationRetries.get(pendingTaskId);
       if (!retryRecord) {
@@ -542,7 +542,7 @@ async function runTaskLoop(
  */
 async function fillDefaults(ws: InternalWalletState): Promise<void> {
   await ws.db
-    .mktx((x) => ({ config: x.config, auditorTrustStore: x.auditorTrust }))
+    .mktx((x) => [x.config, x.auditorTrust])
     .runReadWrite(async (tx) => {
       let applied = false;
       await tx.config.iter().forEach((x) => {
@@ -552,7 +552,7 @@ async function fillDefaults(ws: InternalWalletState): 
Promise<void> {
       });
       if (!applied) {
         for (const c of builtinAuditors) {
-          await tx.auditorTrustStore.put(c);
+          await tx.auditorTrust.put(c);
         }
       }
       // FIXME: make sure exchanges are added transactionally to
@@ -634,9 +634,7 @@ async function listKnownBankAccounts(
 ): Promise<KnownBankAccounts> {
   const accounts: { [account: string]: PaytoUri } = {};
   await ws.db
-    .mktx((x) => ({
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.withdrawalGroups])
     .runReadOnly(async (tx) => {
       const withdrawalGroups = await tx.withdrawalGroups.iter().toArray();
       for (const r of withdrawalGroups) {
@@ -660,11 +658,7 @@ async function getExchanges(
 ): Promise<ExchangesListResponse> {
   const exchanges: ExchangeListItem[] = [];
   await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations])
     .runReadOnly(async (tx) => {
       const exchangeRecords = await tx.exchanges.iter().toArray();
       for (const r of exchangeRecords) {
@@ -708,11 +702,7 @@ async function getExchangeDetailedInfo(
 ): Promise<ExchangeFullDetails> {
   //TODO: should we use the forceUpdate parameter?
   const exchange = await ws.db
-    .mktx((x) => ({
-      exchanges: x.exchanges,
-      exchangeDetails: x.exchangeDetails,
-      denominations: x.denominations,
-    }))
+    .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations])
     .runReadOnly(async (tx) => {
       const ex = await tx.exchanges.get(exchangeBaseurl);
       const dp = ex?.detailsPointer;
@@ -790,9 +780,7 @@ async function setCoinSuspended(
   suspended: boolean,
 ): Promise<void> {
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-    }))
+    .mktx((x) => [x.coins])
     .runReadWrite(async (tx) => {
       const c = await tx.coins.get(coinPub);
       if (!c) {
@@ -811,11 +799,7 @@ async function dumpCoins(ws: InternalWalletState): 
Promise<CoinDumpJson> {
   const coinsJson: CoinDumpJson = { coins: [] };
   logger.info("dumping coins");
   await ws.db
-    .mktx((x) => ({
-      coins: x.coins,
-      denominations: x.denominations,
-      withdrawalGroups: x.withdrawalGroups,
-    }))
+    .mktx((x) => [x.coins, x.denominations, x.withdrawalGroups])
     .runReadOnly(async (tx) => {
       const coins = await tx.coins.iter().toArray();
       for (const c of coins) {
@@ -1065,11 +1049,7 @@ async function dispatchRequestInternal(
       const req = codecForForceRefreshRequest().decode(payload);
       const coinPubs = req.coinPubList.map((x) => ({ coinPub: x }));
       const refreshGroupId = await ws.db
-        .mktx((x) => ({
-          refreshGroups: x.refreshGroups,
-          denominations: x.denominations,
-          coins: x.coins,
-        }))
+        .mktx((x) => [x.refreshGroups, x.denominations, x.coins])
         .runReadWrite(async (tx) => {
           return await createRefreshGroup(
             ws,
@@ -1164,10 +1144,7 @@ async function dispatchRequestInternal(
     }
     case "listCurrencies": {
       return await ws.db
-        .mktx((x) => ({
-          auditorTrust: x.auditorTrust,
-          exchangeTrust: x.exchangeTrust,
-        }))
+        .mktx((x) => [x.auditorTrust, x.exchangeTrust])
         .runReadOnly(async (tx) => {
           const trustedAuditors = await tx.auditorTrust.iter().toArray();
           const trustedExchanges = await tx.exchangeTrust.iter().toArray();

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