@bsv/wallet-toolbox 1.1.46 → 1.1.47

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.
Files changed (80) hide show
  1. package/docs/client.md +112 -72
  2. package/docs/services.md +4 -3
  3. package/docs/storage.md +11 -24
  4. package/docs/wallet.md +112 -72
  5. package/out/src/Wallet.d.ts +3 -7
  6. package/out/src/Wallet.d.ts.map +1 -1
  7. package/out/src/Wallet.js +6 -5
  8. package/out/src/Wallet.js.map +1 -1
  9. package/out/src/monitor/tasks/TaskCheckForProofs.d.ts.map +1 -1
  10. package/out/src/monitor/tasks/TaskCheckForProofs.js +4 -2
  11. package/out/src/monitor/tasks/TaskCheckForProofs.js.map +1 -1
  12. package/out/src/sdk/WalletServices.interfaces.d.ts +3 -0
  13. package/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
  14. package/out/src/sdk/types.d.ts +12 -0
  15. package/out/src/sdk/types.d.ts.map +1 -1
  16. package/out/src/services/Services.d.ts.map +1 -1
  17. package/out/src/services/Services.js +7 -2
  18. package/out/src/services/Services.js.map +1 -1
  19. package/out/src/services/providers/ARC.d.ts.map +1 -1
  20. package/out/src/services/providers/ARC.js +83 -14
  21. package/out/src/services/providers/ARC.js.map +1 -1
  22. package/out/src/services/providers/WhatsOnChain.d.ts +1 -1
  23. package/out/src/services/providers/WhatsOnChain.d.ts.map +1 -1
  24. package/out/src/services/providers/WhatsOnChain.js +119 -32
  25. package/out/src/services/providers/WhatsOnChain.js.map +1 -1
  26. package/out/src/services/providers/__tests/WhatsOnChain.test.js +4 -4
  27. package/out/src/services/providers/__tests/WhatsOnChain.test.js.map +1 -1
  28. package/out/src/signer/methods/buildSignableTransaction.d.ts +10 -0
  29. package/out/src/signer/methods/buildSignableTransaction.d.ts.map +1 -0
  30. package/out/src/signer/methods/buildSignableTransaction.js +125 -0
  31. package/out/src/signer/methods/buildSignableTransaction.js.map +1 -0
  32. package/out/src/signer/methods/createAction.d.ts +5 -1
  33. package/out/src/signer/methods/createAction.d.ts.map +1 -1
  34. package/out/src/signer/methods/createAction.js +8 -121
  35. package/out/src/signer/methods/createAction.js.map +1 -1
  36. package/out/src/storage/WalletStorageManager.d.ts +1 -0
  37. package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
  38. package/out/src/storage/WalletStorageManager.js +3 -0
  39. package/out/src/storage/WalletStorageManager.js.map +1 -1
  40. package/out/src/storage/methods/attemptToPostReqsToNetwork.d.ts.map +1 -1
  41. package/out/src/storage/methods/attemptToPostReqsToNetwork.js +18 -1
  42. package/out/src/storage/methods/attemptToPostReqsToNetwork.js.map +1 -1
  43. package/out/src/storage/schema/entities/ProvenTx.d.ts.map +1 -1
  44. package/out/src/storage/schema/entities/ProvenTx.js +9 -20
  45. package/out/src/storage/schema/entities/ProvenTx.js.map +1 -1
  46. package/out/src/storage/schema/entities/ProvenTxReq.d.ts +4 -9
  47. package/out/src/storage/schema/entities/ProvenTxReq.d.ts.map +1 -1
  48. package/out/src/storage/schema/entities/ProvenTxReq.js +8 -5
  49. package/out/src/storage/schema/entities/ProvenTxReq.js.map +1 -1
  50. package/out/test/Wallet/local/localWallet.man.test.d.ts +2 -0
  51. package/out/test/Wallet/local/localWallet.man.test.d.ts.map +1 -0
  52. package/out/test/Wallet/local/localWallet.man.test.js +171 -0
  53. package/out/test/Wallet/local/localWallet.man.test.js.map +1 -0
  54. package/out/test/utils/TestUtilsWalletStorage.d.ts +5 -0
  55. package/out/test/utils/TestUtilsWalletStorage.d.ts.map +1 -1
  56. package/out/test/utils/TestUtilsWalletStorage.js +29 -18
  57. package/out/test/utils/TestUtilsWalletStorage.js.map +1 -1
  58. package/out/tsconfig.all.tsbuildinfo +1 -1
  59. package/package.json +1 -1
  60. package/src/Wallet.ts +7 -9
  61. package/src/monitor/tasks/TaskCheckForProofs.ts +4 -2
  62. package/src/sdk/WalletServices.interfaces.ts +6 -0
  63. package/src/sdk/types.ts +11 -0
  64. package/src/services/Services.ts +5 -2
  65. package/src/services/providers/ARC.ts +84 -15
  66. package/src/services/providers/WhatsOnChain.ts +129 -41
  67. package/src/services/providers/__tests/WhatsOnChain.test.ts +4 -5
  68. package/src/signer/methods/buildSignableTransaction.ts +160 -0
  69. package/src/signer/methods/createAction.ts +8 -161
  70. package/src/storage/WalletStorageManager.ts +4 -0
  71. package/src/storage/methods/attemptToPostReqsToNetwork.ts +17 -1
  72. package/src/storage/schema/entities/ProvenTx.ts +10 -30
  73. package/src/storage/schema/entities/ProvenTxReq.ts +15 -17
  74. package/test/Wallet/local/localWallet.man.test.ts +208 -0
  75. package/test/utils/TestUtilsWalletStorage.ts +43 -19
  76. package/out/test/Wallet/local/localWallet.test.d.ts +0 -2
  77. package/out/test/Wallet/local/localWallet.test.d.ts.map +0 -1
  78. package/out/test/Wallet/local/localWallet.test.js +0 -14
  79. package/out/test/Wallet/local/localWallet.test.js.map +0 -1
  80. package/test/Wallet/local/localWallet.test.ts +0 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/wallet-toolbox",
3
- "version": "1.1.46",
3
+ "version": "1.1.47",
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",
package/src/Wallet.ts CHANGED
@@ -888,15 +888,13 @@ export class Wallet implements WalletInterface, ProtoWallet {
888
888
  /**
889
889
  * Uses `listOutputs` to iterate through all spendable outputs in the 'default' (change) basket.
890
890
  *
891
+ * Outputs in the 'default' basket are managed by the wallet and MUST NOT USED AS UNMANAGED INPUTS.
892
+ *
891
893
  * @param {string} basket - Optional. Defaults to 'default', the wallet change basket.
892
894
  * @returns { total: number, utxos: { satoshis: number, outpoint: string }[] }
893
895
  */
894
- async balance(basket: string = 'default'): Promise<{
895
- total: number
896
- utxos: { satoshis: number; outpoint: string }[]
897
- }> {
898
- let total = 0
899
- const utxos: { satoshis: number; outpoint: string }[] = []
896
+ async balance(basket: string = 'default'): Promise<sdk.WalletBalance> {
897
+ const r: sdk.WalletBalance = { total: 0, utxos: [] }
900
898
  let offset = 0
901
899
  for (;;) {
902
900
  const change = await this.listOutputs({
@@ -906,12 +904,12 @@ export class Wallet implements WalletInterface, ProtoWallet {
906
904
  })
907
905
  if (change.totalOutputs === 0) break
908
906
  for (const o of change.outputs) {
909
- total += o.satoshis
910
- utxos.push({ satoshis: o.satoshis, outpoint: o.outpoint })
907
+ r.total += o.satoshis
908
+ r.utxos.push({ satoshis: o.satoshis, outpoint: o.outpoint })
911
909
  }
912
910
  offset += change.outputs.length
913
911
  }
914
- return { total, utxos }
912
+ return r
915
913
  }
916
914
  }
917
915
 
@@ -197,6 +197,8 @@ export class TaskCheckForProofs extends WalletMonitorTask {
197
197
 
198
198
  if (ptx) {
199
199
  // We have a merklePath proof for the request (and a block header)
200
+ await req.updateStorageDynamicProperties(this.storage)
201
+ await req.refreshFromStorage(this.storage)
200
202
  const { provenTxReqId, status, txid, attempts, history } = req.toApi()
201
203
  const { index, height, blockHash, merklePath, merkleRoot } = ptx.toApi()
202
204
  const r = await this.storage.runAsStorageProvider(async sp => {
@@ -221,9 +223,9 @@ export class TaskCheckForProofs extends WalletMonitorTask {
221
223
  if (countsAsAttempt && req.status !== 'nosend') {
222
224
  req.attempts++
223
225
  }
224
- await req.updateStorageDynamicProperties(this.storage)
225
- await req.refreshFromStorage(this.storage)
226
226
  }
227
+ await req.updateStorageDynamicProperties(this.storage)
228
+ await req.refreshFromStorage(this.storage)
227
229
 
228
230
  log += req.historyPretty(since, indent + 2) + '\n'
229
231
 
@@ -207,6 +207,8 @@ export interface GetMerklePathResult {
207
207
  * The first exception error that occurred during processing, if any.
208
208
  */
209
209
  error?: sdk.WalletError
210
+
211
+ notes?: sdk.ReqHistoryNote[]
210
212
  }
211
213
 
212
214
  export interface PostTxResultForTxid {
@@ -237,6 +239,8 @@ export interface PostTxResultForTxid {
237
239
  competingTxs?: string[]
238
240
 
239
241
  data?: object | string | PostTxResultForTxidError
242
+
243
+ notes?: sdk.ReqHistoryNote[]
240
244
  }
241
245
 
242
246
  export interface PostTxResultForTxidError {
@@ -269,6 +273,8 @@ export interface PostTxsResult {
269
273
  * Service response object. Use service name and status to infer type of object.
270
274
  */
271
275
  data?: object
276
+
277
+ notes?: sdk.ReqHistoryNote[]
272
278
  }
273
279
 
274
280
  export interface GetUtxoStatusDetails {
package/src/sdk/types.ts CHANGED
@@ -120,3 +120,14 @@ export interface ScriptTemplateUnlock {
120
120
  sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
121
121
  estimateLength: (tx: Transaction, inputIndex: number) => Promise<number>
122
122
  }
123
+
124
+ export interface WalletBalance {
125
+ total: number
126
+ utxos: { satoshis: number; outpoint: string }[]
127
+ }
128
+
129
+ export type ReqHistoryNote = {
130
+ when?: string
131
+ what: string
132
+ [key: string]: string | number | undefined
133
+ }
@@ -272,11 +272,13 @@ export class Services implements sdk.WalletServices {
272
272
  ): Promise<sdk.GetMerklePathResult> {
273
273
  if (useNext) this.getMerklePathServices.next()
274
274
 
275
- const r0: sdk.GetMerklePathResult = {}
275
+ const r0: sdk.GetMerklePathResult = { notes: [] }
276
276
 
277
277
  for (let tries = 0; tries < this.getMerklePathServices.count; tries++) {
278
278
  const service = this.getMerklePathServices.service
279
279
  const r = await service(txid, this)
280
+ if (r.notes) r0.notes!.push(...r.notes)
281
+ if (!r0.name) r0.name = r.name
280
282
  if (r.merklePath) {
281
283
  // If we have a proof, call it done.
282
284
  r0.merklePath = r.merklePath
@@ -284,9 +286,10 @@ export class Services implements sdk.WalletServices {
284
286
  r0.name = r.name
285
287
  r0.error = undefined
286
288
  break
287
- } else if (r.error && !r0.error)
289
+ } else if (r.error && !r0.error) {
288
290
  // If we have an error and didn't before...
289
291
  r0.error = r.error
292
+ }
290
293
 
291
294
  this.getMerklePathServices.next()
292
295
  }
@@ -10,6 +10,7 @@ import {
10
10
  Utils
11
11
  } from '@bsv/sdk'
12
12
  import { doubleSha256BE, sdk } from '../../index.client'
13
+ import { ReqHistoryNote } from '../../sdk'
13
14
 
14
15
  /** Configuration options for the ARC broadcaster. */
15
16
  export interface ArcConfig {
@@ -135,6 +136,8 @@ export default class ARC {
135
136
  let txid = Utils.toHex(doubleSha256BE(Utils.toArray(rawTx, 'hex')))
136
137
  if (txids) {
137
138
  txid = txids.slice(-1)[0]
139
+ } else {
140
+ txids = [txid]
138
141
  }
139
142
 
140
143
  const requestOptions: HttpClientRequestOptions = {
@@ -145,17 +148,29 @@ export default class ARC {
145
148
 
146
149
  const r: sdk.PostTxResultForTxid = {
147
150
  txid,
148
- status: 'success'
151
+ status: 'success',
152
+ notes: []
149
153
  }
150
154
 
155
+ const url = `${this.URL}/v1/tx`
156
+ const nn = () => ({ name: 'ARCv1tx', when: new Date().toISOString() })
157
+ const nne = () => ({ ...nn(), rawTx, txids: txids.join(','), url })
158
+
151
159
  try {
152
160
  const response = await this.httpClient.request<ArcResponse>(
153
- `${this.URL}/v1/tx`,
161
+ url,
154
162
  requestOptions
155
163
  )
156
164
 
165
+ const { txid, extraInfo, txStatus, competingTxs } = response.data
166
+ const nnr = () => ({
167
+ txid,
168
+ extraInfo,
169
+ txStatus,
170
+ competingTxs: competingTxs?.join(',')
171
+ })
172
+
157
173
  if (response.ok) {
158
- const { txid, extraInfo, txStatus, competingTxs } = response.data
159
174
  r.data = `${txStatus} ${extraInfo}`
160
175
  if (r.txid !== txid) r.data += ` txid altered from ${r.txid} to ${txid}`
161
176
  r.txid = txid
@@ -163,35 +178,59 @@ export default class ARC {
163
178
  r.status = 'error'
164
179
  r.doubleSpend = true
165
180
  r.competingTxs = competingTxs
181
+ r.notes!.push({ ...nne(), ...nnr(), what: 'postRawTxDoubleSpend' })
182
+ } else {
183
+ r.notes!.push({ ...nn(), ...nnr(), what: 'postRawTxSuccess' })
166
184
  }
185
+ } else if (typeof response === 'string') {
186
+ r.notes!.push({ ...nn(), what: 'postRawTxString', response })
167
187
  } else {
168
188
  r.status = 'error'
189
+ const n: ReqHistoryNote = {
190
+ ...nn(),
191
+ ...nne(),
192
+ ...nnr(),
193
+ what: 'postRawTxError'
194
+ }
169
195
  const ed: sdk.PostTxResultForTxidError = {}
170
196
  r.data = ed
171
197
  const st = typeof response.status
172
- ed.status =
173
- st === 'number' || st === 'string'
174
- ? response.status.toString()
175
- : 'ERR_UNKNOWN'
198
+ if (st === 'number' || st === 'string') {
199
+ n.status = response.status
200
+ ed.status = response.status.toString()
201
+ } else {
202
+ n.status = st
203
+ ed.status = 'ERR_UNKNOWN'
204
+ }
176
205
 
177
206
  let d = response.data
178
207
  if (d && typeof d === 'string') {
208
+ n.data = response.data.slice(0, 128)
179
209
  try {
180
210
  d = JSON.parse(d)
181
211
  } catch {
182
212
  // Intentionally left empty
183
213
  }
184
- }
185
- if (d && typeof d === 'object') {
214
+ } else if (d && typeof d === 'object') {
186
215
  ed.more = d
187
216
  ed.detail = d['detail']
188
217
  if (typeof ed.detail !== 'string') ed.detail = undefined
218
+ if (ed.detail) {
219
+ n.detail = ed.detail
220
+ }
189
221
  }
222
+ r.notes!.push(n)
190
223
  }
191
224
  } catch (eu: unknown) {
192
225
  const e = sdk.WalletError.fromUnknown(eu)
193
226
  r.status = 'error'
194
227
  r.data = `${e.code} ${e.message}`
228
+ r.notes!.push({
229
+ ...nne(),
230
+ what: 'postRawTxCatch',
231
+ code: e.code,
232
+ description: e.description
233
+ })
195
234
  }
196
235
 
197
236
  return r
@@ -208,38 +247,68 @@ export default class ARC {
208
247
  * @returns
209
248
  */
210
249
  async postBeef(beef: Beef, txids: string[]): Promise<sdk.PostBeefResult> {
250
+ const r: sdk.PostBeefResult = {
251
+ name: 'ARC',
252
+ status: 'success',
253
+ txidResults: [],
254
+ notes: []
255
+ }
256
+
257
+ const nn = () => ({ name: 'ARCpostBeef', when: new Date().toISOString() })
258
+
211
259
  if (beef.version === BEEF_V2 && beef.txs.every(btx => !btx.isTxidOnly)) {
212
260
  beef.version = BEEF_V1
261
+ r.notes!.push({ ...nn(), what: 'postBeefV2ToV1' })
213
262
  }
214
263
 
215
264
  const beefHex = beef.toHex()
216
265
 
217
266
  const prtr = await this.postRawTx(beefHex, txids)
218
267
 
219
- const r: sdk.PostBeefResult = {
220
- name: 'ARC',
221
- status: prtr.status,
222
- txidResults: [prtr]
223
- }
268
+ r.status = prtr.status
269
+ r.txidResults = [prtr]
224
270
 
271
+ // Since postRawTx only returns results for a single txid,
272
+ // replicate the basic results any additional txids.
273
+ // TODO: Temporary hack...
225
274
  for (const txid of txids) {
226
275
  if (prtr.txid === txid) continue
227
276
  const tr: sdk.PostTxResultForTxid = {
228
277
  txid,
229
- status: 'success'
278
+ status: 'success',
279
+ notes: []
230
280
  }
281
+ // For the extra txids, go back to the service for confirmation...
231
282
  const dr = await this.getTxData(txid)
232
283
  if (dr.txid !== txid) {
233
284
  tr.status = 'error'
234
285
  tr.data = 'internal error'
286
+ tr.notes!.push({
287
+ ...nn(),
288
+ what: 'postBeefGetTxDataInternal',
289
+ txid,
290
+ returnedTxid: dr.txid
291
+ })
235
292
  } else if (
236
293
  dr.txStatus === 'SEEN_ON_NETWORK' ||
237
294
  dr.txStatus === 'STORED'
238
295
  ) {
239
296
  tr.data = dr.txStatus
297
+ tr.notes!.push({
298
+ ...nn(),
299
+ what: 'postBeefGetTxDataSuccess',
300
+ txid,
301
+ txStatus: dr.txStatus
302
+ })
240
303
  } else {
241
304
  tr.status = 'error'
242
305
  tr.data = dr
306
+ tr.notes!.push({
307
+ ...nn(),
308
+ what: 'postBeefGetTxDataError',
309
+ txid,
310
+ txStatus: dr.txStatus
311
+ })
243
312
  }
244
313
  r.txidResults.push(tr)
245
314
  if (r.status === 'success' && tr.status === 'error') r.status = 'error'
@@ -2,12 +2,14 @@ import { Beef, HexString, Utils, WhatsOnChainConfig } from '@bsv/sdk'
2
2
  import {
3
3
  asArray,
4
4
  asString,
5
+ doubleSha256BE,
5
6
  sdk,
6
7
  validateScriptHash,
7
8
  wait
8
9
  } from '../../index.client'
9
10
  import { convertProofToMerklePath } from '../../utility/tscProofToMerklePath'
10
11
  import SdkWhatsOnChain from './SdkWhatsOnChain'
12
+ import { ReqHistoryNote } from '../../sdk'
11
13
 
12
14
  /**
13
15
  *
@@ -116,17 +118,16 @@ export class WhatsOnChain extends SdkWhatsOnChain {
116
118
  const r: sdk.PostBeefResult = {
117
119
  name: 'WoC',
118
120
  status: 'success',
119
- txidResults: []
121
+ txidResults: [],
122
+ notes: []
120
123
  }
121
124
 
122
125
  let delay = false
123
126
 
124
- for (const txid of txids) {
125
- const tr: sdk.PostTxResultForTxid = {
126
- txid,
127
- status: 'success'
128
- }
127
+ const nn = () => ({ name: 'WoCpostBeef', when: new Date().toISOString() })
128
+ const nne = () => ({ ...nn(), beef: beef.toHex(), txids: txids.join(',') })
129
129
 
130
+ for (const txid of txids) {
130
131
  const rawTx = Utils.toHex(beef.findTxid(txid)!.rawTx!)
131
132
 
132
133
  if (delay) {
@@ -135,19 +136,21 @@ export class WhatsOnChain extends SdkWhatsOnChain {
135
136
  }
136
137
  delay = true
137
138
 
138
- try {
139
- const wocTxid = await this.postRawTx(rawTx)
140
- if (txid !== wocTxid) {
141
- tr.status = 'error'
142
- tr.data = `woc returned txid ${wocTxid}, expected ${txid}`
143
- }
144
- } catch (eu: unknown) {
145
- const e = sdk.WalletError.fromUnknown(eu)
146
- tr.status = 'error'
147
- tr.data = `exception ${e.code} ${e.message}`
139
+ const tr = await this.postRawTx(rawTx)
140
+ if (txid !== tr.txid) {
141
+ throw new sdk.WERR_INTERNAL(
142
+ `woc returned txid ${tr.txid}, expected ${txid}`
143
+ )
148
144
  }
149
145
 
150
146
  r.txidResults.push(tr)
147
+ if (r.status === 'success' && tr.status !== 'success') r.status = 'error'
148
+ }
149
+
150
+ if (r.status === 'success') {
151
+ r.notes!.push({ ...nn(), what: 'postBeefSuccess' })
152
+ } else {
153
+ r.notes!.push({ ...nne(), what: 'postBeefError' })
151
154
  }
152
155
 
153
156
  return r
@@ -157,7 +160,15 @@ export class WhatsOnChain extends SdkWhatsOnChain {
157
160
  * @param rawTx raw transaction to broadcast as hex string
158
161
  * @returns txid returned by transaction processor of transaction broadcast
159
162
  */
160
- async postRawTx(rawTx: HexString): Promise<string> {
163
+ async postRawTx(rawTx: HexString): Promise<sdk.PostTxResultForTxid> {
164
+ let txid = Utils.toHex(doubleSha256BE(Utils.toArray(rawTx, 'hex')))
165
+
166
+ const r: sdk.PostTxResultForTxid = {
167
+ txid,
168
+ status: 'success',
169
+ notes: []
170
+ }
171
+
161
172
  const headers = this.getHttpHeaders()
162
173
  headers['Content-Type'] = 'application/json'
163
174
  headers['Accept'] = 'text/plain'
@@ -168,41 +179,71 @@ export class WhatsOnChain extends SdkWhatsOnChain {
168
179
  data: { txhex: rawTx }
169
180
  }
170
181
 
171
- for (let retry = 0; retry < 2; retry++) {
182
+ const url = `${this.URL}/tx/raw`
183
+ const nn = () => ({ name: 'WoCpostRawTx', when: new Date().toISOString() })
184
+ const nne = () => ({ ...nn(), rawTx, txid, url })
185
+
186
+ const retryLimit = 5
187
+ for (let retry = 0; retry < retryLimit; retry++) {
172
188
  try {
173
189
  const response = await this.httpClient.request<string>(
174
- `${this.URL}/tx/raw`,
190
+ url,
175
191
  requestOptions
176
192
  )
177
193
  if (response.statusText === 'Too Many Requests' && retry < 2) {
194
+ r.notes!.push({ ...nn(), what: 'postRawTxRateLimit' })
178
195
  await wait(2000)
179
196
  continue
180
197
  }
181
198
  if (response.ok) {
182
199
  const txid = response.data
183
- return txid
200
+ r.notes!.push({ ...nn(), what: 'postRawTxRateSuccess' })
201
+ return r
184
202
  } else {
185
- if (typeof response.data === 'string')
186
- throw new sdk.WERR_INVALID_PARAMETER(
187
- 'rawTx',
188
- `valid. ${response.data}`
189
- )
190
- else
191
- throw new sdk.WERR_INVALID_PARAMETER(
192
- 'rawTx',
193
- `valid. ${response.status} ${response.statusText}`
194
- )
203
+ r.status = 'error'
204
+ const n: ReqHistoryNote = {
205
+ ...nn(),
206
+ ...nne(),
207
+ what: 'postRawTxError'
208
+ }
209
+ if (typeof response.data === 'string') {
210
+ n.data = response.data.slice(0, 128)
211
+ r.data = response.data
212
+ }
213
+ if (typeof response.statusText === 'string') {
214
+ n.statusText = response.data.slice(0, 128)
215
+ r.data = `${r.data || ''} ${response.statusText}`
216
+ }
217
+ if (
218
+ typeof response.status === 'string' ||
219
+ typeof response.status === 'number'
220
+ ) {
221
+ n.status = response.data.slice(0, 128)
222
+ r.data = `${r.data || ''} ${response.status}`
223
+ }
224
+ r.notes!.push(n)
195
225
  }
196
226
  } catch (eu: unknown) {
197
- if (eu instanceof sdk.WERR_INVALID_PARAMETER) throw eu
227
+ r.status = 'error'
198
228
  const e = sdk.WalletError.fromUnknown(eu)
199
- throw new sdk.WERR_INVALID_PARAMETER(
200
- 'rawTx',
201
- `valid rawTx. error ${e.code} ${e.message} ${rawTx}`
202
- )
229
+ r.notes!.push({
230
+ ...nn(),
231
+ ...nne(),
232
+ what: 'postRawTxCatch',
233
+ code: e.code,
234
+ description: e.description
235
+ })
236
+ r.data = `${e.code} ${e.description}`
203
237
  }
204
238
  }
205
- throw new sdk.WERR_INTERNAL()
239
+ r.status = 'error'
240
+ r.notes!.push({
241
+ ...nn(),
242
+ ...nne(),
243
+ what: 'postRawTxRetryLimit',
244
+ retryLimit
245
+ })
246
+ return r
206
247
  }
207
248
 
208
249
  /**
@@ -213,7 +254,7 @@ export class WhatsOnChain extends SdkWhatsOnChain {
213
254
  txid: string,
214
255
  services: sdk.WalletServices
215
256
  ): Promise<sdk.GetMerklePathResult> {
216
- const r: sdk.GetMerklePathResult = { name: 'WoCTsc' }
257
+ const r: sdk.GetMerklePathResult = { name: 'WoCTsc', notes: [] }
217
258
 
218
259
  const headers = this.getHttpHeaders()
219
260
  const requestOptions = {
@@ -227,25 +268,51 @@ export class WhatsOnChain extends SdkWhatsOnChain {
227
268
  WhatsOnChainTscProof | WhatsOnChainTscProof[]
228
269
  >(`${this.URL}/tx/${txid}/proof/tsc`, requestOptions)
229
270
  if (response.statusText === 'Too Many Requests' && retry < 2) {
271
+ r.notes!.push({
272
+ what: 'getMerklePathRetry',
273
+ name: r.name,
274
+ status: response.status,
275
+ statusText: response.statusText
276
+ })
230
277
  await wait(2000)
231
278
  continue
232
279
  }
233
280
 
234
- if (response.status === 404 && response.statusText === 'Not Found')
281
+ if (response.status === 404 && response.statusText === 'Not Found') {
282
+ r.notes!.push({
283
+ what: 'getMerklePathNotFound',
284
+ name: r.name,
285
+ status: response.status,
286
+ statusText: response.statusText
287
+ })
235
288
  return r
289
+ }
236
290
 
237
291
  if (
238
292
  !response.ok ||
239
293
  response.status !== 200 ||
240
294
  response.statusText !== 'OK'
241
- )
295
+ ) {
296
+ r.notes!.push({
297
+ what: 'getMerklePathBadStatus',
298
+ name: r.name,
299
+ status: response.status,
300
+ statusText: response.statusText
301
+ })
242
302
  throw new sdk.WERR_INVALID_PARAMETER(
243
303
  'txid',
244
304
  `valid transaction. '${txid}' response ${response.statusText}`
245
305
  )
306
+ }
246
307
 
247
308
  if (!response.data) {
248
309
  // Unmined, proof not yet available.
310
+ r.notes!.push({
311
+ what: 'getMerklePathNoData',
312
+ name: r.name,
313
+ status: response.status,
314
+ statusText: response.statusText
315
+ })
249
316
  return r
250
317
  }
251
318
 
@@ -263,17 +330,38 @@ export class WhatsOnChain extends SdkWhatsOnChain {
263
330
  }
264
331
  r.merklePath = convertProofToMerklePath(txid, proof)
265
332
  r.header = header
333
+ r.notes!.push({
334
+ what: 'getMerklePathSuccess',
335
+ name: r.name,
336
+ status: response.status,
337
+ statusText: response.statusText
338
+ })
266
339
  } else {
340
+ r.notes!.push({
341
+ what: 'getMerklePathNoHeader',
342
+ target: p.target,
343
+ name: r.name,
344
+ status: response.status,
345
+ statusText: response.statusText
346
+ })
267
347
  throw new sdk.WERR_INVALID_PARAMETER(
268
348
  'blockhash',
269
349
  'a valid on-chain block hash'
270
350
  )
271
351
  }
272
- } catch (err: unknown) {
273
- r.error = sdk.WalletError.fromUnknown(err)
352
+ } catch (eu: unknown) {
353
+ const e = sdk.WalletError.fromUnknown(eu)
354
+ r.notes!.push({
355
+ what: 'getMerklePathError',
356
+ name: r.name,
357
+ code: e.code,
358
+ description: e.description
359
+ })
360
+ r.error = e
274
361
  }
275
362
  return r
276
363
  }
364
+ r.notes!.push({ what: 'getMerklePathInternal', name: r.name })
277
365
  throw new sdk.WERR_INTERNAL()
278
366
  }
279
367
 
@@ -49,14 +49,14 @@ describe('whatsonchain tests', () => {
49
49
  )
50
50
  const s = JSON.stringify(r)
51
51
  expect(s).toBe(
52
- '{"name":"WoCTsc","merklePath":{"blockHeight":1661398,"path":[[{"offset":6,"hash":"7e5b797b86abd31a654bf296900d6cb14d04ef0811568ff4675494af2d92166b","txid":true},{"offset":7,"hash":"97dd9d9080394d52338588732d9f84e1debca93f171f674ac3beac1e75495568"}],[{"offset":2,"hash":"81beedcd219d9e03255bde2ee479db34b9fed04d30373ba8bc264a64af2515b9"}],[{"offset":0,"hash":"9965f9aaeea33f6878335e6f7e6bdb544c3a8550c84e2f0daca54e9cd912111c"}]]},"header":{"version":536870912,"previousHash":"000000000688340a14b77e49bb0fca5ac7b624f7f79a5517583d1aae61c4e658","merkleRoot":"edbc07082ca0a31d5ec89d1f503a9cd41112c0d8f3221a96acfb8a9d16f8e82b","time":1739624725,"bits":486604799,"nonce":1437884974,"height":1661398,"hash":"00000000d8a73bf9a37272a71886ea92a25376bed1c1916f2b5cfbec4d6f6a25"}}'
52
+ "{\"name\":\"WoCTsc\",\"notes\":[{\"what\":\"getMerklePathSuccess\",\"name\":\"WoCTsc\",\"status\":200,\"statusText\":\"OK\"}],\"merklePath\":{\"blockHeight\":1661398,\"path\":[[{\"offset\":6,\"hash\":\"7e5b797b86abd31a654bf296900d6cb14d04ef0811568ff4675494af2d92166b\",\"txid\":true},{\"offset\":7,\"hash\":\"97dd9d9080394d52338588732d9f84e1debca93f171f674ac3beac1e75495568\"}],[{\"offset\":2,\"hash\":\"81beedcd219d9e03255bde2ee479db34b9fed04d30373ba8bc264a64af2515b9\"}],[{\"offset\":0,\"hash\":\"9965f9aaeea33f6878335e6f7e6bdb544c3a8550c84e2f0daca54e9cd912111c\"}]]},\"header\":{\"version\":536870912,\"previousHash\":\"000000000688340a14b77e49bb0fca5ac7b624f7f79a5517583d1aae61c4e658\",\"merkleRoot\":\"edbc07082ca0a31d5ec89d1f503a9cd41112c0d8f3221a96acfb8a9d16f8e82b\",\"time\":1739624725,\"bits\":486604799,\"nonce\":1437884974,\"height\":1661398,\"hash\":\"00000000d8a73bf9a37272a71886ea92a25376bed1c1916f2b5cfbec4d6f6a25\"}}"
53
53
  )
54
54
  }
55
55
 
56
56
  {
57
57
  const r = await wocTest.getMerklePath('1'.repeat(64), services)
58
58
  const s = JSON.stringify(r)
59
- expect(s).toBe('{"name":"WoCTsc"}')
59
+ expect(s).toBe("{\"name\":\"WoCTsc\",\"notes\":[{\"what\":\"getMerklePathNoData\",\"name\":\"WoCTsc\",\"status\":200,\"statusText\":\"OK\"}]}")
60
60
  }
61
61
  })
62
62
 
@@ -69,14 +69,13 @@ describe('whatsonchain tests', () => {
69
69
  )
70
70
  const s = JSON.stringify(r)
71
71
  expect(s).toBe(
72
- '{"name":"WoCTsc","merklePath":{"blockHeight":883637,"path":[[{"offset":46,"hash":"d9978ffc6676523208f7b33bebf1b176388bbeace2c7ef67ce35c2eababa1805","txid":true},{"offset":47,"hash":"066f6fa6fa988f2e3a9d6fe35fa0d3666c652dac35cabaeebff3738a4e67f68f"}],[{"offset":22,"hash":"232089a6f77c566151bc4701fda394b5cc5bf17073140d46a73c4c3ed0a7b911"}],[{"offset":10,"hash":"c639b3a6ce127f67dbd01c7331a6fca62a4b429830387bd68ac6ac05e162116d"}],[{"offset":4,"hash":"730cec44be97881530947d782bb328d25f1122fdae206296937fffb03e936d48"}],[{"offset":3,"hash":"28b681f8ab8db0fa4d5d20cb1532b95184a155346b0b8447bde580b2406d51e6"}],[{"offset":0,"hash":"c49a18028e230dd1439b26794c08c339506f24a450f067c4facd4e0d5a346490"}],[{"offset":1,"hash":"0ba57d1b1fad6874de3640c01088e3dedad3507e5b3a3102b9a8a8055f3df88b"}],[{"offset":1,"hash":"c830edebe5565c19ba584ec73d49129344d17539f322509b7c314ae641c2fcdb"}],[{"offset":1,"hash":"ff62d5ed2a94eb93a2b7d084b8f15b12083573896b6a58cf871507e3352c75f5"}]]},"header":{"version":1040187392,"previousHash":"00000000000000000d9f6889dd6743500adee204ea25d8a57225ecd48b111769","merkleRoot":"59c1efd79fae0d9c29dd8da63f8eeec0aadde048f4491c6bfa324fcfd537156d","time":1739329877,"bits":403818359,"nonce":596827153,"height":883637,"hash":"0000000000000000060ac8d63b78d41f58c9aba0b09f81db7d51fa4905a47263"}}'
73
- )
72
+ "{\"name\":\"WoCTsc\",\"notes\":[{\"what\":\"getMerklePathSuccess\",\"name\":\"WoCTsc\",\"status\":200,\"statusText\":\"OK\"}],\"merklePath\":{\"blockHeight\":883637,\"path\":[[{\"offset\":46,\"hash\":\"d9978ffc6676523208f7b33bebf1b176388bbeace2c7ef67ce35c2eababa1805\",\"txid\":true},{\"offset\":47,\"hash\":\"066f6fa6fa988f2e3a9d6fe35fa0d3666c652dac35cabaeebff3738a4e67f68f\"}],[{\"offset\":22,\"hash\":\"232089a6f77c566151bc4701fda394b5cc5bf17073140d46a73c4c3ed0a7b911\"}],[{\"offset\":10,\"hash\":\"c639b3a6ce127f67dbd01c7331a6fca62a4b429830387bd68ac6ac05e162116d\"}],[{\"offset\":4,\"hash\":\"730cec44be97881530947d782bb328d25f1122fdae206296937fffb03e936d48\"}],[{\"offset\":3,\"hash\":\"28b681f8ab8db0fa4d5d20cb1532b95184a155346b0b8447bde580b2406d51e6\"}],[{\"offset\":0,\"hash\":\"c49a18028e230dd1439b26794c08c339506f24a450f067c4facd4e0d5a346490\"}],[{\"offset\":1,\"hash\":\"0ba57d1b1fad6874de3640c01088e3dedad3507e5b3a3102b9a8a8055f3df88b\"}],[{\"offset\":1,\"hash\":\"c830edebe5565c19ba584ec73d49129344d17539f322509b7c314ae641c2fcdb\"}],[{\"offset\":1,\"hash\":\"ff62d5ed2a94eb93a2b7d084b8f15b12083573896b6a58cf871507e3352c75f5\"}]]},\"header\":{\"version\":1040187392,\"previousHash\":\"00000000000000000d9f6889dd6743500adee204ea25d8a57225ecd48b111769\",\"merkleRoot\":\"59c1efd79fae0d9c29dd8da63f8eeec0aadde048f4491c6bfa324fcfd537156d\",\"time\":1739329877,\"bits\":403818359,\"nonce\":596827153,\"height\":883637,\"hash\":\"0000000000000000060ac8d63b78d41f58c9aba0b09f81db7d51fa4905a47263\"}}" )
74
73
  }
75
74
 
76
75
  {
77
76
  const r = await wocMain.getMerklePath('1'.repeat(64), services)
78
77
  const s = JSON.stringify(r)
79
- expect(s).toBe('{"name":"WoCTsc"}')
78
+ expect(s).toBe("{\"name\":\"WoCTsc\",\"notes\":[{\"what\":\"getMerklePathNoData\",\"name\":\"WoCTsc\",\"status\":200,\"statusText\":\"OK\"}]}")
80
79
  }
81
80
  })
82
81