@bsv/sdk 1.8.8 → 1.8.10

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 (78) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js +7 -2
  3. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  4. package/dist/cjs/src/overlay-tools/LookupResolver.js +7 -2
  5. package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
  6. package/dist/cjs/src/transaction/Beef.js +1 -0
  7. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  8. package/dist/cjs/src/wallet/WERR_INSUFFICIENT_FUNDS.js +26 -0
  9. package/dist/cjs/src/wallet/WERR_INSUFFICIENT_FUNDS.js.map +1 -0
  10. package/dist/cjs/src/wallet/WERR_INVALID_PARAMETER.js +20 -0
  11. package/dist/cjs/src/wallet/WERR_INVALID_PARAMETER.js.map +1 -0
  12. package/dist/cjs/src/wallet/WalletClient.js +23 -0
  13. package/dist/cjs/src/wallet/WalletClient.js.map +1 -1
  14. package/dist/cjs/src/wallet/WalletError.js +55 -0
  15. package/dist/cjs/src/wallet/WalletError.js.map +1 -1
  16. package/dist/cjs/src/wallet/index.js +18 -1
  17. package/dist/cjs/src/wallet/index.js.map +1 -1
  18. package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js +28 -11
  19. package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
  20. package/dist/cjs/src/wallet/validationHelpers.js +920 -0
  21. package/dist/cjs/src/wallet/validationHelpers.js.map +1 -0
  22. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  23. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js +7 -2
  24. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  25. package/dist/esm/src/overlay-tools/LookupResolver.js +7 -2
  26. package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
  27. package/dist/esm/src/transaction/Beef.js +1 -0
  28. package/dist/esm/src/transaction/Beef.js.map +1 -1
  29. package/dist/esm/src/wallet/WERR_INSUFFICIENT_FUNDS.js +25 -0
  30. package/dist/esm/src/wallet/WERR_INSUFFICIENT_FUNDS.js.map +1 -0
  31. package/dist/esm/src/wallet/WERR_INVALID_PARAMETER.js +18 -0
  32. package/dist/esm/src/wallet/WERR_INVALID_PARAMETER.js.map +1 -0
  33. package/dist/esm/src/wallet/WalletClient.js +23 -0
  34. package/dist/esm/src/wallet/WalletClient.js.map +1 -1
  35. package/dist/esm/src/wallet/WalletError.js +55 -0
  36. package/dist/esm/src/wallet/WalletError.js.map +1 -1
  37. package/dist/esm/src/wallet/index.js +3 -0
  38. package/dist/esm/src/wallet/index.js.map +1 -1
  39. package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js +25 -11
  40. package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
  41. package/dist/esm/src/wallet/validationHelpers.js +859 -0
  42. package/dist/esm/src/wallet/validationHelpers.js.map +1 -0
  43. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  44. package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts +1 -1
  45. package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts.map +1 -1
  46. package/dist/types/src/overlay-tools/LookupResolver.d.ts +1 -1
  47. package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -1
  48. package/dist/types/src/transaction/Beef.d.ts +1 -0
  49. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  50. package/dist/types/src/wallet/WERR_INSUFFICIENT_FUNDS.d.ts +19 -0
  51. package/dist/types/src/wallet/WERR_INSUFFICIENT_FUNDS.d.ts.map +1 -0
  52. package/dist/types/src/wallet/WERR_INVALID_PARAMETER.d.ts +13 -0
  53. package/dist/types/src/wallet/WERR_INVALID_PARAMETER.d.ts.map +1 -0
  54. package/dist/types/src/wallet/WalletClient.d.ts.map +1 -1
  55. package/dist/types/src/wallet/WalletError.d.ts +14 -1
  56. package/dist/types/src/wallet/WalletError.d.ts.map +1 -1
  57. package/dist/types/src/wallet/index.d.ts +3 -0
  58. package/dist/types/src/wallet/index.d.ts.map +1 -1
  59. package/dist/types/src/wallet/substrates/HTTPWalletJSON.d.ts.map +1 -1
  60. package/dist/types/src/wallet/validationHelpers.d.ts +512 -0
  61. package/dist/types/src/wallet/validationHelpers.d.ts.map +1 -0
  62. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  63. package/dist/umd/bundle.js +4 -4
  64. package/dist/umd/bundle.js.map +1 -1
  65. package/docs/reference/kvstore.md +11 -0
  66. package/docs/reference/wallet.md +1431 -53
  67. package/package.json +1 -1
  68. package/src/auth/transports/SimplifiedFetchTransport.ts +10 -2
  69. package/src/overlay-tools/LookupResolver.ts +10 -2
  70. package/src/transaction/Beef.ts +2 -0
  71. package/src/wallet/WERR_INSUFFICIENT_FUNDS.ts +25 -0
  72. package/src/wallet/WERR_INVALID_PARAMETER.ts +20 -0
  73. package/src/wallet/WalletClient.ts +30 -0
  74. package/src/wallet/WalletError.ts +52 -0
  75. package/src/wallet/__tests/WalletClient.test.ts +31 -0
  76. package/src/wallet/index.ts +3 -0
  77. package/src/wallet/substrates/HTTPWalletJSON.ts +19 -9
  78. package/src/wallet/validationHelpers.ts +1211 -0
@@ -0,0 +1,1211 @@
1
+ /* eslint-disable @typescript-eslint/strict-boolean-expressions */
2
+ import {
3
+ AbortActionArgs,
4
+ AcquireCertificateArgs,
5
+ AcquisitionProtocol,
6
+ AtomicBEEF,
7
+ Base64String,
8
+ BasketInsertion,
9
+ BasketStringUnder300Bytes,
10
+ BEEF,
11
+ BooleanDefaultFalse,
12
+ BooleanDefaultTrue,
13
+ CertificateFieldNameUnder50Bytes,
14
+ CreateActionArgs,
15
+ CreateActionInput,
16
+ CreateActionOptions,
17
+ CreateActionOutput,
18
+ DescriptionString5to50Bytes,
19
+ DiscoverByAttributesArgs,
20
+ DiscoverByIdentityKeyArgs,
21
+ HexString,
22
+ InternalizeActionArgs,
23
+ InternalizeOutput,
24
+ KeyringRevealer,
25
+ LabelStringUnder300Bytes,
26
+ ListActionsArgs,
27
+ ListCertificatesArgs,
28
+ ListOutputsArgs,
29
+ OutpointString,
30
+ OutputTagStringUnder300Bytes,
31
+ PositiveInteger,
32
+ PositiveIntegerDefault10Max10000,
33
+ PositiveIntegerOrZero,
34
+ ProveCertificateArgs,
35
+ PubKeyHex,
36
+ RelinquishCertificateArgs,
37
+ RelinquishOutputArgs,
38
+ SatoshiValue,
39
+ SignActionArgs,
40
+ SignActionOptions,
41
+ SignActionSpend,
42
+ TrustSelf,
43
+ TXIDHexString,
44
+ WalletPayment
45
+ } from './Wallet.interfaces.js'
46
+ import * as Utils from '../primitives/utils.js'
47
+ import WERR_INVALID_PARAMETER from './WERR_INVALID_PARAMETER.js'
48
+ import Beef from '../transaction/Beef.js'
49
+
50
+ export function parseWalletOutpoint (outpoint: string): {
51
+ txid: string
52
+ vout: number
53
+ } {
54
+ const [txid, vout] = outpoint.split('.')
55
+ return { txid, vout: Number(vout) }
56
+ }
57
+
58
+ function defaultTrue (v?: boolean): boolean {
59
+ return v ?? true
60
+ }
61
+ function defaultFalse (v?: boolean): boolean {
62
+ return v ?? false
63
+ }
64
+ function defaultZero (v?: number): number {
65
+ return v ?? 0
66
+ }
67
+ function default0xffffffff (v?: number): number {
68
+ return v ?? 0xffffffff
69
+ }
70
+ function defaultOne (v?: number): number {
71
+ return v ?? 1
72
+ }
73
+ function defaultEmpty<T> (v?: T[]): T[] {
74
+ return v ?? []
75
+ }
76
+
77
+ function validateOptionalStringLength (
78
+ s: string | undefined,
79
+ name: string,
80
+ min?: number,
81
+ max?: number
82
+ ): string | undefined {
83
+ if (s === undefined) return undefined
84
+ return validateStringLength(s, name, min, max)
85
+ }
86
+
87
+ /**
88
+ * Validate a satoshi amount.
89
+ *
90
+ * @param v - value to validate (integer number of satoshis)
91
+ * @param name - parameter name used in error messages
92
+ * @param min - optional minimum allowed satoshi value
93
+ * @returns validated satoshi number
94
+ * @throws WERR_INVALID_PARAMETER when invalid
95
+ */
96
+ export function validateSatoshis (v: number | undefined, name: string, min?: number): number {
97
+ if (v === undefined || !Number.isInteger(v) || v < 0 || v > 21e14) { throw new WERR_INVALID_PARAMETER(name, 'a valid number of satoshis') }
98
+ if (min !== undefined && v < min) throw new WERR_INVALID_PARAMETER(name, `at least ${min} satoshis.`)
99
+ return v
100
+ }
101
+
102
+ /**
103
+ * Validate an optional integer. Returns undefined or the validated integer.
104
+ *
105
+ * @param v - value to validate (may be undefined)
106
+ * @param name - parameter name used in error messages
107
+ * @param min - optional minimum value
108
+ * @param max - optional maximum value
109
+ * @returns validated integer or undefined
110
+ * @throws WERR_INVALID_PARAMETER when invalid
111
+ */
112
+ export function validateOptionalInteger (
113
+ v: number | undefined,
114
+ name: string,
115
+ min?: number,
116
+ max?: number
117
+ ): number | undefined {
118
+ if (v === undefined) return undefined
119
+ return validateInteger(v, name, undefined, min, max)
120
+ }
121
+
122
+ /**
123
+ * Validate an integer, applying an optional default.
124
+ *
125
+ * @param v - value to validate (may be undefined)
126
+ * @param name - parameter name used in error messages
127
+ * @param defaultValue - value to return when v is undefined
128
+ * @param min - optional minimum allowed value
129
+ * @param max - optional maximum allowed value
130
+ * @returns validated integer
131
+ * @throws WERR_INVALID_PARAMETER when invalid
132
+ */
133
+ export function validateInteger (
134
+ v: number | undefined,
135
+ name: string,
136
+ defaultValue?: number,
137
+ min?: number,
138
+ max?: number
139
+ ): number {
140
+ if (v === undefined) {
141
+ if (defaultValue !== undefined) return defaultValue
142
+ throw new WERR_INVALID_PARAMETER(name, 'a valid integer')
143
+ }
144
+ if (!Number.isInteger(v)) throw new WERR_INVALID_PARAMETER(name, 'an integer')
145
+ v = Number(v)
146
+ if (min !== undefined && v < min) throw new WERR_INVALID_PARAMETER(name, `at least ${min} length.`)
147
+ if (max !== undefined && v > max) throw new WERR_INVALID_PARAMETER(name, `no more than ${max} length.`)
148
+ return v
149
+ }
150
+
151
+ /**
152
+ * Validate a non-negative integer (zero allowed).
153
+ *
154
+ * @param v - value to validate
155
+ * @param name - parameter name used in error messages
156
+ * @returns validated integer
157
+ * @throws WERR_INVALID_PARAMETER when invalid
158
+ */
159
+ export function validatePositiveIntegerOrZero (v: number, name: string): number {
160
+ return validateInteger(v, name, 0, 0)
161
+ }
162
+
163
+ /**
164
+ * Validate string length in bytes for UTF-8 encoded string.
165
+ *
166
+ * @param s - string to validate
167
+ * @param name - parameter name used in error messages
168
+ * @param min - optional minimum byte length
169
+ * @param max - optional maximum byte length
170
+ * @returns the original string when valid
171
+ * @throws WERR_INVALID_PARAMETER when invalid
172
+ */
173
+ export function validateStringLength (s: string, name: string, min?: number, max?: number): string {
174
+ const bytes = Utils.toArray(s, 'utf8').length
175
+ if (min !== undefined && bytes < min) throw new WERR_INVALID_PARAMETER(name, `at least ${min} length.`)
176
+ if (max !== undefined && bytes > max) throw new WERR_INVALID_PARAMETER(name, `no more than ${max} length.`)
177
+ return s
178
+ }
179
+
180
+ /**
181
+ * Validate an optional basket string (1..300 bytes).
182
+ *
183
+ * @param s - basket string or undefined
184
+ * @returns validated basket string or undefined
185
+ */
186
+ function validateOptionalBasket (s?: string): string | undefined {
187
+ if (s === undefined) return undefined
188
+ return validateBasket(s)
189
+ }
190
+
191
+ /**
192
+ * Validate basket identifier (1..300 bytes).
193
+ *
194
+ * @param s - basket string
195
+ * @returns validated basket string
196
+ */
197
+ function validateBasket (s: string): string {
198
+ return validateIdentifier(s, 'basket', 1, 300)
199
+ }
200
+
201
+ /**
202
+ * Validate label identifier (1..300 bytes).
203
+ *
204
+ * @param s - label string
205
+ * @returns validated label string
206
+ */
207
+ function validateLabel (s: string): string {
208
+ return validateIdentifier(s, 'label', 1, 300)
209
+ }
210
+
211
+ /**
212
+ * Validate tag identifier (1..300 bytes).
213
+ *
214
+ * @param s - tag string
215
+ * @returns validated tag string
216
+ */
217
+ function validateTag (s: string): string {
218
+ return validateIdentifier(s, 'tag', 1, 300)
219
+ }
220
+
221
+ /**
222
+ * Normalize and validate an identifier (trim, lowercase, byte length).
223
+ *
224
+ * @param s - input string
225
+ * @param name - name used in errors
226
+ * @param min - optional minimum byte length
227
+ * @param max - optional maximum byte length
228
+ * @returns normalized identifier
229
+ * @throws WERR_INVALID_PARAMETER when invalid
230
+ */
231
+ function validateIdentifier (s: string, name: string, min?: number, max?: number): string {
232
+ s = s.trim().toLowerCase()
233
+ const bytes = Utils.toArray(s, 'utf8').length
234
+ if (min !== undefined && bytes < min) throw new WERR_INVALID_PARAMETER(name, `at least ${min} length.`)
235
+ if (max !== undefined && bytes > max) throw new WERR_INVALID_PARAMETER(name, `no more than ${max} length.`)
236
+ return s
237
+ }
238
+
239
+ /**
240
+ * Validate an optional Base64 encoded string.
241
+ *
242
+ * @param s - base64 string or undefined
243
+ * @param name - parameter name used in error messages
244
+ * @param min - optional minimum decoded byte length
245
+ * @param max - optional maximum decoded byte length
246
+ * @returns validated base64 string or undefined
247
+ */
248
+ function validateOptionalBase64String (
249
+ s: string | undefined,
250
+ name: string,
251
+ min?: number,
252
+ max?: number
253
+ ): string | undefined {
254
+ if (s === undefined) return undefined
255
+ return validateBase64String(s, name, min, max)
256
+ }
257
+
258
+ /**
259
+ * Validate a Base64 string (structure and decoded size).
260
+ *
261
+ * @param s - base64 string
262
+ * @param name - parameter name used in error messages
263
+ * @param min - optional minimum decoded byte length
264
+ * @param max - optional maximum decoded byte length
265
+ * @returns validated base64 string
266
+ * @throws WERR_INVALID_PARAMETER when invalid
267
+ */
268
+ export function validateBase64String (s: string, name: string, min?: number, max?: number): string {
269
+ s = s.trim()
270
+ if (s.length === 0) {
271
+ throw new WERR_INVALID_PARAMETER(name, 'valid base64 string')
272
+ }
273
+
274
+ let paddingCount = 0
275
+ for (let i = 0; i < s.length; i++) {
276
+ const char = s.charCodeAt(i)
277
+ if (char >= 65 && char <= 90) continue // A-Z
278
+ if (char >= 97 && char <= 122) continue // a-z
279
+ if (char >= 48 && char <= 57) continue // 0-9
280
+ if (char === 43) continue // +
281
+ if (char === 47) continue // /
282
+ if (char === 61) { // =
283
+ if (i < s.length - 2) {
284
+ throw new WERR_INVALID_PARAMETER(name, 'valid base64 string')
285
+ }
286
+ paddingCount++
287
+ continue
288
+ }
289
+ throw new WERR_INVALID_PARAMETER(name, 'valid base64 string')
290
+ }
291
+
292
+ // Padding rules
293
+ if (paddingCount > 2) {
294
+ throw new WERR_INVALID_PARAMETER(name, 'valid base64 string')
295
+ }
296
+ if (paddingCount > 0 && s.length % 4 !== 0) {
297
+ throw new WERR_INVALID_PARAMETER(name, 'valid base64 string')
298
+ }
299
+
300
+ // Length must be multiple of 4 if no padding, or valid with padding
301
+ const mod = s.length % 4
302
+ if (mod !== 0 && mod !== (4 - paddingCount)) {
303
+ throw new WERR_INVALID_PARAMETER(name, 'valid base64 string')
304
+ }
305
+
306
+ // Calculate decoded byte length: (valid chars * 6) / 8
307
+ const encodedLength = s.length - paddingCount
308
+ const bytes = Math.floor(encodedLength * 3 / 4)
309
+
310
+ if (min !== undefined && bytes < min) {
311
+ throw new WERR_INVALID_PARAMETER(name, `at least ${min} bytes`)
312
+ }
313
+ if (max !== undefined && bytes > max) {
314
+ throw new WERR_INVALID_PARAMETER(name, `no more than ${max} bytes`)
315
+ }
316
+
317
+ return s
318
+ }
319
+
320
+ function validateOptionalHexString (
321
+ s: string | undefined,
322
+ name: string,
323
+ min?: number,
324
+ max?: number
325
+ ): string | undefined {
326
+ if (s === undefined) return undefined
327
+ return validateHexString(s, name, min, max)
328
+ }
329
+
330
+ /**
331
+ * Validate a hex string (even length, hex chars) and optional length bounds (character count).
332
+ *
333
+ * @param s - hex string
334
+ * @param name - parameter name used in error messages
335
+ * @param min if valid, string length minimum (not bytes)
336
+ * @param max if valid, string length maximum (not bytes)
337
+ * @returns
338
+ */
339
+ function validateHexString (s: string, name: string, min?: number, max?: number): string {
340
+ s = s.trim().toLowerCase()
341
+ if (s.length % 2 === 1) throw new WERR_INVALID_PARAMETER(name, `even length, not ${s.length}.`)
342
+ const hexRegex = /^[0-9A-Fa-f]+$/
343
+ if (!hexRegex.test(s)) throw new WERR_INVALID_PARAMETER(name, 'hexadecimal string.')
344
+ if (min !== undefined && s.length < min) throw new WERR_INVALID_PARAMETER(name, `at least ${min} length.`)
345
+ if (max !== undefined && s.length > max) throw new WERR_INVALID_PARAMETER(name, `no more than ${max} length.`)
346
+ return s
347
+ }
348
+
349
+ /**
350
+ * Check whether a string is a valid hex string (even length and hex characters).
351
+ *
352
+ * @param s - input string
353
+ * @returns true when s is a valid hex string
354
+ */
355
+ export function isHexString (s: string): boolean {
356
+ s = s.trim()
357
+ if (s.length % 2 === 1) return false
358
+ const hexRegex = /^[0-9A-Fa-f]+$/
359
+ if (!hexRegex.test(s)) return false
360
+ return true
361
+ }
362
+
363
+ /**
364
+ * DescriptionString5to2000Bytes alias type (documented).
365
+ */
366
+ export type DescriptionString5to2000Bytes = string
367
+
368
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
369
+ export interface ValidWalletSignerArgs {}
370
+
371
+ export interface ValidCreateActionInput {
372
+ outpoint: OutPoint
373
+ inputDescription: DescriptionString5to2000Bytes
374
+ sequenceNumber: PositiveIntegerOrZero
375
+ unlockingScript?: HexString
376
+ unlockingScriptLength: PositiveInteger
377
+ }
378
+
379
+ /**
380
+ * Validate a CreateActionInput structure.
381
+ *
382
+ * Ensures either unlockingScript or unlockingScriptLength is provided and consistent,
383
+ * validates outpoint, description length, and sequence number.
384
+ *
385
+ * @param i - CreateActionInput to validate
386
+ * @returns ValidCreateActionInput
387
+ * @throws WERR_INVALID_PARAMETER when invalid
388
+ */
389
+ export function validateCreateActionInput (i: CreateActionInput): ValidCreateActionInput {
390
+ if (i.unlockingScript === undefined && i.unlockingScriptLength === undefined) { throw new WERR_INVALID_PARAMETER('unlockingScript, unlockingScriptLength', 'at least one valid value.') }
391
+ const unlockingScript = validateOptionalHexString(i.unlockingScript, 'unlockingScript')
392
+ const unlockingScriptLength = i.unlockingScriptLength ?? (unlockingScript != null ? unlockingScript.length / 2 : 0)
393
+ if (unlockingScript && unlockingScriptLength !== unlockingScript.length / 2) { throw new WERR_INVALID_PARAMETER('unlockingScriptLength', 'length unlockingScript if both valid.') }
394
+ const vi: ValidCreateActionInput = {
395
+ outpoint: parseWalletOutpoint(i.outpoint),
396
+ inputDescription: validateStringLength(i.inputDescription, 'inputDescription', 5, 2000),
397
+ unlockingScript,
398
+ unlockingScriptLength,
399
+ sequenceNumber: default0xffffffff(i.sequenceNumber)
400
+ }
401
+ return vi
402
+ }
403
+
404
+ export interface ValidCreateActionOutput {
405
+ lockingScript: HexString
406
+ satoshis: SatoshiValue
407
+ outputDescription: DescriptionString5to2000Bytes
408
+ basket?: BasketStringUnder300Bytes
409
+ customInstructions?: string
410
+ tags: BasketStringUnder300Bytes[]
411
+ }
412
+
413
+ /**
414
+ * Validate CreateActionOutput fields: locking script, satoshis, description, basket, tags.
415
+ *
416
+ * @param o - CreateActionOutput to validate
417
+ * @returns ValidCreateActionOutput
418
+ * @throws WERR_INVALID_PARAMETER when invalid
419
+ */
420
+ export function validateCreateActionOutput (o: CreateActionOutput): ValidCreateActionOutput {
421
+ const vo: ValidCreateActionOutput = {
422
+ lockingScript: validateHexString(o.lockingScript, 'lockingScript'),
423
+ satoshis: validateSatoshis(o.satoshis, 'satoshis'),
424
+ outputDescription: validateStringLength(o.outputDescription, 'outputDescription', 5, 2000),
425
+ basket: validateOptionalBasket(o.basket),
426
+ customInstructions: o.customInstructions,
427
+ tags: defaultEmpty(o.tags).map(t => validateTag(t))
428
+ }
429
+ return vo
430
+ }
431
+
432
+ /**
433
+ * Normalize and validate CreateActionOptions, applying defaults for booleans/numbers/arrays.
434
+ *
435
+ * @param options - CreateActionOptions or undefined
436
+ * @returns ValidCreateActionOptions with defaults applied
437
+ */
438
+ export function validateCreateActionOptions (options?: CreateActionOptions): ValidCreateActionOptions {
439
+ const o = options != null ? options : {}
440
+ const vo: ValidCreateActionOptions = {
441
+ signAndProcess: defaultTrue(o.signAndProcess),
442
+ acceptDelayedBroadcast: defaultTrue(o.acceptDelayedBroadcast),
443
+ knownTxids: defaultEmpty(o.knownTxids),
444
+ returnTXIDOnly: defaultFalse(o.returnTXIDOnly),
445
+ noSend: defaultFalse(o.noSend),
446
+ noSendChange: defaultEmpty(o.noSendChange).map(nsc => parseWalletOutpoint(nsc)),
447
+ sendWith: defaultEmpty(o.sendWith),
448
+ randomizeOutputs: defaultTrue(o.randomizeOutputs)
449
+ }
450
+ return vo
451
+ }
452
+
453
+ export interface ValidProcessActionOptions {
454
+ acceptDelayedBroadcast: BooleanDefaultTrue
455
+ returnTXIDOnly: BooleanDefaultFalse
456
+ noSend: BooleanDefaultFalse
457
+ sendWith: TXIDHexString[]
458
+ }
459
+
460
+ export interface ValidCreateActionOptions extends ValidProcessActionOptions {
461
+ signAndProcess: boolean
462
+ trustSelf?: TrustSelf
463
+ knownTxids: TXIDHexString[]
464
+ noSendChange: OutPoint[]
465
+ randomizeOutputs: boolean
466
+ }
467
+
468
+ export interface ValidSignActionOptions extends ValidProcessActionOptions {
469
+ acceptDelayedBroadcast: boolean
470
+ returnTXIDOnly: boolean
471
+ noSend: boolean
472
+ sendWith: TXIDHexString[]
473
+ }
474
+
475
+ export interface ValidProcessActionArgs extends ValidWalletSignerArgs {
476
+ options: ValidProcessActionOptions
477
+ // true if a batch of transactions is included for processing.
478
+ isSendWith: boolean
479
+ // true if there is a new transaction (not no inputs and no outputs)
480
+ isNewTx: boolean
481
+ // true if this is a request to remix change, `isNewTx` will also be true and `isSendWith` must be false
482
+ isRemixChange: boolean
483
+ // true if any new transaction should NOT be sent to the network
484
+ isNoSend: boolean
485
+ // true if options.acceptDelayedBroadcast is true
486
+ isDelayed: boolean
487
+ // true if WERR_REVIEW_ACTIONS should be thrown to test review actions handling
488
+ isTestWerrReviewActions: boolean
489
+ }
490
+
491
+ export interface ValidCreateActionArgs extends ValidProcessActionArgs {
492
+ description: DescriptionString5to2000Bytes
493
+ inputBEEF?: BEEF
494
+ inputs: ValidCreateActionInput[]
495
+ outputs: ValidCreateActionOutput[]
496
+ lockTime: number
497
+ version: number
498
+ labels: string[]
499
+
500
+ options: ValidCreateActionOptions
501
+ // true if transaction creation completion will require a `signAction` call.
502
+ isSignAction: boolean
503
+ randomVals?: number[]
504
+ /**
505
+ * If true, signableTransactions will include sourceTransaction for each input,
506
+ * including those that do not require signature and those that were also contained
507
+ * in the inputBEEF.
508
+ */
509
+ includeAllSourceTransactions: boolean
510
+ }
511
+
512
+ export interface ValidSignActionArgs extends ValidProcessActionArgs {
513
+ spends: Record<PositiveIntegerOrZero, SignActionSpend>
514
+ reference: Base64String
515
+
516
+ options: ValidSignActionOptions
517
+ }
518
+
519
+ /**
520
+ * Validate the arguments for creating a new action.
521
+ *
522
+ * @param args
523
+ * @returns validated arguments
524
+ * @throws primarily WERR_INVALID_PARAMETER if args are invalid.
525
+ */
526
+ export function validateCreateActionArgs (args: CreateActionArgs): ValidCreateActionArgs {
527
+ const vargs: ValidCreateActionArgs = {
528
+ description: validateStringLength(args.description, 'description', 5, 2000),
529
+ inputBEEF: args.inputBEEF,
530
+ inputs: defaultEmpty(args.inputs).map(i => validateCreateActionInput(i)),
531
+ outputs: defaultEmpty(args.outputs).map(o => validateCreateActionOutput(o)),
532
+ lockTime: defaultZero(args.lockTime),
533
+ version: defaultOne(args.version),
534
+ labels: defaultEmpty(args.labels?.map(l => validateLabel(l))),
535
+ options: validateCreateActionOptions(args.options),
536
+ isSendWith: false,
537
+ isDelayed: false,
538
+ isNoSend: false,
539
+ isNewTx: false,
540
+ isRemixChange: false,
541
+ isSignAction: false,
542
+ randomVals: undefined,
543
+ includeAllSourceTransactions: false,
544
+ isTestWerrReviewActions: false
545
+ }
546
+ vargs.isTestWerrReviewActions = vargs.labels.includes(specOpThrowReviewActions)
547
+ vargs.isSendWith = vargs.options.sendWith.length > 0
548
+ vargs.isRemixChange = !vargs.isSendWith && vargs.inputs.length === 0 && vargs.outputs.length === 0
549
+ vargs.isNewTx = vargs.isRemixChange || vargs.inputs.length > 0 || vargs.outputs.length > 0
550
+ vargs.isSignAction =
551
+ vargs.isNewTx && (!vargs.options.signAndProcess || vargs.inputs.some(i => i.unlockingScript === undefined))
552
+ vargs.isDelayed = vargs.options.acceptDelayedBroadcast
553
+ vargs.isNoSend = vargs.options.noSend
554
+
555
+ return vargs
556
+ }
557
+
558
+ /**
559
+ * Set all default true/false booleans to true or false if undefined.
560
+ * Set all possibly undefined numbers to their default values.
561
+ * Set all possibly undefined arrays to empty arrays.
562
+ * Convert string outpoints to `{ txid: string, vout: number }`
563
+ */
564
+ export function validateSignActionOptions (options?: SignActionOptions): ValidSignActionOptions {
565
+ const o = options != null ? options : {}
566
+ const vo: ValidSignActionOptions = {
567
+ acceptDelayedBroadcast: defaultTrue(o.acceptDelayedBroadcast),
568
+ returnTXIDOnly: defaultFalse(o.returnTXIDOnly),
569
+ noSend: defaultFalse(o.noSend),
570
+ sendWith: defaultEmpty(o.sendWith)
571
+ }
572
+ return vo
573
+ }
574
+
575
+ /**
576
+ * Validate SignActionArgs and apply defaults/flags.
577
+ *
578
+ * @param args - SignActionArgs to validate
579
+ * @returns ValidSignActionArgs
580
+ */
581
+ export function validateSignActionArgs (args: SignActionArgs): ValidSignActionArgs {
582
+ const vargs: ValidSignActionArgs = {
583
+ spends: args.spends,
584
+ reference: args.reference,
585
+ options: validateSignActionOptions(args.options),
586
+ isSendWith: false,
587
+ isDelayed: false,
588
+ isNoSend: false,
589
+ isNewTx: true,
590
+ isRemixChange: false,
591
+ isTestWerrReviewActions: false
592
+ }
593
+ vargs.isSendWith = vargs.options.sendWith.length > 0
594
+ vargs.isDelayed = vargs.options.acceptDelayedBroadcast
595
+ vargs.isNoSend = vargs.options.noSend
596
+
597
+ return vargs
598
+ }
599
+
600
+ export interface ValidAbortActionArgs extends ValidWalletSignerArgs {
601
+ reference: Base64String
602
+ }
603
+
604
+ /**
605
+ * Validate AbortActionArgs (ensures reference is a valid base64 string).
606
+ *
607
+ * @param args - AbortActionArgs
608
+ * @returns ValidAbortActionArgs
609
+ */
610
+ export function validateAbortActionArgs (args: AbortActionArgs): ValidAbortActionArgs {
611
+ const vargs: ValidAbortActionArgs = {
612
+ reference: validateBase64String(args.reference, 'reference')
613
+ }
614
+
615
+ return vargs
616
+ }
617
+
618
+ export interface ValidWalletPayment {
619
+ derivationPrefix: Base64String
620
+ derivationSuffix: Base64String
621
+ senderIdentityKey: PubKeyHex
622
+ }
623
+
624
+ /**
625
+ * Validate wallet payment remittance structure.
626
+ *
627
+ * @param args - WalletPayment or undefined
628
+ * @returns ValidWalletPayment or undefined
629
+ */
630
+ export function validateWalletPayment (args?: WalletPayment): ValidWalletPayment | undefined {
631
+ if (args === undefined) return undefined
632
+ const v: ValidWalletPayment = {
633
+ derivationPrefix: validateBase64String(args.derivationPrefix, 'derivationPrefix'),
634
+ derivationSuffix: validateBase64String(args.derivationSuffix, 'derivationSuffix'),
635
+ senderIdentityKey: validateHexString(args.senderIdentityKey, 'senderIdentityKey')
636
+ }
637
+ return v
638
+ }
639
+
640
+ export interface ValidBasketInsertion {
641
+ basket: BasketStringUnder300Bytes
642
+ customInstructions?: string
643
+ tags: BasketStringUnder300Bytes[]
644
+ }
645
+
646
+ /**
647
+ * Validate a BasketInsertion structure (basket, custom instructions, tags).
648
+ *
649
+ * @param args - BasketInsertion or undefined
650
+ * @returns ValidBasketInsertion or undefined
651
+ */
652
+ export function validateBasketInsertion (args?: BasketInsertion): ValidBasketInsertion | undefined {
653
+ if (args === undefined) return undefined
654
+ const v: ValidBasketInsertion = {
655
+ basket: validateBasket(args.basket),
656
+ customInstructions: validateOptionalStringLength(args.customInstructions, 'customInstructions', 0, 1000), // TODO: real max??
657
+ tags: defaultEmpty(args.tags).map(t => validateTag(t))
658
+ }
659
+ return v
660
+ }
661
+
662
+ export interface ValidInternalizeOutput {
663
+ outputIndex: PositiveIntegerOrZero
664
+ protocol: 'wallet payment' | 'basket insertion'
665
+ paymentRemittance?: ValidWalletPayment
666
+ insertionRemittance?: ValidBasketInsertion
667
+ }
668
+
669
+ /**
670
+ * Validate an InternalizeOutput entry.
671
+ *
672
+ * @param args - InternalizeOutput to validate
673
+ * @returns ValidInternalizeOutput
674
+ */
675
+ export function validateInternalizeOutput (args: InternalizeOutput): ValidInternalizeOutput {
676
+ if (args.protocol !== 'basket insertion' && args.protocol !== 'wallet payment') { throw new WERR_INVALID_PARAMETER('protocol', '\'basket insertion\' or \'wallet payment\'') }
677
+ const v: ValidInternalizeOutput = {
678
+ outputIndex: validatePositiveIntegerOrZero(args.outputIndex, 'outputIndex'),
679
+ protocol: args.protocol,
680
+ paymentRemittance: validateWalletPayment(args.paymentRemittance),
681
+ insertionRemittance: validateBasketInsertion(args.insertionRemittance)
682
+ }
683
+ return v
684
+ }
685
+
686
+ export interface ValidInternalizeActionArgs extends ValidWalletSignerArgs {
687
+ tx: AtomicBEEF
688
+ outputs: InternalizeOutput[]
689
+ description: DescriptionString5to2000Bytes
690
+ labels: LabelStringUnder300Bytes[]
691
+ seekPermission: BooleanDefaultTrue
692
+ }
693
+
694
+ /**
695
+ * Validate originator string (trim/lowercase and part length checks).
696
+ *
697
+ * @param s - originator string or undefined
698
+ * @returns normalized originator or undefined
699
+ */
700
+ export function validateOriginator (s?: string): string | undefined {
701
+ if (s === undefined) return undefined
702
+ s = s.trim().toLowerCase()
703
+ validateStringLength(s, 'originator', 1, 250)
704
+ const sps = s.split('.')
705
+ for (const sp of sps) {
706
+ validateStringLength(sp, 'originator part', 1, 63)
707
+ }
708
+ return s
709
+ }
710
+
711
+ /**
712
+ * Validate InternalizeActionArgs: tx, outputs, description, labels, permission flag.
713
+ *
714
+ * @param args - InternalizeActionArgs to validate
715
+ * @returns ValidInternalizeActionArgs
716
+ * @throws WERR_INVALID_PARAMETER when invalid
717
+ */
718
+ export function validateInternalizeActionArgs (args: InternalizeActionArgs): ValidInternalizeActionArgs {
719
+ const vargs: ValidInternalizeActionArgs = {
720
+ tx: args.tx,
721
+ outputs: args.outputs.map(o => validateInternalizeOutput(o)),
722
+ description: validateStringLength(args.description, 'description', 5, 2000),
723
+ labels: (args.labels != null ? args.labels : []).map(t => validateLabel(t)),
724
+ seekPermission: defaultTrue(args.seekPermission)
725
+ }
726
+
727
+ try {
728
+ const beef = Beef.fromBinary(vargs.tx)
729
+ if (beef.txs.length < 1) { throw new WERR_INVALID_PARAMETER('tx', 'at least one transaction to internalize an output from') }
730
+ } catch {
731
+ throw new WERR_INVALID_PARAMETER('tx', 'valid with at least one transaction to internalize an output from')
732
+ }
733
+ if (vargs.outputs.length < 1) { throw new WERR_INVALID_PARAMETER('outputs', 'at least one output to internalize from the transaction') }
734
+
735
+ return vargs
736
+ }
737
+
738
+ /**
739
+ * Validate an optional outpoint string (txid.vout).
740
+ *
741
+ * @param outpoint - outpoint string or undefined
742
+ * @param name - parameter name used in error messages
743
+ * @returns validated outpoint string or undefined
744
+ */
745
+ export function validateOptionalOutpointString (outpoint: string | undefined, name: string): string | undefined {
746
+ if (outpoint === undefined) return undefined
747
+ return validateOutpointString(outpoint, name)
748
+ }
749
+
750
+ /**
751
+ * Validate an outpoint string of the form txid.vout.
752
+ *
753
+ * @param outpoint - outpoint string
754
+ * @param name - parameter name used in error messages
755
+ * @returns normalized outpoint string (validated txid and vout)
756
+ * @throws WERR_INVALID_PARAMETER when invalid
757
+ */
758
+ export function validateOutpointString (outpoint: string, name: string): string {
759
+ const s = outpoint.split('.')
760
+ if (s.length !== 2 || !Number.isInteger(Number(s[1]))) { throw new WERR_INVALID_PARAMETER(name, 'txid as hex string and numeric output index joined with \'.\'') }
761
+ const txid = validateHexString(s[0], `${name} txid`, undefined, 64)
762
+ const vout = validatePositiveIntegerOrZero(Number(s[1]), `${name} vout`)
763
+ return `${txid}.${vout}`
764
+ }
765
+
766
+ export interface ValidRelinquishOutputArgs extends ValidWalletSignerArgs {
767
+ basket: BasketStringUnder300Bytes
768
+ output: OutpointString
769
+ }
770
+
771
+ /**
772
+ * Validate RelinquishOutputArgs (basket and output).
773
+ *
774
+ * @param args - RelinquishOutputArgs
775
+ * @returns ValidRelinquishOutputArgs
776
+ */
777
+ export function validateRelinquishOutputArgs (args: RelinquishOutputArgs): ValidRelinquishOutputArgs {
778
+ const vargs: ValidRelinquishOutputArgs = {
779
+ basket: validateBasket(args.basket),
780
+ output: validateOutpointString(args.output, 'output')
781
+ }
782
+
783
+ return vargs
784
+ }
785
+
786
+ export interface ValidRelinquishCertificateArgs extends ValidWalletSignerArgs {
787
+ type: Base64String
788
+ serialNumber: Base64String
789
+ certifier: PubKeyHex
790
+ }
791
+
792
+ /**
793
+ * Validate RelinquishCertificateArgs (type, serialNumber, certifier).
794
+ *
795
+ * @param args - RelinquishCertificateArgs
796
+ * @returns ValidRelinquishCertificateArgs
797
+ */
798
+ export function validateRelinquishCertificateArgs (args: RelinquishCertificateArgs): ValidRelinquishCertificateArgs {
799
+ const vargs: ValidRelinquishCertificateArgs = {
800
+ type: validateBase64String(args.type, 'type'),
801
+ serialNumber: validateBase64String(args.serialNumber, 'serialNumber'),
802
+ certifier: validateHexString(args.certifier, 'certifier')
803
+ }
804
+
805
+ return vargs
806
+ }
807
+
808
+ export interface ValidListCertificatesArgs extends ValidWalletSignerArgs {
809
+ partial?: {
810
+ type?: Base64String
811
+ serialNumber?: Base64String
812
+ certifier?: PubKeyHex
813
+ subject?: PubKeyHex
814
+ revocationOutpoint?: OutpointString
815
+ signature?: HexString
816
+ }
817
+ certifiers: PubKeyHex[]
818
+ types: Base64String[]
819
+ limit: PositiveIntegerDefault10Max10000
820
+ offset: PositiveIntegerOrZero
821
+ privileged: BooleanDefaultFalse
822
+ privilegedReason?: DescriptionString5to50Bytes
823
+ }
824
+
825
+ /**
826
+ * Validate ListCertificatesArgs: certifiers, types, paging, and optional privileged reason.
827
+ *
828
+ * @param args - ListCertificatesArgs
829
+ * @returns ValidListCertificatesArgs
830
+ */
831
+ export function validateListCertificatesArgs (args: ListCertificatesArgs): ValidListCertificatesArgs {
832
+ const vargs: ValidListCertificatesArgs = {
833
+ certifiers: defaultEmpty(args.certifiers.map(c => validateHexString(c.trim(), 'certifiers'))),
834
+ types: defaultEmpty(args.types.map(t => validateBase64String(t.trim(), 'types'))),
835
+ limit: validateInteger(args.limit, 'limit', 10, 1, 10000),
836
+ offset: validatePositiveIntegerOrZero(defaultZero(args.offset), 'offset'),
837
+ privileged: defaultFalse(args.privileged),
838
+ privilegedReason: validateOptionalStringLength(args.privilegedReason, 'privilegedReason', 5, 50),
839
+ partial: undefined
840
+ }
841
+ return vargs
842
+ }
843
+
844
+ export interface ValidAcquireCertificateArgs extends ValidWalletSignerArgs {
845
+ acquisitionProtocol: AcquisitionProtocol
846
+
847
+ type: Base64String
848
+ serialNumber?: Base64String
849
+ certifier: PubKeyHex
850
+ revocationOutpoint?: OutpointString
851
+ fields: Record<CertificateFieldNameUnder50Bytes, string>
852
+ signature?: HexString
853
+
854
+ certifierUrl?: string
855
+
856
+ keyringRevealer?: KeyringRevealer
857
+ keyringForSubject?: Record<CertificateFieldNameUnder50Bytes, Base64String>
858
+
859
+ privileged: boolean
860
+ privilegedReason?: DescriptionString5to50Bytes
861
+ }
862
+
863
+ function validateCertificateFields (
864
+ fields: Record<CertificateFieldNameUnder50Bytes, string>
865
+ ): Record<CertificateFieldNameUnder50Bytes, string> {
866
+ for (const fieldName of Object.keys(fields)) {
867
+ validateStringLength(fieldName, 'field name', 1, 50)
868
+ }
869
+ return fields
870
+ }
871
+
872
+ function validateKeyringRevealer (kr: KeyringRevealer, name: string): KeyringRevealer {
873
+ if (kr === 'certifier') return kr
874
+ return validateHexString(kr, name)
875
+ }
876
+
877
+ function validateKeyringForSubject (
878
+ kr: Record<CertificateFieldNameUnder50Bytes, Base64String>,
879
+ name: string
880
+ ): Record<CertificateFieldNameUnder50Bytes, Base64String> {
881
+ for (const fn of Object.keys(kr)) {
882
+ validateStringLength(fn, `${name} field name`, 1, 50)
883
+ validateBase64String(kr[fn], `${name} field value`)
884
+ }
885
+ return kr
886
+ }
887
+
888
+ export interface ValidAcquireDirectCertificateArgs extends ValidWalletSignerArgs {
889
+ type: Base64String
890
+ serialNumber: Base64String
891
+ certifier: PubKeyHex
892
+ revocationOutpoint: OutpointString
893
+ fields: Record<CertificateFieldNameUnder50Bytes, string>
894
+ signature: HexString
895
+
896
+ /**
897
+ * validated to an empty string, must be provided by wallet and must
898
+ * match expectations of keyringForSubject
899
+ */
900
+ subject: PubKeyHex
901
+
902
+ keyringRevealer: KeyringRevealer
903
+ keyringForSubject: Record<CertificateFieldNameUnder50Bytes, Base64String>
904
+
905
+ privileged: boolean
906
+ privilegedReason?: DescriptionString5to50Bytes
907
+ }
908
+
909
+ export interface ValidAcquireIssuanceCertificateArgs extends ValidWalletSignerArgs {
910
+ type: Base64String
911
+ certifier: PubKeyHex
912
+ certifierUrl: string
913
+ fields: Record<CertificateFieldNameUnder50Bytes, string>
914
+
915
+ /**
916
+ * validated to an empty string, must be provided by wallet and must
917
+ * match expectations of keyringForSubject
918
+ */
919
+ subject: PubKeyHex
920
+
921
+ privileged: boolean
922
+ privilegedReason?: DescriptionString5to50Bytes
923
+ }
924
+
925
+ /**
926
+ * Validate issuance-specific acquire certificate args.
927
+ *
928
+ * @param args - AcquireCertificateArgs with acquisitionProtocol === 'issuance'
929
+ * @returns ValidAcquireIssuanceCertificateArgs
930
+ * @throws when args contain fields invalid for issuance
931
+ */
932
+ export function validateAcquireIssuanceCertificateArgs (
933
+ args: AcquireCertificateArgs
934
+ ): ValidAcquireIssuanceCertificateArgs {
935
+ if (args.acquisitionProtocol !== 'issuance') { throw new Error('Only acquire certificate via issuance requests allowed here.') }
936
+ if (args.serialNumber) throw new WERR_INVALID_PARAMETER('serialNumber', 'valid when acquisitionProtocol is "direct"')
937
+ if (args.signature) throw new WERR_INVALID_PARAMETER('signature', 'valid when acquisitionProtocol is "direct"')
938
+ if (args.revocationOutpoint) { throw new WERR_INVALID_PARAMETER('revocationOutpoint', 'valid when acquisitionProtocol is "direct"') }
939
+ if (args.keyringRevealer) { throw new WERR_INVALID_PARAMETER('keyringRevealer', 'valid when acquisitionProtocol is "direct"') }
940
+ if (args.keyringForSubject != null) { throw new WERR_INVALID_PARAMETER('keyringForSubject', 'valid when acquisitionProtocol is "direct"') }
941
+ if (!args.certifierUrl) { throw new WERR_INVALID_PARAMETER('certifierUrl', 'valid when acquisitionProtocol is "issuance"') }
942
+ if (args.privileged && !args.privilegedReason) { throw new WERR_INVALID_PARAMETER('privilegedReason', 'valid when \'privileged\' is true ') }
943
+
944
+ const vargs: ValidAcquireIssuanceCertificateArgs = {
945
+ type: validateBase64String(args.type, 'type'),
946
+ certifier: validateHexString(args.certifier, 'certifier'),
947
+ certifierUrl: args.certifierUrl,
948
+ fields: validateCertificateFields(args.fields),
949
+ privileged: defaultFalse(args.privileged),
950
+ privilegedReason: validateOptionalStringLength(args.privilegedReason, 'privilegedReason', 5, 50),
951
+ subject: ''
952
+ }
953
+ return vargs
954
+ }
955
+
956
+ /**
957
+ * Validate direct-acquisition-specific acquire certificate args.
958
+ *
959
+ * @param args - AcquireCertificateArgs with acquisitionProtocol === 'direct'
960
+ * @returns ValidAcquireDirectCertificateArgs
961
+ * @throws when args contain fields invalid for direct acquisition
962
+ */
963
+ export function validateAcquireDirectCertificateArgs (args: AcquireCertificateArgs): ValidAcquireDirectCertificateArgs {
964
+ if (args.acquisitionProtocol !== 'direct') { throw new Error('Only acquire direct certificate requests allowed here.') }
965
+ if (!args.serialNumber) throw new WERR_INVALID_PARAMETER('serialNumber', 'valid when acquisitionProtocol is "direct"')
966
+ if (!args.signature) throw new WERR_INVALID_PARAMETER('signature', 'valid when acquisitionProtocol is "direct"')
967
+ if (!args.revocationOutpoint) { throw new WERR_INVALID_PARAMETER('revocationOutpoint', 'valid when acquisitionProtocol is "direct"') }
968
+ if (!args.keyringRevealer) { throw new WERR_INVALID_PARAMETER('keyringRevealer', 'valid when acquisitionProtocol is "direct"') }
969
+ if (args.keyringForSubject == null) { throw new WERR_INVALID_PARAMETER('keyringForSubject', 'valid when acquisitionProtocol is "direct"') }
970
+ if (args.privileged && !args.privilegedReason) { throw new WERR_INVALID_PARAMETER('privilegedReason', 'valid when \'privileged\' is true ') }
971
+
972
+ const vargs: ValidAcquireDirectCertificateArgs = {
973
+ type: validateBase64String(args.type, 'type'),
974
+ serialNumber: validateBase64String(args.serialNumber, 'serialNumber'),
975
+ certifier: validateHexString(args.certifier, 'certifier'),
976
+ revocationOutpoint: validateOutpointString(args.revocationOutpoint, 'revocationOutpoint'),
977
+ fields: validateCertificateFields(args.fields),
978
+ signature: validateHexString(args.signature, 'signature'),
979
+ keyringRevealer: validateKeyringRevealer(args.keyringRevealer, 'keyringRevealer'),
980
+ keyringForSubject: validateKeyringForSubject(args.keyringForSubject, 'keyringForSubject'),
981
+ privileged: defaultFalse(args.privileged),
982
+ privilegedReason: validateOptionalStringLength(args.privilegedReason, 'privilegedReason', 5, 50),
983
+ subject: ''
984
+ }
985
+ return vargs
986
+ }
987
+
988
+ export interface ValidProveCertificateArgs extends ValidWalletSignerArgs {
989
+ type?: Base64String
990
+ serialNumber?: Base64String
991
+ certifier?: PubKeyHex
992
+ subject?: PubKeyHex
993
+ revocationOutpoint?: OutpointString
994
+ signature?: HexString
995
+
996
+ fieldsToReveal: CertificateFieldNameUnder50Bytes[]
997
+ verifier: PubKeyHex
998
+ privileged: boolean
999
+ privilegedReason?: DescriptionString5to50Bytes
1000
+ }
1001
+
1002
+ /**
1003
+ * Validate ProveCertificateArgs including optional certificate fields and reveal list.
1004
+ *
1005
+ * @param args - ProveCertificateArgs
1006
+ * @returns ValidProveCertificateArgs
1007
+ */
1008
+ export function validateProveCertificateArgs (args: ProveCertificateArgs): ValidProveCertificateArgs {
1009
+ if (args.privileged && !args.privilegedReason) { throw new WERR_INVALID_PARAMETER('privilegedReason', 'valid when \'privileged\' is true ') }
1010
+
1011
+ const vargs: ValidProveCertificateArgs = {
1012
+ type: validateOptionalBase64String(args.certificate.type, 'certificate.type'),
1013
+ serialNumber: validateOptionalBase64String(args.certificate.serialNumber, 'certificate.serialNumber'),
1014
+ certifier: validateOptionalHexString(args.certificate.certifier, 'certificate.certifier'),
1015
+ subject: validateOptionalHexString(args.certificate.subject, 'certificate.subject'),
1016
+ revocationOutpoint: validateOptionalOutpointString(
1017
+ args.certificate.revocationOutpoint,
1018
+ 'certificate.revocationOutpoint'
1019
+ ),
1020
+ signature: validateOptionalHexString(args.certificate.signature, 'certificate.signature'),
1021
+ fieldsToReveal: defaultEmpty(args.fieldsToReveal).map(fieldName =>
1022
+ validateStringLength(fieldName, `fieldsToReveal ${fieldName}`, 1, 50)
1023
+ ),
1024
+ verifier: validateHexString(args.verifier, 'verifier'),
1025
+ privileged: defaultFalse(args.privileged),
1026
+ privilegedReason: validateOptionalStringLength(args.privilegedReason, 'privilegedReason', 5, 50)
1027
+ }
1028
+ return vargs
1029
+ }
1030
+
1031
+ export interface ValidDiscoverByIdentityKeyArgs extends ValidWalletSignerArgs {
1032
+ identityKey: PubKeyHex
1033
+ limit: PositiveIntegerDefault10Max10000
1034
+ offset: PositiveIntegerOrZero
1035
+ seekPermission: boolean
1036
+ }
1037
+
1038
+ /**
1039
+ * Validate DiscoverByIdentityKeyArgs, enforcing identity key length and defaults.
1040
+ *
1041
+ * @param args - DiscoverByIdentityKeyArgs
1042
+ * @returns ValidDiscoverByIdentityKeyArgs
1043
+ */
1044
+ export function validateDiscoverByIdentityKeyArgs (args: DiscoverByIdentityKeyArgs): ValidDiscoverByIdentityKeyArgs {
1045
+ const vargs: ValidDiscoverByIdentityKeyArgs = {
1046
+ identityKey: validateHexString(args.identityKey, 'identityKey', 66, 66),
1047
+ limit: validateInteger(args.limit, 'limit', 10, 1, 10000),
1048
+ offset: validatePositiveIntegerOrZero(defaultZero(args.offset), 'offset'),
1049
+ seekPermission: defaultFalse(args.seekPermission)
1050
+ }
1051
+ return vargs
1052
+ }
1053
+
1054
+ export interface ValidDiscoverByAttributesArgs extends ValidWalletSignerArgs {
1055
+ attributes: Record<CertificateFieldNameUnder50Bytes, string>
1056
+ limit: PositiveIntegerDefault10Max10000
1057
+ offset: PositiveIntegerOrZero
1058
+ seekPermission: boolean
1059
+ }
1060
+
1061
+ function validateAttributes (
1062
+ attributes: Record<CertificateFieldNameUnder50Bytes, string>
1063
+ ): Record<CertificateFieldNameUnder50Bytes, string> {
1064
+ for (const fieldName of Object.keys(attributes)) {
1065
+ validateStringLength(fieldName, `field name ${fieldName}`, 1, 50)
1066
+ }
1067
+ return attributes
1068
+ }
1069
+
1070
+ /**
1071
+ * Validate DiscoverByAttributesArgs: attributes, limit, offset, and permission flag.
1072
+ *
1073
+ * @param args - DiscoverByAttributesArgs
1074
+ * @returns ValidDiscoverByAttributesArgs
1075
+ */
1076
+ export function validateDiscoverByAttributesArgs (args: DiscoverByAttributesArgs): ValidDiscoverByAttributesArgs {
1077
+ const vargs: ValidDiscoverByAttributesArgs = {
1078
+ attributes: validateAttributes(args.attributes),
1079
+ limit: validateInteger(args.limit, 'limit', 10, 1, 10000),
1080
+ offset: validatePositiveIntegerOrZero(defaultZero(args.offset), 'offset'),
1081
+ seekPermission: defaultFalse(args.seekPermission)
1082
+ }
1083
+ return vargs
1084
+ }
1085
+
1086
+ export interface ValidListOutputsArgs extends ValidWalletSignerArgs {
1087
+ basket: BasketStringUnder300Bytes
1088
+ tags: OutputTagStringUnder300Bytes[]
1089
+ tagQueryMode: 'all' | 'any'
1090
+ includeLockingScripts: boolean
1091
+ includeTransactions: boolean
1092
+ includeCustomInstructions: BooleanDefaultFalse
1093
+ includeTags: BooleanDefaultFalse
1094
+ includeLabels: BooleanDefaultFalse
1095
+ limit: PositiveIntegerDefault10Max10000
1096
+ offset: number
1097
+ seekPermission: BooleanDefaultTrue
1098
+ knownTxids: string[]
1099
+ }
1100
+
1101
+ /**
1102
+ * @param {BasketStringUnder300Bytes} args.basket - Required. The associated basket name whose outputs should be listed.
1103
+ * @param {OutputTagStringUnder300Bytes[]} [args.tags] - Optional. Filter outputs based on these tags.
1104
+ * @param {'all' | 'any'} [args.tagQueryMode] - Optional. Filter mode, defining whether all or any of the tags must match. By default, any tag can match.
1105
+ * @param {'locking scripts' | 'entire transactions'} [args.include] - Optional. Whether to include locking scripts (with each output) or entire transactions (as aggregated BEEF, at the top level) in the result. By default, unless specified, neither are returned.
1106
+ * @param {BooleanDefaultFalse} [args.includeEntireTransactions] - Optional. Whether to include the entire transaction(s) in the result.
1107
+ * @param {BooleanDefaultFalse} [args.includeCustomInstructions] - Optional. Whether custom instructions should be returned in the result.
1108
+ * @param {BooleanDefaultFalse} [args.includeTags] - Optional. Whether the tags associated with the output should be returned.
1109
+ * @param {BooleanDefaultFalse} [args.includeLabels] - Optional. Whether the labels associated with the transaction containing the output should be returned.
1110
+ * @param {PositiveIntegerDefault10Max10000} [args.limit] - Optional limit on the number of outputs to return.
1111
+ * @param {number} [args.offset] - If positive or zero: Number of outputs to skip before starting to return results, oldest first.
1112
+ * If negative: Outputs are returned newest first and offset of -1 is the newest output.
1113
+ * When using negative offsets, caution is required as new outputs may be added between calls,
1114
+ * potentially causing outputs to be duplicated across calls.
1115
+ * @param {BooleanDefaultTrue} [args.seekPermission] — Optional. Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
1116
+ */
1117
+ export function validateListOutputsArgs (args: ListOutputsArgs): ValidListOutputsArgs {
1118
+ let tagQueryMode: 'any' | 'all'
1119
+ if (args.tagQueryMode === undefined || args.tagQueryMode === 'any') tagQueryMode = 'any'
1120
+ else if (args.tagQueryMode === 'all') tagQueryMode = 'all'
1121
+ else throw new WERR_INVALID_PARAMETER('tagQueryMode', 'undefined, \'any\', or \'all\'')
1122
+
1123
+ const vargs: ValidListOutputsArgs = {
1124
+ basket: validateStringLength(args.basket, 'basket', 1, 300),
1125
+ tags: (args.tags != null ? args.tags : []).map(t => validateStringLength(t, 'tag', 1, 300)),
1126
+ tagQueryMode,
1127
+ includeLockingScripts: args.include === 'locking scripts',
1128
+ includeTransactions: args.include === 'entire transactions',
1129
+ includeCustomInstructions: defaultFalse(args.includeCustomInstructions),
1130
+ includeTags: defaultFalse(args.includeTags),
1131
+ includeLabels: defaultFalse(args.includeLabels),
1132
+ limit: validateInteger(args.limit, 'limit', 10, 1, 10000),
1133
+ offset: validateInteger(args.offset, 'offset', 0, undefined, undefined),
1134
+ seekPermission: defaultTrue(args.seekPermission),
1135
+ knownTxids: []
1136
+ }
1137
+
1138
+ return vargs
1139
+ }
1140
+
1141
+ export interface ValidListActionsArgs extends ValidWalletSignerArgs {
1142
+ labels: LabelStringUnder300Bytes[]
1143
+ labelQueryMode: 'any' | 'all'
1144
+ includeLabels: BooleanDefaultFalse
1145
+ includeInputs: BooleanDefaultFalse
1146
+ includeInputSourceLockingScripts: BooleanDefaultFalse
1147
+ includeInputUnlockingScripts: BooleanDefaultFalse
1148
+ includeOutputs: BooleanDefaultFalse
1149
+ includeOutputLockingScripts: BooleanDefaultFalse
1150
+ limit: PositiveIntegerDefault10Max10000
1151
+ offset: PositiveIntegerOrZero
1152
+ seekPermission: BooleanDefaultTrue
1153
+ }
1154
+
1155
+ /**
1156
+ * @param {LabelStringUnder300Bytes[]} args.labels - An array of labels used to filter actions.
1157
+ * @param {'any' | 'all'} [args.labelQueryMode] - Optional. Specifies how to match labels (default is any which matches any of the labels).
1158
+ * @param {BooleanDefaultFalse} [args.includeLabels] - Optional. Whether to include transaction labels in the result set.
1159
+ * @param {BooleanDefaultFalse} [args.includeInputs] - Optional. Whether to include input details in the result set.
1160
+ * @param {BooleanDefaultFalse} [args.includeInputSourceLockingScripts] - Optional. Whether to include input source locking scripts in the result set.
1161
+ * @param {BooleanDefaultFalse} [args.includeInputUnlockingScripts] - Optional. Whether to include input unlocking scripts in the result set.
1162
+ * @param {BooleanDefaultFalse} [args.includeOutputs] - Optional. Whether to include output details in the result set.
1163
+ * @param {BooleanDefaultFalse} [args.includeOutputLockingScripts] - Optional. Whether to include output locking scripts in the result set.
1164
+ * @param {PositiveIntegerDefault10Max10000} [args.limit] - Optional. The maximum number of transactions to retrieve.
1165
+ * @param {PositiveIntegerOrZero} [args.offset] - Optional. Number of transactions to skip before starting to return the results.
1166
+ * @param {BooleanDefaultTrue} [args.seekPermission] — Optional. Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
1167
+ */
1168
+ export function validateListActionsArgs (args: ListActionsArgs): ValidListActionsArgs {
1169
+ let labelQueryMode: 'any' | 'all'
1170
+ if (args.labelQueryMode === undefined || args.labelQueryMode === 'any') labelQueryMode = 'any'
1171
+ else if (args.labelQueryMode === 'all') labelQueryMode = 'all'
1172
+ else throw new WERR_INVALID_PARAMETER('labelQueryMode', 'undefined, \'any\', or \'all\'')
1173
+
1174
+ const vargs: ValidListActionsArgs = {
1175
+ labels: (args.labels != null ? args.labels : []).map(t => validateLabel(t)),
1176
+ labelQueryMode,
1177
+ includeLabels: defaultFalse(args.includeLabels),
1178
+ includeInputs: defaultFalse(args.includeInputs),
1179
+ includeInputSourceLockingScripts: defaultFalse(args.includeInputSourceLockingScripts),
1180
+ includeInputUnlockingScripts: defaultFalse(args.includeInputUnlockingScripts),
1181
+ includeOutputs: defaultFalse(args.includeOutputs),
1182
+ includeOutputLockingScripts: defaultFalse(args.includeOutputLockingScripts),
1183
+ limit: validateInteger(args.limit, 'limit', 10, 1, 10000),
1184
+ offset: validateInteger(args.offset, 'offset', 0, 0),
1185
+ seekPermission: defaultTrue(args.seekPermission)
1186
+ }
1187
+
1188
+ return vargs
1189
+ }
1190
+
1191
+ /**
1192
+ * Identifies a unique transaction output by its `txid` and index `vout`
1193
+ */
1194
+ export interface OutPoint {
1195
+ /**
1196
+ * Transaction double sha256 hash as big endian hex string
1197
+ */
1198
+ txid: string
1199
+ /**
1200
+ * zero based output index within the transaction
1201
+ */
1202
+ vout: number
1203
+ }
1204
+
1205
+ /**
1206
+ * `createAction` special operation label name value.
1207
+ *
1208
+ * Causes WERR_REVIEW_ACTIONS throw with dummy properties.
1209
+ *
1210
+ */
1211
+ export const specOpThrowReviewActions = 'a496e747fc3ad5fabdd4ae8f91184e71f87539bd3d962aa2548942faaaf0047a'