@bopen-io/wallet-toolbox 1.7.19 → 1.7.20-idb-fix.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bopen-io/wallet-toolbox",
3
- "version": "1.7.19",
3
+ "version": "1.7.20-idb-fix.2",
4
4
  "description": "BRC100 conforming wallet, wallet storage and wallet signer components",
5
5
  "main": "./out/src/index.js",
6
6
  "types": "./out/src/index.d.ts",
@@ -114,6 +114,17 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
114
114
  ): IDBPTransaction<StorageIdbSchema, string[], 'readwrite' | 'readonly'> {
115
115
  if (trx) {
116
116
  const t = trx as IDBPTransaction<StorageIdbSchema, string[], 'readwrite' | 'readonly'>
117
+ // Check if the transaction is still active by trying to access an object store
118
+ try {
119
+ const storeToCheck = stores[0] || this.allStores[0]
120
+ t.objectStore(storeToCheck)
121
+ } catch (e) {
122
+ console.error(
123
+ `[StorageIdb.toDbTrx] Passed transaction already finished! stores=${stores.join(',')}, mode=${mode}`
124
+ )
125
+ console.error('[StorageIdb.toDbTrx] Stack trace:', new Error().stack)
126
+ throw e
127
+ }
117
128
  return t
118
129
  } else {
119
130
  if (!this.db) throw new Error('not initialized')
@@ -357,7 +368,8 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
357
368
  excludeSending: boolean,
358
369
  transactionId: number
359
370
  ): Promise<TableOutput | undefined> {
360
- const dbTrx = this.toDbTrx(['outputs', 'transactions'], 'readwrite')
371
+ // Include proven_txs in store list since findOutputs -> validateOutputScript needs it
372
+ const dbTrx = this.toDbTrx(['outputs', 'transactions', 'proven_txs'], 'readwrite')
361
373
  try {
362
374
  const txStatus: TransactionStatus[] = ['completed', 'unproven']
363
375
  if (!excludeSending) txStatus.push('sending')
@@ -519,21 +531,36 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
519
531
  filtered: (v: TableOutputTagMap) => void,
520
532
  userId?: number
521
533
  ): Promise<void> {
534
+ // Pre-compute outputTagIds for this user BEFORE opening cursor.
535
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
536
+ let userOutputTagIds: Set<number> | undefined
537
+ if (userId !== undefined) {
538
+ userOutputTagIds = new Set<number>()
539
+ const userTags = await this.findOutputTags({ partial: { userId } })
540
+ for (const tag of userTags) {
541
+ userOutputTagIds.add(tag.outputTagId)
542
+ }
543
+ }
544
+
522
545
  const offset = args.paged?.offset || 0
523
546
  let skipped = 0
524
547
  let count = 0
525
548
  const dbTrx = this.toDbTrx(['output_tags_map'], 'readonly', args.trx)
549
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
526
550
  let cursor:
527
551
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_tags_map', unknown, 'readwrite' | 'readonly'>
528
552
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_tags_map', 'outputTagId', 'readwrite' | 'readonly'>
529
553
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_tags_map', 'outputId', 'readwrite' | 'readonly'>
530
554
  | null
531
555
  if (args.partial?.outputTagId !== undefined) {
532
- cursor = await dbTrx.objectStore('output_tags_map').index('outputTagId').openCursor(args.partial.outputTagId)
556
+ cursor = await dbTrx
557
+ .objectStore('output_tags_map')
558
+ .index('outputTagId')
559
+ .openCursor(args.partial.outputTagId, direction)
533
560
  } else if (args.partial?.outputId !== undefined) {
534
- cursor = await dbTrx.objectStore('output_tags_map').index('outputId').openCursor(args.partial.outputId)
561
+ cursor = await dbTrx.objectStore('output_tags_map').index('outputId').openCursor(args.partial.outputId, direction)
535
562
  } else {
536
- cursor = await dbTrx.objectStore('output_tags_map').openCursor()
563
+ cursor = await dbTrx.objectStore('output_tags_map').openCursor(null, direction)
537
564
  }
538
565
  let firstTime = true
539
566
  while (cursor) {
@@ -550,9 +577,8 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
550
577
  if (args.partial.updated_at && r.updated_at.getTime() !== args.partial.updated_at.getTime()) continue
551
578
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted) continue
552
579
  }
553
- if (userId !== undefined && r.txid) {
554
- const count = await this.countOutputTags({ partial: { userId, outputTagId: r.outputTagId }, trx: args.trx })
555
- if (count === 0) continue
580
+ if (userOutputTagIds !== undefined && !userOutputTagIds.has(r.outputTagId)) {
581
+ continue
556
582
  }
557
583
  if (skipped < offset) {
558
584
  skipped++
@@ -585,10 +611,25 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
585
611
  'args.partial.inputBEEF',
586
612
  `undefined. ProvenTxReqs may not be found by inputBEEF value.`
587
613
  )
614
+
615
+ // Pre-compute txids for this user's transactions BEFORE opening cursor.
616
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
617
+ let userTxIds: Set<string> | undefined
618
+ if (userId !== undefined) {
619
+ userTxIds = new Set<string>()
620
+ const userTxs = await this.findTransactions({ partial: { userId }, noRawTx: true })
621
+ for (const tx of userTxs) {
622
+ if (tx.txid) {
623
+ userTxIds.add(tx.txid)
624
+ }
625
+ }
626
+ }
627
+
588
628
  const offset = args.paged?.offset || 0
589
629
  let skipped = 0
590
630
  let count = 0
591
631
  const dbTrx = this.toDbTrx(['proven_tx_reqs'], 'readonly', args.trx)
632
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
592
633
  let cursor:
593
634
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'proven_tx_reqs', unknown, 'readwrite' | 'readonly'>
594
635
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'proven_tx_reqs', 'provenTxId', 'readwrite' | 'readonly'>
@@ -597,17 +638,20 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
597
638
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'proven_tx_reqs', 'batch', 'readwrite' | 'readonly'>
598
639
  | null
599
640
  if (args.partial?.provenTxReqId) {
600
- cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(args.partial.provenTxReqId)
641
+ cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(args.partial.provenTxReqId, direction)
601
642
  } else if (args.partial?.provenTxId !== undefined) {
602
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('provenTxId').openCursor(args.partial.provenTxId)
643
+ cursor = await dbTrx
644
+ .objectStore('proven_tx_reqs')
645
+ .index('provenTxId')
646
+ .openCursor(args.partial.provenTxId, direction)
603
647
  } else if (args.partial?.txid !== undefined) {
604
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('txid').openCursor(args.partial.txid)
648
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('txid').openCursor(args.partial.txid, direction)
605
649
  } else if (args.partial?.status !== undefined) {
606
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('status').openCursor(args.partial.status)
650
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('status').openCursor(args.partial.status, direction)
607
651
  } else if (args.partial?.batch !== undefined) {
608
- cursor = await dbTrx.objectStore('proven_tx_reqs').index('batch').openCursor(args.partial.batch)
652
+ cursor = await dbTrx.objectStore('proven_tx_reqs').index('batch').openCursor(args.partial.batch, direction)
609
653
  } else {
610
- cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor()
654
+ cursor = await dbTrx.objectStore('proven_tx_reqs').openCursor(null, direction)
611
655
  }
612
656
  let firstTime = true
613
657
  while (cursor) {
@@ -616,6 +660,8 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
616
660
  firstTime = false
617
661
  const r = cursor.value
618
662
  if (args.since && args.since > r.updated_at) continue
663
+ if (args.status && args.status.length > 0 && !args.status.includes(r.status)) continue
664
+ if (args.txids && args.txids.length > 0 && !args.txids.includes(r.txid)) continue
619
665
  if (args.partial) {
620
666
  if (args.partial.provenTxReqId && r.provenTxReqId !== args.partial.provenTxReqId) continue
621
667
  if (args.partial.provenTxId && r.provenTxId !== args.partial.provenTxId) continue
@@ -629,9 +675,8 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
629
675
  if (args.partial.history && r.history !== args.partial.history) continue
630
676
  if (args.partial.notify && r.notify !== args.partial.notify) continue
631
677
  }
632
- if (userId !== undefined && r.txid) {
633
- const count = await this.countTransactions({ partial: { userId, txid: r.txid }, trx: args.trx })
634
- if (count === 0) continue
678
+ if (userTxIds !== undefined && r.txid && !userTxIds.has(r.txid)) {
679
+ continue
635
680
  }
636
681
  if (skipped < offset) {
637
682
  skipped++
@@ -660,20 +705,35 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
660
705
  'args.partial.merklePath',
661
706
  `undefined. ProvenTxs may not be found by merklePath value.`
662
707
  )
708
+
709
+ // Pre-compute provenTxIds for this user's transactions BEFORE opening cursor.
710
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
711
+ let userProvenTxIds: Set<number> | undefined
712
+ if (userId !== undefined) {
713
+ userProvenTxIds = new Set<number>()
714
+ const userTxs = await this.findTransactions({ partial: { userId }, noRawTx: true })
715
+ for (const tx of userTxs) {
716
+ if (tx.provenTxId !== undefined) {
717
+ userProvenTxIds.add(tx.provenTxId)
718
+ }
719
+ }
720
+ }
721
+
663
722
  const offset = args.paged?.offset || 0
664
723
  let skipped = 0
665
724
  let count = 0
666
725
  const dbTrx = this.toDbTrx(['proven_txs'], 'readonly', args.trx)
726
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
667
727
  let cursor:
668
728
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'proven_txs', unknown, 'readwrite' | 'readonly'>
669
729
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'proven_txs', 'txid', 'readwrite' | 'readonly'>
670
730
  | null
671
731
  if (args.partial?.provenTxId) {
672
- cursor = await dbTrx.objectStore('proven_txs').openCursor(args.partial.provenTxId)
732
+ cursor = await dbTrx.objectStore('proven_txs').openCursor(args.partial.provenTxId, direction)
673
733
  } else if (args.partial?.txid !== undefined) {
674
- cursor = await dbTrx.objectStore('proven_txs').index('txid').openCursor(args.partial.txid)
734
+ cursor = await dbTrx.objectStore('proven_txs').index('txid').openCursor(args.partial.txid, direction)
675
735
  } else {
676
- cursor = await dbTrx.objectStore('proven_txs').openCursor()
736
+ cursor = await dbTrx.objectStore('proven_txs').openCursor(null, direction)
677
737
  }
678
738
  let firstTime = true
679
739
  while (cursor) {
@@ -692,9 +752,8 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
692
752
  if (args.partial.blockHash && r.blockHash !== args.partial.blockHash) continue
693
753
  if (args.partial.merkleRoot && r.merkleRoot !== args.partial.merkleRoot) continue
694
754
  }
695
- if (userId !== undefined) {
696
- const count = await this.countTransactions({ partial: { userId, provenTxId: r.provenTxId }, trx: args.trx })
697
- if (count === 0) continue
755
+ if (userProvenTxIds !== undefined && !userProvenTxIds.has(r.provenTxId)) {
756
+ continue
698
757
  }
699
758
  if (skipped < offset) {
700
759
  skipped++
@@ -720,21 +779,36 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
720
779
  filtered: (v: TableTxLabelMap) => void,
721
780
  userId?: number
722
781
  ): Promise<void> {
782
+ // Pre-compute txLabelIds for this user BEFORE opening cursor.
783
+ // This avoids nested queries inside the cursor loop which cause IDB transaction timeouts.
784
+ let userTxLabelIds: Set<number> | undefined
785
+ if (userId !== undefined) {
786
+ userTxLabelIds = new Set<number>()
787
+ const userLabels = await this.findTxLabels({ partial: { userId } })
788
+ for (const label of userLabels) {
789
+ userTxLabelIds.add(label.txLabelId)
790
+ }
791
+ }
792
+
723
793
  const offset = args.paged?.offset || 0
724
794
  let skipped = 0
725
795
  let count = 0
726
796
  const dbTrx = this.toDbTrx(['tx_labels_map'], 'readonly', args.trx)
797
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
727
798
  let cursor:
728
799
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'tx_labels_map', unknown, 'readwrite' | 'readonly'>
729
800
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'tx_labels_map', 'transactionId', 'readwrite' | 'readonly'>
730
801
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'tx_labels_map', 'txLabelId', 'readwrite' | 'readonly'>
731
802
  | null
732
803
  if (args.partial?.transactionId !== undefined) {
733
- cursor = await dbTrx.objectStore('tx_labels_map').index('transactionId').openCursor(args.partial.transactionId)
804
+ cursor = await dbTrx
805
+ .objectStore('tx_labels_map')
806
+ .index('transactionId')
807
+ .openCursor(args.partial.transactionId, direction)
734
808
  } else if (args.partial?.txLabelId !== undefined) {
735
- cursor = await dbTrx.objectStore('tx_labels_map').index('txLabelId').openCursor(args.partial.txLabelId)
809
+ cursor = await dbTrx.objectStore('tx_labels_map').index('txLabelId').openCursor(args.partial.txLabelId, direction)
736
810
  } else {
737
- cursor = await dbTrx.objectStore('tx_labels_map').openCursor()
811
+ cursor = await dbTrx.objectStore('tx_labels_map').openCursor(null, direction)
738
812
  }
739
813
  let firstTime = true
740
814
  while (cursor) {
@@ -743,6 +817,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
743
817
  firstTime = false
744
818
  const r = cursor.value
745
819
  if (args.since && args.since > r.updated_at) continue
820
+ if (args.labelIds && !args.labelIds.includes(r.txLabelId)) continue
746
821
  if (args.partial) {
747
822
  if (args.partial.txLabelId && r.txLabelId !== args.partial.txLabelId) continue
748
823
  if (args.partial.transactionId && r.transactionId !== args.partial.transactionId) continue
@@ -750,9 +825,8 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
750
825
  if (args.partial.updated_at && r.updated_at.getTime() !== args.partial.updated_at.getTime()) continue
751
826
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted) continue
752
827
  }
753
- if (userId !== undefined) {
754
- const count = await this.countTxLabels({ partial: { userId, txLabelId: r.txLabelId }, trx: args.trx })
755
- if (count === 0) continue
828
+ if (userTxLabelIds !== undefined && !userTxLabelIds.has(r.txLabelId)) {
829
+ continue
756
830
  }
757
831
  if (skipped < offset) {
758
832
  skipped++
@@ -1014,7 +1088,16 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1014
1088
  }
1015
1089
  const u = this.validatePartialForUpdate(update)
1016
1090
  const dbTrx = this.toDbTrx([storeName], 'readwrite', trx)
1017
- const store = dbTrx.objectStore(storeName)
1091
+ let store: any
1092
+ try {
1093
+ store = dbTrx.objectStore(storeName)
1094
+ } catch (e) {
1095
+ console.error(
1096
+ `[StorageIdb.updateIdb] objectStore('${storeName}') failed for id=${JSON.stringify(id)}, trx=${!!trx}:`,
1097
+ e
1098
+ )
1099
+ throw e
1100
+ }
1018
1101
  const ids = Array.isArray(id) ? id : [id]
1019
1102
  try {
1020
1103
  for (const i of ids) {
@@ -1189,7 +1272,13 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1189
1272
  await tx.done
1190
1273
  return r
1191
1274
  } catch (err) {
1192
- tx.abort()
1275
+ // Log more detail about transaction errors to help debug IDB issues
1276
+ console.error('[StorageIdb] Transaction error:', err instanceof Error ? err.message : err)
1277
+ try {
1278
+ tx.abort()
1279
+ } catch (abortErr) {
1280
+ console.error('[StorageIdb] Error aborting transaction:', abortErr)
1281
+ }
1193
1282
  await tx.done
1194
1283
  throw err
1195
1284
  }
@@ -1203,6 +1292,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1203
1292
  let skipped = 0
1204
1293
  let count = 0
1205
1294
  const dbTrx = this.toDbTrx(['certificate_fields'], 'readonly', args.trx)
1295
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1206
1296
  let cursor:
1207
1297
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'certificate_fields', unknown, 'readwrite' | 'readonly'>
1208
1298
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'certificate_fields', 'userId', 'readwrite' | 'readonly'>
@@ -1212,11 +1302,11 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1212
1302
  cursor = await dbTrx
1213
1303
  .objectStore('certificate_fields')
1214
1304
  .index('certificateId')
1215
- .openCursor(args.partial.certificateId)
1305
+ .openCursor(args.partial.certificateId, direction)
1216
1306
  } else if (args.partial?.userId !== undefined) {
1217
- cursor = await dbTrx.objectStore('certificate_fields').index('userId').openCursor(args.partial.userId)
1307
+ cursor = await dbTrx.objectStore('certificate_fields').index('userId').openCursor(args.partial.userId, direction)
1218
1308
  } else {
1219
- cursor = await dbTrx.objectStore('certificate_fields').openCursor()
1309
+ cursor = await dbTrx.objectStore('certificate_fields').openCursor(null, direction)
1220
1310
  }
1221
1311
  let firstTime = true
1222
1312
  while (cursor) {
@@ -1258,6 +1348,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1258
1348
  let skipped = 0
1259
1349
  let count = 0
1260
1350
  const dbTrx = this.toDbTrx(['certificates'], 'readonly', args.trx)
1351
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1261
1352
  let cursor:
1262
1353
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'certificates', unknown, 'readwrite' | 'readonly'>
1263
1354
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'certificates', 'userId', 'readwrite' | 'readonly'>
@@ -1270,18 +1361,21 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1270
1361
  >
1271
1362
  | null
1272
1363
  if (args.partial?.certificateId) {
1273
- cursor = await dbTrx.objectStore('certificates').openCursor(args.partial.certificateId)
1364
+ cursor = await dbTrx.objectStore('certificates').openCursor(args.partial.certificateId, direction)
1274
1365
  } else if (args.partial?.userId !== undefined) {
1275
1366
  if (args.partial?.type && args.partial?.certifier && args.partial?.serialNumber) {
1276
1367
  cursor = await dbTrx
1277
1368
  .objectStore('certificates')
1278
1369
  .index('userId_type_certifier_serialNumber')
1279
- .openCursor([args.partial.userId, args.partial.type, args.partial.certifier, args.partial.serialNumber])
1370
+ .openCursor(
1371
+ [args.partial.userId, args.partial.type, args.partial.certifier, args.partial.serialNumber],
1372
+ direction
1373
+ )
1280
1374
  } else {
1281
- cursor = await dbTrx.objectStore('certificates').index('userId').openCursor(args.partial.userId)
1375
+ cursor = await dbTrx.objectStore('certificates').index('userId').openCursor(args.partial.userId, direction)
1282
1376
  }
1283
1377
  } else {
1284
- cursor = await dbTrx.objectStore('certificates').openCursor()
1378
+ cursor = await dbTrx.objectStore('certificates').openCursor(null, direction)
1285
1379
  }
1286
1380
  let firstTime = true
1287
1381
  while (cursor) {
@@ -1341,19 +1435,23 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1341
1435
  let skipped = 0
1342
1436
  let count = 0
1343
1437
  const dbTrx = this.toDbTrx(['commissions'], 'readonly', args.trx)
1438
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1344
1439
  let cursor:
1345
1440
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'commissions', unknown, 'readwrite' | 'readonly'>
1346
1441
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'commissions', 'userId', 'readwrite' | 'readonly'>
1347
1442
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'commissions', 'transactionId', 'readwrite' | 'readonly'>
1348
1443
  | null
1349
1444
  if (args.partial?.commissionId) {
1350
- cursor = await dbTrx.objectStore('commissions').openCursor(args.partial.commissionId)
1445
+ cursor = await dbTrx.objectStore('commissions').openCursor(args.partial.commissionId, direction)
1351
1446
  } else if (args.partial?.userId !== undefined) {
1352
- cursor = await dbTrx.objectStore('commissions').index('userId').openCursor(args.partial.userId)
1447
+ cursor = await dbTrx.objectStore('commissions').index('userId').openCursor(args.partial.userId, direction)
1353
1448
  } else if (args.partial?.transactionId !== undefined) {
1354
- cursor = await dbTrx.objectStore('commissions').index('transactionId').openCursor(args.partial.transactionId)
1449
+ cursor = await dbTrx
1450
+ .objectStore('commissions')
1451
+ .index('transactionId')
1452
+ .openCursor(args.partial.transactionId, direction)
1355
1453
  } else {
1356
- cursor = await dbTrx.objectStore('commissions').openCursor()
1454
+ cursor = await dbTrx.objectStore('commissions').openCursor(null, direction)
1357
1455
  }
1358
1456
  let firstTime = true
1359
1457
  while (cursor) {
@@ -1396,6 +1494,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1396
1494
  let skipped = 0
1397
1495
  let count = 0
1398
1496
  const dbTrx = this.toDbTrx(['monitor_events'], 'readonly', args.trx)
1497
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1399
1498
  let cursor: IDBPCursorWithValue<
1400
1499
  StorageIdbSchema,
1401
1500
  string[],
@@ -1404,9 +1503,9 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1404
1503
  'readwrite' | 'readonly'
1405
1504
  > | null
1406
1505
  if (args.partial?.id) {
1407
- cursor = await dbTrx.objectStore('monitor_events').openCursor(args.partial.id)
1506
+ cursor = await dbTrx.objectStore('monitor_events').openCursor(args.partial.id, direction)
1408
1507
  } else {
1409
- cursor = await dbTrx.objectStore('monitor_events').openCursor()
1508
+ cursor = await dbTrx.objectStore('monitor_events').openCursor(null, direction)
1410
1509
  }
1411
1510
  let firstTime = true
1412
1511
  while (cursor) {
@@ -1446,24 +1545,25 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1446
1545
  let skipped = 0
1447
1546
  let count = 0
1448
1547
  const dbTrx = this.toDbTrx(['output_baskets'], 'readonly', args.trx)
1548
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1449
1549
  let cursor:
1450
1550
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_baskets', unknown, 'readwrite' | 'readonly'>
1451
1551
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_baskets', 'userId', 'readwrite' | 'readonly'>
1452
1552
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_baskets', 'name_userId', 'readwrite' | 'readonly'>
1453
1553
  | null
1454
1554
  if (args.partial?.basketId) {
1455
- cursor = await dbTrx.objectStore('output_baskets').openCursor(args.partial.basketId)
1555
+ cursor = await dbTrx.objectStore('output_baskets').openCursor(args.partial.basketId, direction)
1456
1556
  } else if (args.partial?.userId !== undefined) {
1457
1557
  if (args.partial?.name !== undefined) {
1458
1558
  cursor = await dbTrx
1459
1559
  .objectStore('output_baskets')
1460
1560
  .index('name_userId')
1461
- .openCursor([args.partial.name, args.partial.userId])
1561
+ .openCursor([args.partial.name, args.partial.userId], direction)
1462
1562
  } else {
1463
- cursor = await dbTrx.objectStore('output_baskets').index('userId').openCursor(args.partial.userId)
1563
+ cursor = await dbTrx.objectStore('output_baskets').index('userId').openCursor(args.partial.userId, direction)
1464
1564
  }
1465
1565
  } else {
1466
- cursor = await dbTrx.objectStore('output_baskets').openCursor()
1566
+ cursor = await dbTrx.objectStore('output_baskets').openCursor(null, direction)
1467
1567
  }
1468
1568
  let firstTime = true
1469
1569
  while (cursor) {
@@ -1485,7 +1585,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1485
1585
  continue
1486
1586
  if (
1487
1587
  args.partial.minimumDesiredUTXOValue !== undefined &&
1488
- r.numberOfDesiredSatoshis !== args.partial.minimumDesiredUTXOValue
1588
+ r.minimumDesiredUTXOValue !== args.partial.minimumDesiredUTXOValue
1489
1589
  )
1490
1590
  continue
1491
1591
  if (args.partial.isDeleted !== undefined && r.isDeleted !== args.partial.isDeleted) continue
@@ -1533,6 +1633,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1533
1633
  stores.push('transactions')
1534
1634
  }
1535
1635
  const dbTrx = this.toDbTrx(stores, 'readonly', args.trx)
1636
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1536
1637
  let cursor:
1537
1638
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'outputs', unknown, 'readwrite' | 'readonly'>
1538
1639
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'outputs', 'userId', 'readwrite' | 'readonly'>
@@ -1548,24 +1649,27 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1548
1649
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'outputs', 'spentBy', 'readwrite' | 'readonly'>
1549
1650
  | null
1550
1651
  if (args.partial?.outputId) {
1551
- cursor = await dbTrx.objectStore('outputs').openCursor(args.partial.outputId)
1652
+ cursor = await dbTrx.objectStore('outputs').openCursor(args.partial.outputId, direction)
1552
1653
  } else if (args.partial?.userId !== undefined) {
1553
1654
  if (args.partial?.transactionId && args.partial?.vout !== undefined) {
1554
1655
  cursor = await dbTrx
1555
1656
  .objectStore('outputs')
1556
1657
  .index('transactionId_vout_userId')
1557
- .openCursor([args.partial.transactionId, args.partial.vout, args.partial.userId])
1658
+ .openCursor([args.partial.transactionId, args.partial.vout, args.partial.userId], direction)
1558
1659
  } else {
1559
- cursor = await dbTrx.objectStore('outputs').index('userId').openCursor(args.partial.userId)
1660
+ cursor = await dbTrx.objectStore('outputs').index('userId').openCursor(args.partial.userId, direction)
1560
1661
  }
1561
1662
  } else if (args.partial?.transactionId !== undefined) {
1562
- cursor = await dbTrx.objectStore('outputs').index('transactionId').openCursor(args.partial.transactionId)
1663
+ cursor = await dbTrx
1664
+ .objectStore('outputs')
1665
+ .index('transactionId')
1666
+ .openCursor(args.partial.transactionId, direction)
1563
1667
  } else if (args.partial?.basketId !== undefined) {
1564
- cursor = await dbTrx.objectStore('outputs').index('basketId').openCursor(args.partial.basketId)
1668
+ cursor = await dbTrx.objectStore('outputs').index('basketId').openCursor(args.partial.basketId, direction)
1565
1669
  } else if (args.partial?.spentBy !== undefined) {
1566
- cursor = await dbTrx.objectStore('outputs').index('spentBy').openCursor(args.partial.spentBy)
1670
+ cursor = await dbTrx.objectStore('outputs').index('spentBy').openCursor(args.partial.spentBy, direction)
1567
1671
  } else {
1568
- cursor = await dbTrx.objectStore('outputs').openCursor()
1672
+ cursor = await dbTrx.objectStore('outputs').openCursor(null, direction)
1569
1673
  }
1570
1674
  let firstTime = true
1571
1675
  while (cursor) {
@@ -1649,7 +1753,9 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1649
1753
  )
1650
1754
  for (const o of results) {
1651
1755
  if (!args.noScript) {
1652
- await this.validateOutputScript(o)
1756
+ // Pass the transaction to avoid creating separate IDB operations
1757
+ // that would cause a passed transaction to auto-commit
1758
+ await this.validateOutputScript(o, args.trx)
1653
1759
  } else {
1654
1760
  o.lockingScript = undefined
1655
1761
  }
@@ -1662,24 +1768,25 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1662
1768
  let skipped = 0
1663
1769
  let count = 0
1664
1770
  const dbTrx = this.toDbTrx(['output_tags'], 'readonly', args.trx)
1771
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1665
1772
  let cursor:
1666
1773
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_tags', unknown, 'readwrite' | 'readonly'>
1667
1774
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_tags', 'userId', 'readwrite' | 'readonly'>
1668
1775
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'output_tags', 'tag_userId', 'readwrite' | 'readonly'>
1669
1776
  | null
1670
1777
  if (args.partial?.outputTagId) {
1671
- cursor = await dbTrx.objectStore('output_tags').openCursor(args.partial.outputTagId)
1778
+ cursor = await dbTrx.objectStore('output_tags').openCursor(args.partial.outputTagId, direction)
1672
1779
  } else if (args.partial?.userId !== undefined) {
1673
1780
  if (args.partial?.tag !== undefined) {
1674
1781
  cursor = await dbTrx
1675
1782
  .objectStore('output_tags')
1676
1783
  .index('tag_userId')
1677
- .openCursor([args.partial.tag, args.partial.userId])
1784
+ .openCursor([args.partial.tag, args.partial.userId], direction)
1678
1785
  } else {
1679
- cursor = await dbTrx.objectStore('output_tags').index('userId').openCursor(args.partial.userId)
1786
+ cursor = await dbTrx.objectStore('output_tags').index('userId').openCursor(args.partial.userId, direction)
1680
1787
  }
1681
1788
  } else {
1682
- cursor = await dbTrx.objectStore('output_tags').openCursor()
1789
+ cursor = await dbTrx.objectStore('output_tags').openCursor(null, direction)
1683
1790
  }
1684
1791
  let firstTime = true
1685
1792
  while (cursor) {
@@ -1725,6 +1832,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1725
1832
  let skipped = 0
1726
1833
  let count = 0
1727
1834
  const dbTrx = this.toDbTrx(['sync_states'], 'readonly', args.trx)
1835
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1728
1836
  let cursor:
1729
1837
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'sync_states', unknown, 'readwrite' | 'readonly'>
1730
1838
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'sync_states', 'userId', 'readwrite' | 'readonly'>
@@ -1732,15 +1840,15 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1732
1840
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'sync_states', 'status', 'readwrite' | 'readonly'>
1733
1841
  | null
1734
1842
  if (args.partial?.syncStateId) {
1735
- cursor = await dbTrx.objectStore('sync_states').openCursor(args.partial.syncStateId)
1843
+ cursor = await dbTrx.objectStore('sync_states').openCursor(args.partial.syncStateId, direction)
1736
1844
  } else if (args.partial?.userId !== undefined) {
1737
- cursor = await dbTrx.objectStore('sync_states').index('userId').openCursor(args.partial.userId)
1845
+ cursor = await dbTrx.objectStore('sync_states').index('userId').openCursor(args.partial.userId, direction)
1738
1846
  } else if (args.partial?.refNum !== undefined) {
1739
- cursor = await dbTrx.objectStore('sync_states').index('refNum').openCursor(args.partial.refNum)
1847
+ cursor = await dbTrx.objectStore('sync_states').index('refNum').openCursor(args.partial.refNum, direction)
1740
1848
  } else if (args.partial?.status !== undefined) {
1741
- cursor = await dbTrx.objectStore('sync_states').index('status').openCursor(args.partial.status)
1849
+ cursor = await dbTrx.objectStore('sync_states').index('status').openCursor(args.partial.status, direction)
1742
1850
  } else {
1743
- cursor = await dbTrx.objectStore('sync_states').openCursor()
1851
+ cursor = await dbTrx.objectStore('sync_states').openCursor(null, direction)
1744
1852
  }
1745
1853
  let firstTime = true
1746
1854
  while (cursor) {
@@ -1761,7 +1869,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1761
1869
  if (args.partial.refNum !== undefined && r.refNum !== args.partial.refNum) continue
1762
1870
  if (args.partial.when && r.when?.getTime() !== args.partial.when.getTime()) continue
1763
1871
  if (args.partial.satoshis !== undefined && r.satoshis !== args.partial.satoshis) continue
1764
- if (args.partial.errorLocal && r.errorLocale !== args.partial.errorLocal) continue
1872
+ if (args.partial.errorLocal && r.errorLocal !== args.partial.errorLocal) continue
1765
1873
  if (args.partial.errorOther && r.errorOther !== args.partial.errorOther) continue
1766
1874
  }
1767
1875
  if (skipped < offset) {
@@ -1804,6 +1912,7 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1804
1912
  stores.push('tx_labels_map')
1805
1913
  }
1806
1914
  const dbTrx = this.toDbTrx(stores, 'readonly', args.trx)
1915
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1807
1916
  let cursor:
1808
1917
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'transactions', unknown, 'readwrite' | 'readonly'>
1809
1918
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'transactions', 'userId', 'readwrite' | 'readonly'>
@@ -1813,24 +1922,27 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1813
1922
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'transactions', 'reference', 'readwrite' | 'readonly'>
1814
1923
  | null
1815
1924
  if (args.partial?.transactionId) {
1816
- cursor = await dbTrx.objectStore('transactions').openCursor(args.partial.transactionId)
1925
+ cursor = await dbTrx.objectStore('transactions').openCursor(args.partial.transactionId, direction)
1817
1926
  } else if (args.partial?.userId !== undefined) {
1818
1927
  if (args.partial?.status !== undefined) {
1819
1928
  cursor = await dbTrx
1820
1929
  .objectStore('transactions')
1821
1930
  .index('status_userId')
1822
- .openCursor([args.partial.status, args.partial.userId])
1931
+ .openCursor([args.partial.status, args.partial.userId], direction)
1823
1932
  } else {
1824
- cursor = await dbTrx.objectStore('transactions').index('userId').openCursor(args.partial.userId)
1933
+ cursor = await dbTrx.objectStore('transactions').index('userId').openCursor(args.partial.userId, direction)
1825
1934
  }
1826
1935
  } else if (args.partial?.status !== undefined) {
1827
- cursor = await dbTrx.objectStore('transactions').index('status').openCursor(args.partial.status)
1936
+ cursor = await dbTrx.objectStore('transactions').index('status').openCursor(args.partial.status, direction)
1828
1937
  } else if (args.partial?.provenTxId !== undefined) {
1829
- cursor = await dbTrx.objectStore('transactions').index('provenTxId').openCursor(args.partial.provenTxId)
1938
+ cursor = await dbTrx
1939
+ .objectStore('transactions')
1940
+ .index('provenTxId')
1941
+ .openCursor(args.partial.provenTxId, direction)
1830
1942
  } else if (args.partial?.reference !== undefined) {
1831
- cursor = await dbTrx.objectStore('transactions').index('reference').openCursor(args.partial.reference)
1943
+ cursor = await dbTrx.objectStore('transactions').index('reference').openCursor(args.partial.reference, direction)
1832
1944
  } else {
1833
- cursor = await dbTrx.objectStore('transactions').openCursor()
1945
+ cursor = await dbTrx.objectStore('transactions').openCursor(null, direction)
1834
1946
  }
1835
1947
  let firstTime = true
1836
1948
  while (cursor) {
@@ -1912,24 +2024,25 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1912
2024
  let skipped = 0
1913
2025
  let count = 0
1914
2026
  const dbTrx = this.toDbTrx(['tx_labels'], 'readonly', args.trx)
2027
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
1915
2028
  let cursor:
1916
2029
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'tx_labels', unknown, 'readwrite' | 'readonly'>
1917
2030
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'tx_labels', 'userId', 'readwrite' | 'readonly'>
1918
2031
  | IDBPCursorWithValue<StorageIdbSchema, string[], 'tx_labels', 'label_userId', 'readwrite' | 'readonly'>
1919
2032
  | null
1920
2033
  if (args.partial?.txLabelId) {
1921
- cursor = await dbTrx.objectStore('tx_labels').openCursor(args.partial.txLabelId)
2034
+ cursor = await dbTrx.objectStore('tx_labels').openCursor(args.partial.txLabelId, direction)
1922
2035
  } else if (args.partial?.userId !== undefined) {
1923
2036
  if (args.partial?.label !== undefined) {
1924
2037
  cursor = await dbTrx
1925
2038
  .objectStore('tx_labels')
1926
2039
  .index('label_userId')
1927
- .openCursor([args.partial.label, args.partial.userId])
2040
+ .openCursor([args.partial.label, args.partial.userId], direction)
1928
2041
  } else {
1929
- cursor = await dbTrx.objectStore('tx_labels').index('userId').openCursor(args.partial.userId)
2042
+ cursor = await dbTrx.objectStore('tx_labels').index('userId').openCursor(args.partial.userId, direction)
1930
2043
  }
1931
2044
  } else {
1932
- cursor = await dbTrx.objectStore('tx_labels').openCursor()
2045
+ cursor = await dbTrx.objectStore('tx_labels').openCursor(null, direction)
1933
2046
  }
1934
2047
  let firstTime = true
1935
2048
  while (cursor) {
@@ -1970,7 +2083,8 @@ export class StorageIdb extends StorageProvider implements WalletStorageProvider
1970
2083
  let skipped = 0
1971
2084
  let count = 0
1972
2085
  const dbTrx = this.toDbTrx(['users'], 'readonly', args.trx)
1973
- let cursor = await dbTrx.objectStore('users').openCursor()
2086
+ const direction: IDBCursorDirection = args.orderDescending ? 'prev' : 'next'
2087
+ let cursor = await dbTrx.objectStore('users').openCursor(null, direction)
1974
2088
  let firstTime = true
1975
2089
  while (cursor) {
1976
2090
  if (!firstTime) cursor = await cursor.continue()