@bopen-io/wallet-toolbox-client 1.7.18 → 1.7.20-idb-fix.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -75,6 +75,16 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
75
75
  toDbTrx(stores, mode, trx) {
76
76
  if (trx) {
77
77
  const t = trx;
78
+ // Check if the transaction is still active by trying to access an object store
79
+ try {
80
+ const storeToCheck = stores[0] || this.allStores[0];
81
+ t.objectStore(storeToCheck);
82
+ }
83
+ catch (e) {
84
+ console.error(`[StorageIdb.toDbTrx] Passed transaction already finished! stores=${stores.join(',')}, mode=${mode}`);
85
+ console.error('[StorageIdb.toDbTrx] Stack trace:', new Error().stack);
86
+ throw e;
87
+ }
78
88
  return t;
79
89
  }
80
90
  else {
@@ -290,7 +300,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
290
300
  * @returns next funding output to add to transaction or undefined if there are none.
291
301
  */
292
302
  async allocateChangeInput(userId, basketId, targetSatoshis, exactSatoshis, excludeSending, transactionId) {
293
- const dbTrx = this.toDbTrx(['outputs', 'transactions'], 'readwrite');
303
+ // Include proven_txs in store list since findOutputs -> validateOutputScript needs it
304
+ const dbTrx = this.toDbTrx(['outputs', 'transactions', 'proven_txs'], 'readwrite');
294
305
  try {
295
306
  const txStatus = ['completed', 'unproven'];
296
307
  if (!excludeSending)
@@ -444,19 +455,33 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
444
455
  }
445
456
  async filterOutputTagMaps(args, filtered, userId) {
446
457
  var _a, _b, _c, _d;
458
+ // Pre-compute outputTagIds for this user BEFORE opening cursor.
459
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
460
+ let userOutputTagIds;
461
+ if (userId !== undefined) {
462
+ userOutputTagIds = new Set();
463
+ const userTags = await this.findOutputTags({ partial: { userId } });
464
+ for (const tag of userTags) {
465
+ userOutputTagIds.add(tag.outputTagId);
466
+ }
467
+ }
447
468
  const offset = ((_a = args.paged) === null || _a === void 0 ? void 0 : _a.offset) || 0;
448
469
  let skipped = 0;
449
470
  let count = 0;
450
471
  const dbTrx = this.toDbTrx(['output_tags_map'], 'readonly', args.trx);
472
+ const direction = args.orderDescending ? 'prev' : 'next';
451
473
  let cursor;
452
474
  if (((_b = args.partial) === null || _b === void 0 ? void 0 : _b.outputTagId) !== undefined) {
453
- cursor = await dbTrx.objectStore('output_tags_map').index('outputTagId').openCursor(args.partial.outputTagId);
475
+ cursor = await dbTrx
476
+ .objectStore('output_tags_map')
477
+ .index('outputTagId')
478
+ .openCursor(args.partial.outputTagId, direction);
454
479
  }
455
480
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.outputId) !== undefined) {
456
- cursor = await dbTrx.objectStore('output_tags_map').index('outputId').openCursor(args.partial.outputId);
481
+ cursor = await dbTrx.objectStore('output_tags_map').index('outputId').openCursor(args.partial.outputId, direction);
457
482
  }
458
483
  else {
459
- cursor = await dbTrx.objectStore('output_tags_map').openCursor();
484
+ cursor = await dbTrx.objectStore('output_tags_map').openCursor(null, direction);
460
485
  }
461
486
  let firstTime = true;
462
487
  while (cursor) {
@@ -482,10 +507,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
482
507
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted)
483
508
  continue;
484
509
  }
485
- if (userId !== undefined && r.txid) {
486
- const count = await this.countOutputTags({ partial: { userId, outputTagId: r.outputTagId }, trx: args.trx });
487
- if (count === 0)
488
- continue;
510
+ if (userOutputTagIds !== undefined && !userOutputTagIds.has(r.outputTagId)) {
511
+ continue;
489
512
  }
490
513
  if (skipped < offset) {
491
514
  skipped++;
@@ -512,28 +535,44 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
512
535
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('args.partial.rawTx', `undefined. ProvenTxReqs may not be found by rawTx value.`);
513
536
  if (args.partial.inputBEEF)
514
537
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('args.partial.inputBEEF', `undefined. ProvenTxReqs may not be found by inputBEEF value.`);
538
+ // Pre-compute txids for this user's transactions BEFORE opening cursor.
539
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
540
+ let userTxIds;
541
+ if (userId !== undefined) {
542
+ userTxIds = new Set();
543
+ const userTxs = await this.findTransactions({ partial: { userId }, noRawTx: true });
544
+ for (const tx of userTxs) {
545
+ if (tx.txid) {
546
+ userTxIds.add(tx.txid);
547
+ }
548
+ }
549
+ }
515
550
  const offset = ((_a = args.paged) === null || _a === void 0 ? void 0 : _a.offset) || 0;
516
551
  let skipped = 0;
517
552
  let count = 0;
518
553
  const dbTrx = this.toDbTrx(['proven_tx_reqs'], 'readonly', args.trx);
554
+ const direction = args.orderDescending ? 'prev' : 'next';
519
555
  let cursor;
520
556
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.provenTxReqId) {
521
- cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(args.partial.provenTxReqId);
557
+ cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(args.partial.provenTxReqId, direction);
522
558
  }
523
559
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.provenTxId) !== undefined) {
524
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('provenTxId').openCursor(args.partial.provenTxId);
560
+ cursor = await dbTrx
561
+ .objectStore('proven_tx_reqs')
562
+ .index('provenTxId')
563
+ .openCursor(args.partial.provenTxId, direction);
525
564
  }
526
565
  else if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.txid) !== undefined) {
527
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('txid').openCursor(args.partial.txid);
566
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('txid').openCursor(args.partial.txid, direction);
528
567
  }
529
568
  else if (((_e = args.partial) === null || _e === void 0 ? void 0 : _e.status) !== undefined) {
530
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('status').openCursor(args.partial.status);
569
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('status').openCursor(args.partial.status, direction);
531
570
  }
532
571
  else if (((_f = args.partial) === null || _f === void 0 ? void 0 : _f.batch) !== undefined) {
533
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('batch').openCursor(args.partial.batch);
572
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('batch').openCursor(args.partial.batch, direction);
534
573
  }
535
574
  else {
536
- cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor();
575
+ cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(null, direction);
537
576
  }
538
577
  let firstTime = true;
539
578
  while (cursor) {
@@ -545,6 +584,10 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
545
584
  const r = cursor.value;
546
585
  if (args.since && args.since > r.updated_at)
547
586
  continue;
587
+ if (args.status && args.status.length > 0 && !args.status.includes(r.status))
588
+ continue;
589
+ if (args.txids && args.txids.length > 0 && !args.txids.includes(r.txid))
590
+ continue;
548
591
  if (args.partial) {
549
592
  if (args.partial.provenTxReqId && r.provenTxReqId !== args.partial.provenTxReqId)
550
593
  continue;
@@ -569,10 +612,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
569
612
  if (args.partial.notify && r.notify !== args.partial.notify)
570
613
  continue;
571
614
  }
572
- if (userId !== undefined && r.txid) {
573
- const count = await this.countTransactions({ partial: { userId, txid: r.txid }, trx: args.trx });
574
- if (count === 0)
575
- continue;
615
+ if (userTxIds !== undefined && r.txid && !userTxIds.has(r.txid)) {
616
+ continue;
576
617
  }
577
618
  if (skipped < offset) {
578
619
  skipped++;
@@ -599,19 +640,32 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
599
640
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('args.partial.rawTx', `undefined. ProvenTxs may not be found by rawTx value.`);
600
641
  if (args.partial.merklePath)
601
642
  throw new WERR_errors_1.WERR_INVALID_PARAMETER('args.partial.merklePath', `undefined. ProvenTxs may not be found by merklePath value.`);
643
+ // Pre-compute provenTxIds for this user's transactions BEFORE opening cursor.
644
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
645
+ let userProvenTxIds;
646
+ if (userId !== undefined) {
647
+ userProvenTxIds = new Set();
648
+ const userTxs = await this.findTransactions({ partial: { userId }, noRawTx: true });
649
+ for (const tx of userTxs) {
650
+ if (tx.provenTxId !== undefined) {
651
+ userProvenTxIds.add(tx.provenTxId);
652
+ }
653
+ }
654
+ }
602
655
  const offset = ((_a = args.paged) === null || _a === void 0 ? void 0 : _a.offset) || 0;
603
656
  let skipped = 0;
604
657
  let count = 0;
605
658
  const dbTrx = this.toDbTrx(['proven_txs'], 'readonly', args.trx);
659
+ const direction = args.orderDescending ? 'prev' : 'next';
606
660
  let cursor;
607
661
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.provenTxId) {
608
- cursor = await dbTrx.objectStore('proven_txs').openCursor(args.partial.provenTxId);
662
+ cursor = await dbTrx.objectStore('proven_txs').openCursor(args.partial.provenTxId, direction);
609
663
  }
610
664
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.txid) !== undefined) {
611
- cursor = await dbTrx.objectStore('proven_txs').index('txid').openCursor(args.partial.txid);
665
+ cursor = await dbTrx.objectStore('proven_txs').index('txid').openCursor(args.partial.txid, direction);
612
666
  }
613
667
  else {
614
- cursor = await dbTrx.objectStore('proven_txs').openCursor();
668
+ cursor = await dbTrx.objectStore('proven_txs').openCursor(null, direction);
615
669
  }
616
670
  let firstTime = true;
617
671
  while (cursor) {
@@ -641,10 +695,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
641
695
  if (args.partial.merkleRoot && r.merkleRoot !== args.partial.merkleRoot)
642
696
  continue;
643
697
  }
644
- if (userId !== undefined) {
645
- const count = await this.countTransactions({ partial: { userId, provenTxId: r.provenTxId }, trx: args.trx });
646
- if (count === 0)
647
- continue;
698
+ if (userProvenTxIds !== undefined && !userProvenTxIds.has(r.provenTxId)) {
699
+ continue;
648
700
  }
649
701
  if (skipped < offset) {
650
702
  skipped++;
@@ -667,19 +719,33 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
667
719
  }
668
720
  async filterTxLabelMaps(args, filtered, userId) {
669
721
  var _a, _b, _c, _d;
722
+ // Pre-compute txLabelIds for this user BEFORE opening cursor.
723
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
724
+ let userTxLabelIds;
725
+ if (userId !== undefined) {
726
+ userTxLabelIds = new Set();
727
+ const userLabels = await this.findTxLabels({ partial: { userId } });
728
+ for (const label of userLabels) {
729
+ userTxLabelIds.add(label.txLabelId);
730
+ }
731
+ }
670
732
  const offset = ((_a = args.paged) === null || _a === void 0 ? void 0 : _a.offset) || 0;
671
733
  let skipped = 0;
672
734
  let count = 0;
673
735
  const dbTrx = this.toDbTrx(['tx_labels_map'], 'readonly', args.trx);
736
+ const direction = args.orderDescending ? 'prev' : 'next';
674
737
  let cursor;
675
738
  if (((_b = args.partial) === null || _b === void 0 ? void 0 : _b.transactionId) !== undefined) {
676
- cursor = await dbTrx.objectStore('tx_labels_map').index('transactionId').openCursor(args.partial.transactionId);
739
+ cursor = await dbTrx
740
+ .objectStore('tx_labels_map')
741
+ .index('transactionId')
742
+ .openCursor(args.partial.transactionId, direction);
677
743
  }
678
744
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.txLabelId) !== undefined) {
679
- cursor = await dbTrx.objectStore('tx_labels_map').index('txLabelId').openCursor(args.partial.txLabelId);
745
+ cursor = await dbTrx.objectStore('tx_labels_map').index('txLabelId').openCursor(args.partial.txLabelId, direction);
680
746
  }
681
747
  else {
682
- cursor = await dbTrx.objectStore('tx_labels_map').openCursor();
748
+ cursor = await dbTrx.objectStore('tx_labels_map').openCursor(null, direction);
683
749
  }
684
750
  let firstTime = true;
685
751
  while (cursor) {
@@ -691,10 +757,12 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
691
757
  const r = cursor.value;
692
758
  if (args.since && args.since > r.updated_at)
693
759
  continue;
760
+ if (args.labelIds && !args.labelIds.includes(r.txLabelId))
761
+ continue;
694
762
  if (args.partial) {
695
- if (args.partial.txLabelId && r.txLabelId !== args.partial.txLabelId)
763
+ if (args.partial.txLabelId !== undefined && r.txLabelId !== args.partial.txLabelId)
696
764
  continue;
697
- if (args.partial.transactionId && r.transactionId !== args.partial.transactionId)
765
+ if (args.partial.transactionId !== undefined && r.transactionId !== args.partial.transactionId)
698
766
  continue;
699
767
  if (args.partial.created_at && r.created_at.getTime() !== args.partial.created_at.getTime())
700
768
  continue;
@@ -703,10 +771,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
703
771
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted)
704
772
  continue;
705
773
  }
706
- if (userId !== undefined) {
707
- const count = await this.countTxLabels({ partial: { userId, txLabelId: r.txLabelId }, trx: args.trx });
708
- if (count === 0)
709
- continue;
774
+ if (userTxLabelIds !== undefined && !userTxLabelIds.has(r.txLabelId)) {
775
+ continue;
710
776
  }
711
777
  if (skipped < offset) {
712
778
  skipped++;
@@ -765,6 +831,16 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
765
831
  const dbTrx = this.toDbTrx(['certificates', 'certificate_fields'], 'readwrite', trx);
766
832
  const store = dbTrx.objectStore('certificates');
767
833
  try {
834
+ const existing = await store.index('userId_type_certifier_serialNumber').get([
835
+ certificate.userId,
836
+ certificate.type,
837
+ certificate.certifier,
838
+ certificate.serialNumber
839
+ ]);
840
+ if (existing) {
841
+ certificate.certificateId = existing.certificateId;
842
+ return certificate.certificateId;
843
+ }
768
844
  const id = Number(await store.add(e));
769
845
  certificate.certificateId = id;
770
846
  if (fields) {
@@ -800,6 +876,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
800
876
  const dbTrx = this.toDbTrx(['commissions'], 'readwrite', trx);
801
877
  const store = dbTrx.objectStore('commissions');
802
878
  try {
879
+ const existing = await store.index('transactionId').get(commission.transactionId);
880
+ if (existing) {
881
+ commission.commissionId = existing.commissionId;
882
+ return commission.commissionId;
883
+ }
803
884
  const id = Number(await store.add(e));
804
885
  commission.commissionId = id;
805
886
  }
@@ -832,6 +913,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
832
913
  const dbTrx = this.toDbTrx(['outputs'], 'readwrite', trx);
833
914
  const store = dbTrx.objectStore('outputs');
834
915
  try {
916
+ const existing = await store.index('transactionId_vout_userId').get([output.transactionId, output.vout, output.userId]);
917
+ if (existing) {
918
+ output.outputId = existing.outputId;
919
+ return output.outputId;
920
+ }
835
921
  const id = Number(await store.add(e));
836
922
  output.outputId = id;
837
923
  }
@@ -848,6 +934,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
848
934
  const dbTrx = this.toDbTrx(['output_baskets'], 'readwrite', trx);
849
935
  const store = dbTrx.objectStore('output_baskets');
850
936
  try {
937
+ const existing = await store.index('name_userId').get([basket.name, basket.userId]);
938
+ if (existing) {
939
+ basket.basketId = existing.basketId;
940
+ return basket.basketId;
941
+ }
851
942
  const id = Number(await store.add(e));
852
943
  basket.basketId = id;
853
944
  }
@@ -864,6 +955,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
864
955
  const dbTrx = this.toDbTrx(['output_tags'], 'readwrite', trx);
865
956
  const store = dbTrx.objectStore('output_tags');
866
957
  try {
958
+ const existing = await store.index('tag_userId').get([tag.tag, tag.userId]);
959
+ if (existing) {
960
+ tag.outputTagId = existing.outputTagId;
961
+ return tag.outputTagId;
962
+ }
867
963
  const id = Number(await store.add(e));
868
964
  tag.outputTagId = id;
869
965
  }
@@ -892,6 +988,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
892
988
  const dbTrx = this.toDbTrx(['proven_txs'], 'readwrite', trx);
893
989
  const store = dbTrx.objectStore('proven_txs');
894
990
  try {
991
+ const existing = await store.index('txid').get(tx.txid);
992
+ if (existing) {
993
+ tx.provenTxId = existing.provenTxId;
994
+ return tx.provenTxId;
995
+ }
895
996
  const id = Number(await store.add(e));
896
997
  tx.provenTxId = id;
897
998
  }
@@ -908,6 +1009,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
908
1009
  const dbTrx = this.toDbTrx(['proven_tx_reqs'], 'readwrite', trx);
909
1010
  const store = dbTrx.objectStore('proven_tx_reqs');
910
1011
  try {
1012
+ const existing = await store.index('txid').get(tx.txid);
1013
+ if (existing) {
1014
+ tx.provenTxReqId = existing.provenTxReqId;
1015
+ return tx.provenTxReqId;
1016
+ }
911
1017
  const id = Number(await store.add(e));
912
1018
  tx.provenTxReqId = id;
913
1019
  }
@@ -940,6 +1046,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
940
1046
  const dbTrx = this.toDbTrx(['transactions'], 'readwrite', trx);
941
1047
  const store = dbTrx.objectStore('transactions');
942
1048
  try {
1049
+ const existing = await store.index('reference').get(tx.reference);
1050
+ if (existing) {
1051
+ tx.transactionId = existing.transactionId;
1052
+ return tx.transactionId;
1053
+ }
943
1054
  const id = Number(await store.add(e));
944
1055
  tx.transactionId = id;
945
1056
  }
@@ -956,6 +1067,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
956
1067
  const dbTrx = this.toDbTrx(['tx_labels'], 'readwrite', trx);
957
1068
  const store = dbTrx.objectStore('tx_labels');
958
1069
  try {
1070
+ const existing = await store.index('label_userId').get([label.label, label.userId]);
1071
+ if (existing) {
1072
+ label.txLabelId = existing.txLabelId;
1073
+ return label.txLabelId;
1074
+ }
959
1075
  const id = Number(await store.add(e));
960
1076
  label.txLabelId = id;
961
1077
  }
@@ -984,6 +1100,11 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
984
1100
  const dbTrx = this.toDbTrx(['users'], 'readwrite', trx);
985
1101
  const store = dbTrx.objectStore('users');
986
1102
  try {
1103
+ const existing = await store.index('identityKey').get(user.identityKey);
1104
+ if (existing) {
1105
+ user.userId = existing.userId;
1106
+ return user.userId;
1107
+ }
987
1108
  const id = Number(await store.add(e));
988
1109
  user.userId = id;
989
1110
  }
@@ -999,7 +1120,14 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
999
1120
  }
1000
1121
  const u = this.validatePartialForUpdate(update);
1001
1122
  const dbTrx = this.toDbTrx([storeName], 'readwrite', trx);
1002
- const store = dbTrx.objectStore(storeName);
1123
+ let store;
1124
+ try {
1125
+ store = dbTrx.objectStore(storeName);
1126
+ }
1127
+ catch (e) {
1128
+ console.error(`[StorageIdb.updateIdb] objectStore('${storeName}') failed for id=${JSON.stringify(id)}, trx=${!!trx}:`, e);
1129
+ throw e;
1130
+ }
1003
1131
  const ids = Array.isArray(id) ? id : [id];
1004
1132
  try {
1005
1133
  for (const i of ids) {
@@ -1124,7 +1252,14 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1124
1252
  return r;
1125
1253
  }
1126
1254
  catch (err) {
1127
- tx.abort();
1255
+ // Log more detail about transaction errors to help debug IDB issues
1256
+ console.error('[StorageIdb] Transaction error:', err instanceof Error ? err.message : err);
1257
+ try {
1258
+ tx.abort();
1259
+ }
1260
+ catch (abortErr) {
1261
+ console.error('[StorageIdb] Error aborting transaction:', abortErr);
1262
+ }
1128
1263
  await tx.done;
1129
1264
  throw err;
1130
1265
  }
@@ -1135,18 +1270,19 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1135
1270
  let skipped = 0;
1136
1271
  let count = 0;
1137
1272
  const dbTrx = this.toDbTrx(['certificate_fields'], 'readonly', args.trx);
1273
+ const direction = args.orderDescending ? 'prev' : 'next';
1138
1274
  let cursor;
1139
1275
  if (((_b = args.partial) === null || _b === void 0 ? void 0 : _b.certificateId) !== undefined) {
1140
1276
  cursor = await dbTrx
1141
1277
  .objectStore('certificate_fields')
1142
1278
  .index('certificateId')
1143
- .openCursor(args.partial.certificateId);
1279
+ .openCursor(args.partial.certificateId, direction);
1144
1280
  }
1145
1281
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1146
- cursor = await dbTrx.objectStore('certificate_fields').index('userId').openCursor(args.partial.userId);
1282
+ cursor = await dbTrx.objectStore('certificate_fields').index('userId').openCursor(args.partial.userId, direction);
1147
1283
  }
1148
1284
  else {
1149
- cursor = await dbTrx.objectStore('certificate_fields').openCursor();
1285
+ cursor = await dbTrx.objectStore('certificate_fields').openCursor(null, direction);
1150
1286
  }
1151
1287
  let firstTime = true;
1152
1288
  while (cursor) {
@@ -1199,23 +1335,24 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1199
1335
  let skipped = 0;
1200
1336
  let count = 0;
1201
1337
  const dbTrx = this.toDbTrx(['certificates'], 'readonly', args.trx);
1338
+ const direction = args.orderDescending ? 'prev' : 'next';
1202
1339
  let cursor;
1203
1340
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.certificateId) {
1204
- cursor = await dbTrx.objectStore('certificates').openCursor(args.partial.certificateId);
1341
+ cursor = await dbTrx.objectStore('certificates').openCursor(args.partial.certificateId, direction);
1205
1342
  }
1206
1343
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1207
1344
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.type) && ((_e = args.partial) === null || _e === void 0 ? void 0 : _e.certifier) && ((_f = args.partial) === null || _f === void 0 ? void 0 : _f.serialNumber)) {
1208
1345
  cursor = await dbTrx
1209
1346
  .objectStore('certificates')
1210
1347
  .index('userId_type_certifier_serialNumber')
1211
- .openCursor([args.partial.userId, args.partial.type, args.partial.certifier, args.partial.serialNumber]);
1348
+ .openCursor([args.partial.userId, args.partial.type, args.partial.certifier, args.partial.serialNumber], direction);
1212
1349
  }
1213
1350
  else {
1214
- cursor = await dbTrx.objectStore('certificates').index('userId').openCursor(args.partial.userId);
1351
+ cursor = await dbTrx.objectStore('certificates').index('userId').openCursor(args.partial.userId, direction);
1215
1352
  }
1216
1353
  }
1217
1354
  else {
1218
- cursor = await dbTrx.objectStore('certificates').openCursor();
1355
+ cursor = await dbTrx.objectStore('certificates').openCursor(null, direction);
1219
1356
  }
1220
1357
  let firstTime = true;
1221
1358
  while (cursor) {
@@ -1290,18 +1427,22 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1290
1427
  let skipped = 0;
1291
1428
  let count = 0;
1292
1429
  const dbTrx = this.toDbTrx(['commissions'], 'readonly', args.trx);
1430
+ const direction = args.orderDescending ? 'prev' : 'next';
1293
1431
  let cursor;
1294
1432
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.commissionId) {
1295
- cursor = await dbTrx.objectStore('commissions').openCursor(args.partial.commissionId);
1433
+ cursor = await dbTrx.objectStore('commissions').openCursor(args.partial.commissionId, direction);
1296
1434
  }
1297
1435
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1298
- cursor = await dbTrx.objectStore('commissions').index('userId').openCursor(args.partial.userId);
1436
+ cursor = await dbTrx.objectStore('commissions').index('userId').openCursor(args.partial.userId, direction);
1299
1437
  }
1300
1438
  else if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.transactionId) !== undefined) {
1301
- cursor = await dbTrx.objectStore('commissions').index('transactionId').openCursor(args.partial.transactionId);
1439
+ cursor = await dbTrx
1440
+ .objectStore('commissions')
1441
+ .index('transactionId')
1442
+ .openCursor(args.partial.transactionId, direction);
1302
1443
  }
1303
1444
  else {
1304
- cursor = await dbTrx.objectStore('commissions').openCursor();
1445
+ cursor = await dbTrx.objectStore('commissions').openCursor(null, direction);
1305
1446
  }
1306
1447
  let firstTime = true;
1307
1448
  while (cursor) {
@@ -1356,12 +1497,13 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1356
1497
  let skipped = 0;
1357
1498
  let count = 0;
1358
1499
  const dbTrx = this.toDbTrx(['monitor_events'], 'readonly', args.trx);
1500
+ const direction = args.orderDescending ? 'prev' : 'next';
1359
1501
  let cursor;
1360
1502
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.id) {
1361
- cursor = await dbTrx.objectStore('monitor_events').openCursor(args.partial.id);
1503
+ cursor = await dbTrx.objectStore('monitor_events').openCursor(args.partial.id, direction);
1362
1504
  }
1363
1505
  else {
1364
- cursor = await dbTrx.objectStore('monitor_events').openCursor();
1506
+ cursor = await dbTrx.objectStore('monitor_events').openCursor(null, direction);
1365
1507
  }
1366
1508
  let firstTime = true;
1367
1509
  while (cursor) {
@@ -1410,23 +1552,24 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1410
1552
  let skipped = 0;
1411
1553
  let count = 0;
1412
1554
  const dbTrx = this.toDbTrx(['output_baskets'], 'readonly', args.trx);
1555
+ const direction = args.orderDescending ? 'prev' : 'next';
1413
1556
  let cursor;
1414
1557
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.basketId) {
1415
- cursor = await dbTrx.objectStore('output_baskets').openCursor(args.partial.basketId);
1558
+ cursor = await dbTrx.objectStore('output_baskets').openCursor(args.partial.basketId, direction);
1416
1559
  }
1417
1560
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1418
1561
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.name) !== undefined) {
1419
1562
  cursor = await dbTrx
1420
1563
  .objectStore('output_baskets')
1421
1564
  .index('name_userId')
1422
- .openCursor([args.partial.name, args.partial.userId]);
1565
+ .openCursor([args.partial.name, args.partial.userId], direction);
1423
1566
  }
1424
1567
  else {
1425
- cursor = await dbTrx.objectStore('output_baskets').index('userId').openCursor(args.partial.userId);
1568
+ cursor = await dbTrx.objectStore('output_baskets').index('userId').openCursor(args.partial.userId, direction);
1426
1569
  }
1427
1570
  }
1428
1571
  else {
1429
- cursor = await dbTrx.objectStore('output_baskets').openCursor();
1572
+ cursor = await dbTrx.objectStore('output_baskets').openCursor(null, direction);
1430
1573
  }
1431
1574
  let firstTime = true;
1432
1575
  while (cursor) {
@@ -1453,7 +1596,7 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1453
1596
  r.numberOfDesiredUTXOs !== args.partial.numberOfDesiredUTXOs)
1454
1597
  continue;
1455
1598
  if (args.partial.minimumDesiredUTXOValue !== undefined &&
1456
- r.numberOfDesiredSatoshis !== args.partial.minimumDesiredUTXOValue)
1599
+ r.minimumDesiredUTXOValue !== args.partial.minimumDesiredUTXOValue)
1457
1600
  continue;
1458
1601
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted)
1459
1602
  continue;
@@ -1494,32 +1637,36 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1494
1637
  stores.push('transactions');
1495
1638
  }
1496
1639
  const dbTrx = this.toDbTrx(stores, 'readonly', args.trx);
1640
+ const direction = args.orderDescending ? 'prev' : 'next';
1497
1641
  let cursor;
1498
1642
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.outputId) {
1499
- cursor = await dbTrx.objectStore('outputs').openCursor(args.partial.outputId);
1643
+ cursor = await dbTrx.objectStore('outputs').openCursor(args.partial.outputId, direction);
1500
1644
  }
1501
1645
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1502
1646
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.transactionId) && ((_e = args.partial) === null || _e === void 0 ? void 0 : _e.vout) !== undefined) {
1503
1647
  cursor = await dbTrx
1504
1648
  .objectStore('outputs')
1505
1649
  .index('transactionId_vout_userId')
1506
- .openCursor([args.partial.transactionId, args.partial.vout, args.partial.userId]);
1650
+ .openCursor([args.partial.transactionId, args.partial.vout, args.partial.userId], direction);
1507
1651
  }
1508
1652
  else {
1509
- cursor = await dbTrx.objectStore('outputs').index('userId').openCursor(args.partial.userId);
1653
+ cursor = await dbTrx.objectStore('outputs').index('userId').openCursor(args.partial.userId, direction);
1510
1654
  }
1511
1655
  }
1512
1656
  else if (((_f = args.partial) === null || _f === void 0 ? void 0 : _f.transactionId) !== undefined) {
1513
- cursor = await dbTrx.objectStore('outputs').index('transactionId').openCursor(args.partial.transactionId);
1657
+ cursor = await dbTrx
1658
+ .objectStore('outputs')
1659
+ .index('transactionId')
1660
+ .openCursor(args.partial.transactionId, direction);
1514
1661
  }
1515
1662
  else if (((_g = args.partial) === null || _g === void 0 ? void 0 : _g.basketId) !== undefined) {
1516
- cursor = await dbTrx.objectStore('outputs').index('basketId').openCursor(args.partial.basketId);
1663
+ cursor = await dbTrx.objectStore('outputs').index('basketId').openCursor(args.partial.basketId, direction);
1517
1664
  }
1518
1665
  else if (((_h = args.partial) === null || _h === void 0 ? void 0 : _h.spentBy) !== undefined) {
1519
- cursor = await dbTrx.objectStore('outputs').index('spentBy').openCursor(args.partial.spentBy);
1666
+ cursor = await dbTrx.objectStore('outputs').index('spentBy').openCursor(args.partial.spentBy, direction);
1520
1667
  }
1521
1668
  else {
1522
- cursor = await dbTrx.objectStore('outputs').openCursor();
1669
+ cursor = await dbTrx.objectStore('outputs').openCursor(null, direction);
1523
1670
  }
1524
1671
  let firstTime = true;
1525
1672
  while (cursor) {
@@ -1628,7 +1775,9 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1628
1775
  }, tagIds, isQueryModeAll);
1629
1776
  for (const o of results) {
1630
1777
  if (!args.noScript) {
1631
- await this.validateOutputScript(o);
1778
+ // Pass the transaction to avoid creating separate IDB operations
1779
+ // that would cause a passed transaction to auto-commit
1780
+ await this.validateOutputScript(o, args.trx);
1632
1781
  }
1633
1782
  else {
1634
1783
  o.lockingScript = undefined;
@@ -1642,23 +1791,24 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1642
1791
  let skipped = 0;
1643
1792
  let count = 0;
1644
1793
  const dbTrx = this.toDbTrx(['output_tags'], 'readonly', args.trx);
1794
+ const direction = args.orderDescending ? 'prev' : 'next';
1645
1795
  let cursor;
1646
1796
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.outputTagId) {
1647
- cursor = await dbTrx.objectStore('output_tags').openCursor(args.partial.outputTagId);
1797
+ cursor = await dbTrx.objectStore('output_tags').openCursor(args.partial.outputTagId, direction);
1648
1798
  }
1649
1799
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1650
1800
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.tag) !== undefined) {
1651
1801
  cursor = await dbTrx
1652
1802
  .objectStore('output_tags')
1653
1803
  .index('tag_userId')
1654
- .openCursor([args.partial.tag, args.partial.userId]);
1804
+ .openCursor([args.partial.tag, args.partial.userId], direction);
1655
1805
  }
1656
1806
  else {
1657
- cursor = await dbTrx.objectStore('output_tags').index('userId').openCursor(args.partial.userId);
1807
+ cursor = await dbTrx.objectStore('output_tags').index('userId').openCursor(args.partial.userId, direction);
1658
1808
  }
1659
1809
  }
1660
1810
  else {
1661
- cursor = await dbTrx.objectStore('output_tags').openCursor();
1811
+ cursor = await dbTrx.objectStore('output_tags').openCursor(null, direction);
1662
1812
  }
1663
1813
  let firstTime = true;
1664
1814
  while (cursor) {
@@ -1711,21 +1861,22 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1711
1861
  let skipped = 0;
1712
1862
  let count = 0;
1713
1863
  const dbTrx = this.toDbTrx(['sync_states'], 'readonly', args.trx);
1864
+ const direction = args.orderDescending ? 'prev' : 'next';
1714
1865
  let cursor;
1715
1866
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.syncStateId) {
1716
- cursor = await dbTrx.objectStore('sync_states').openCursor(args.partial.syncStateId);
1867
+ cursor = await dbTrx.objectStore('sync_states').openCursor(args.partial.syncStateId, direction);
1717
1868
  }
1718
1869
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1719
- cursor = await dbTrx.objectStore('sync_states').index('userId').openCursor(args.partial.userId);
1870
+ cursor = await dbTrx.objectStore('sync_states').index('userId').openCursor(args.partial.userId, direction);
1720
1871
  }
1721
1872
  else if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.refNum) !== undefined) {
1722
- cursor = await dbTrx.objectStore('sync_states').index('refNum').openCursor(args.partial.refNum);
1873
+ cursor = await dbTrx.objectStore('sync_states').index('refNum').openCursor(args.partial.refNum, direction);
1723
1874
  }
1724
1875
  else if (((_e = args.partial) === null || _e === void 0 ? void 0 : _e.status) !== undefined) {
1725
- cursor = await dbTrx.objectStore('sync_states').index('status').openCursor(args.partial.status);
1876
+ cursor = await dbTrx.objectStore('sync_states').index('status').openCursor(args.partial.status, direction);
1726
1877
  }
1727
1878
  else {
1728
- cursor = await dbTrx.objectStore('sync_states').openCursor();
1879
+ cursor = await dbTrx.objectStore('sync_states').openCursor(null, direction);
1729
1880
  }
1730
1881
  let firstTime = true;
1731
1882
  while (cursor) {
@@ -1760,7 +1911,7 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1760
1911
  continue;
1761
1912
  if (args.partial.satoshis !== undefined && r.satoshis !== args.partial.satoshis)
1762
1913
  continue;
1763
- if (args.partial.errorLocal && r.errorLocale !== args.partial.errorLocal)
1914
+ if (args.partial.errorLocal && r.errorLocal !== args.partial.errorLocal)
1764
1915
  continue;
1765
1916
  if (args.partial.errorOther && r.errorOther !== args.partial.errorOther)
1766
1917
  continue;
@@ -1798,32 +1949,36 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1798
1949
  stores.push('tx_labels_map');
1799
1950
  }
1800
1951
  const dbTrx = this.toDbTrx(stores, 'readonly', args.trx);
1952
+ const direction = args.orderDescending ? 'prev' : 'next';
1801
1953
  let cursor;
1802
1954
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.transactionId) {
1803
- cursor = await dbTrx.objectStore('transactions').openCursor(args.partial.transactionId);
1955
+ cursor = await dbTrx.objectStore('transactions').openCursor(args.partial.transactionId, direction);
1804
1956
  }
1805
1957
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1806
1958
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.status) !== undefined) {
1807
1959
  cursor = await dbTrx
1808
1960
  .objectStore('transactions')
1809
1961
  .index('status_userId')
1810
- .openCursor([args.partial.status, args.partial.userId]);
1962
+ .openCursor([args.partial.status, args.partial.userId], direction);
1811
1963
  }
1812
1964
  else {
1813
- cursor = await dbTrx.objectStore('transactions').index('userId').openCursor(args.partial.userId);
1965
+ cursor = await dbTrx.objectStore('transactions').index('userId').openCursor(args.partial.userId, direction);
1814
1966
  }
1815
1967
  }
1816
1968
  else if (((_e = args.partial) === null || _e === void 0 ? void 0 : _e.status) !== undefined) {
1817
- cursor = await dbTrx.objectStore('transactions').index('status').openCursor(args.partial.status);
1969
+ cursor = await dbTrx.objectStore('transactions').index('status').openCursor(args.partial.status, direction);
1818
1970
  }
1819
1971
  else if (((_f = args.partial) === null || _f === void 0 ? void 0 : _f.provenTxId) !== undefined) {
1820
- cursor = await dbTrx.objectStore('transactions').index('provenTxId').openCursor(args.partial.provenTxId);
1972
+ cursor = await dbTrx
1973
+ .objectStore('transactions')
1974
+ .index('provenTxId')
1975
+ .openCursor(args.partial.provenTxId, direction);
1821
1976
  }
1822
1977
  else if (((_g = args.partial) === null || _g === void 0 ? void 0 : _g.reference) !== undefined) {
1823
- cursor = await dbTrx.objectStore('transactions').index('reference').openCursor(args.partial.reference);
1978
+ cursor = await dbTrx.objectStore('transactions').index('reference').openCursor(args.partial.reference, direction);
1824
1979
  }
1825
1980
  else {
1826
- cursor = await dbTrx.objectStore('transactions').openCursor();
1981
+ cursor = await dbTrx.objectStore('transactions').openCursor(null, direction);
1827
1982
  }
1828
1983
  let firstTime = true;
1829
1984
  while (cursor) {
@@ -1917,23 +2072,24 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1917
2072
  let skipped = 0;
1918
2073
  let count = 0;
1919
2074
  const dbTrx = this.toDbTrx(['tx_labels'], 'readonly', args.trx);
2075
+ const direction = args.orderDescending ? 'prev' : 'next';
1920
2076
  let cursor;
1921
2077
  if ((_b = args.partial) === null || _b === void 0 ? void 0 : _b.txLabelId) {
1922
- cursor = await dbTrx.objectStore('tx_labels').openCursor(args.partial.txLabelId);
2078
+ cursor = await dbTrx.objectStore('tx_labels').openCursor(args.partial.txLabelId, direction);
1923
2079
  }
1924
2080
  else if (((_c = args.partial) === null || _c === void 0 ? void 0 : _c.userId) !== undefined) {
1925
2081
  if (((_d = args.partial) === null || _d === void 0 ? void 0 : _d.label) !== undefined) {
1926
2082
  cursor = await dbTrx
1927
2083
  .objectStore('tx_labels')
1928
2084
  .index('label_userId')
1929
- .openCursor([args.partial.label, args.partial.userId]);
2085
+ .openCursor([args.partial.label, args.partial.userId], direction);
1930
2086
  }
1931
2087
  else {
1932
- cursor = await dbTrx.objectStore('tx_labels').index('userId').openCursor(args.partial.userId);
2088
+ cursor = await dbTrx.objectStore('tx_labels').index('userId').openCursor(args.partial.userId, direction);
1933
2089
  }
1934
2090
  }
1935
2091
  else {
1936
- cursor = await dbTrx.objectStore('tx_labels').openCursor();
2092
+ cursor = await dbTrx.objectStore('tx_labels').openCursor(null, direction);
1937
2093
  }
1938
2094
  let firstTime = true;
1939
2095
  while (cursor) {
@@ -1984,7 +2140,8 @@ class StorageIdb extends StorageProvider_1.StorageProvider {
1984
2140
  let skipped = 0;
1985
2141
  let count = 0;
1986
2142
  const dbTrx = this.toDbTrx(['users'], 'readonly', args.trx);
1987
- let cursor = await dbTrx.objectStore('users').openCursor();
2143
+ const direction = args.orderDescending ? 'prev' : 'next';
2144
+ let cursor = await dbTrx.objectStore('users').openCursor(null, direction);
1988
2145
  let firstTime = true;
1989
2146
  while (cursor) {
1990
2147
  if (!firstTime)