@bsv/wallet-toolbox 1.3.0 → 1.3.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": "@bsv/wallet-toolbox",
3
- "version": "1.3.0",
3
+ "version": "1.3.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",
@@ -32,7 +32,7 @@
32
32
  "dependencies": {
33
33
  "@bsv/auth-express-middleware": "^1.1.2",
34
34
  "@bsv/payment-express-middleware": "^1.0.6",
35
- "@bsv/sdk": "^1.4.19",
35
+ "@bsv/sdk": "^1.4.20",
36
36
  "express": "^4.21.2",
37
37
  "knex": "^3.1.0",
38
38
  "mysql2": "^3.12.0",
@@ -466,8 +466,8 @@ export class WhatsOnChain extends SdkWhatsOnChain {
466
466
  headers: this.getHttpHeaders()
467
467
  }
468
468
 
469
- const response = await this.httpClient.request<WhatsOnChainUtxoStatus[]>(
470
- `${this.URL}/script/${scriptHash}/unspent`,
469
+ const response = await this.httpClient.request<WhatsOnChainUtxoStatus>(
470
+ `${this.URL}/script/${scriptHash}/unspent/all`,
471
471
  requestOptions
472
472
  )
473
473
  if (response.statusText === 'Too Many Requests' && retry < 2) {
@@ -479,30 +479,31 @@ export class WhatsOnChain extends SdkWhatsOnChain {
479
479
  if (!response.data || !response.ok || response.status !== 200)
480
480
  throw new sdk.WERR_INVALID_OPERATION(`WoC getUtxoStatus response ${response.statusText}`)
481
481
 
482
- if (Array.isArray(response.data)) {
483
- const data = response.data
484
- if (data.length === 0) {
485
- r.status = 'success'
486
- r.error = undefined
487
- r.isUtxo = false
488
- } else {
489
- r.status = 'success'
490
- r.error = undefined
491
- for (const s of data) {
492
- r.details.push({
493
- txid: s.tx_hash,
494
- satoshis: s.value,
495
- height: s.height,
496
- index: s.tx_pos
497
- })
498
- }
499
- if (outpoint) {
500
- const { txid, vout } = parseWalletOutpoint(outpoint)
501
- r.isUtxo = r.details.find(d => d.txid === txid && d.index === vout) !== undefined
502
- } else r.isUtxo = r.details.length > 0
503
- }
482
+ const data = response.data
483
+
484
+ if (data.script !== scriptHash || !Array.isArray(data.result)) {
485
+ throw new sdk.WERR_INTERNAL('data. is not an array')
486
+ }
487
+
488
+ if (data.result.length === 0) {
489
+ r.status = 'success'
490
+ r.error = undefined
491
+ r.isUtxo = false
504
492
  } else {
505
- throw new sdk.WERR_INTERNAL('data is not an array')
493
+ r.status = 'success'
494
+ r.error = undefined
495
+ for (const s of data.result) {
496
+ r.details.push({
497
+ txid: s.tx_hash,
498
+ satoshis: s.value,
499
+ height: s.height,
500
+ index: s.tx_pos
501
+ })
502
+ }
503
+ if (outpoint) {
504
+ const { txid, vout } = parseWalletOutpoint(outpoint)
505
+ r.isUtxo = r.details.find(d => d.txid === txid && d.index === vout) !== undefined
506
+ } else r.isUtxo = r.details.length > 0
506
507
  }
507
508
 
508
509
  return r
@@ -661,13 +662,6 @@ interface WhatsOnChainTscProof {
661
662
  txOrId: string
662
663
  }
663
664
 
664
- interface WhatsOnChainUtxoStatus {
665
- value: number
666
- height: number
667
- tx_pos: number
668
- tx_hash: string
669
- }
670
-
671
665
  interface WhatsOnChainScriptHashHistory {
672
666
  tx_hash: string
673
667
  height?: number
@@ -691,3 +685,40 @@ interface WhatsOnChainTxsStatusData {
691
685
  */
692
686
  error?: string
693
687
  }
688
+
689
+ /**
690
+ * GET https://api.whatsonchain.com/v1/bsv/<network>/script/<scriptHash>/unspent/all
691
+ *
692
+ * Response
693
+ {
694
+ "error":"",
695
+ "status":200,
696
+ "statusText":"OK",
697
+ "ok":true,
698
+ "data":{
699
+ "script":"d3ef8eeb691e7405caca142bfcd6f499b142884d7883e6701a0ee76047b4af32",
700
+ "result":[
701
+ {
702
+ "height":893652,
703
+ "tx_pos":11,
704
+ "tx_hash":"2178a1e93d46edda946d9069f9b157ddfacb451fee0278e657941f09bfdb5d8f",
705
+ "value":1005,
706
+ "isSpentInMempoolTx":false,
707
+ "status":"confirmed"
708
+ }
709
+ ]
710
+ }
711
+ }
712
+ *
713
+ */
714
+ interface WhatsOnChainUtxoStatus {
715
+ script: string
716
+ result: {
717
+ value: number
718
+ height: number
719
+ tx_pos: number
720
+ tx_hash: string
721
+ isSpentInMempoolTx: boolean
722
+ status: string // 'confirmed'
723
+ }[]
724
+ }
@@ -3,11 +3,13 @@ import { TableOutput, TableOutputBasket, TableOutputTag } from '../index.client'
3
3
  import { asString, sdk, verifyId, verifyInteger, verifyOne } from '../../index.client'
4
4
  import { StorageKnex } from '../StorageKnex'
5
5
  import { ValidListOutputsArgs } from '../../sdk'
6
+ import { spec } from 'node:test/reporters'
6
7
 
7
8
  interface ListOutputsSpecOp {
8
9
  name: string
9
10
  useBasket?: string
10
11
  ignoreLimit?: boolean
12
+ includeSpent?: boolean
11
13
  includeOutputScripts?: boolean
12
14
  resultFromTags?: (
13
15
  s: StorageKnex,
@@ -62,6 +64,7 @@ const basketToSpecOp: Record<string, ListOutputsSpecOp> = {
62
64
  name: 'invalidChangeOutputs',
63
65
  useBasket: 'default',
64
66
  ignoreLimit: true,
67
+ includeSpent: false,
65
68
  includeOutputScripts: true,
66
69
  tagsToIntercept: ['release', 'all'],
67
70
  filterOutputs: async (
@@ -71,7 +74,8 @@ const basketToSpecOp: Record<string, ListOutputsSpecOp> = {
71
74
  specOpTags: string[],
72
75
  outputs: TableOutput[]
73
76
  ): Promise<TableOutput[]> => {
74
- const filteredOutputs: TableOutput[] = []
77
+ const updateToSpent: TableOutput[] = []
78
+ const updateToSpendable: TableOutput[] = []
75
79
  const services = s.getServices()
76
80
  for (const o of outputs) {
77
81
  await s.validateOutputScript(o)
@@ -84,17 +88,23 @@ const basketToSpecOp: Record<string, ListOutputsSpecOp> = {
84
88
  } else {
85
89
  ok = undefined
86
90
  }
87
- if (ok === false) {
88
- filteredOutputs.push(o)
91
+ if (ok === false && o.spendable) {
92
+ updateToSpent.push(o)
93
+ } else if (ok === true && !o.spendable) {
94
+ updateToSpendable.push(o)
89
95
  }
90
96
  }
91
97
  if (specOpTags.indexOf('release') >= 0) {
92
- for (const o of filteredOutputs) {
98
+ for (const o of updateToSpent) {
93
99
  await s.updateOutput(o.outputId, { spendable: false })
94
100
  o.spendable = false
95
101
  }
102
+ for (const o of updateToSpendable) {
103
+ await s.updateOutput(o.outputId, { spendable: true })
104
+ o.spendable = true
105
+ }
96
106
  }
97
- return filteredOutputs
107
+ return updateToSpent.concat(updateToSpendable)
98
108
  }
99
109
  },
100
110
  [sdk.specOpSetWalletChangeParams]: {
@@ -236,7 +246,7 @@ export async function listOutputs(
236
246
  ]
237
247
 
238
248
  const noTags = tagIds.length === 0
239
- const includeSpent = false
249
+ const includeSpent = specOp && specOp.includeSpent ? specOp.includeSpent : false
240
250
 
241
251
  const txStatusOk = `(select status as tstatus from transactions where transactions.transactionId = outputs.transactionId) in ('completed', 'unproven', 'nosend')`
242
252
  const txStatusOkCteq = `(select status as tstatus from transactions where transactions.transactionId = o.transactionId) in ('completed', 'unproven', 'nosend')`
@@ -51,9 +51,9 @@ describe('operations1 tests', () => {
51
51
  let r = await storage.listOutputs(auth, vargs)
52
52
  if (r.totalOutputs > 0) {
53
53
  const total: number = r.outputs.reduce((s, o) => (s += o.satoshis), 0)
54
- log += `userId ${userId}: ${r.totalOutputs} unspendable utxos, total ${total}, ${user.identityKey}\n`
54
+ log += `userId ${userId}: ${r.totalOutputs} utxos updated, total ${total}, ${user.identityKey}\n`
55
55
  for (const o of r.outputs) {
56
- log += ` ${o.outpoint} ${o.satoshis}\n`
56
+ log += ` ${o.outpoint} ${o.satoshis} now ${o.spendable ? 'spendable' : 'spent'}\n`
57
57
  }
58
58
  withInvalid[userId] = { user, outputs: r.outputs, total }
59
59
  }