@bsv/sdk 1.6.12 → 1.6.14

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 (82) hide show
  1. package/README.md +4 -4
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/cjs/src/transaction/broadcasters/DefaultBroadcaster.js +1 -1
  4. package/dist/cjs/src/transaction/broadcasters/DefaultBroadcaster.js.map +1 -1
  5. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  6. package/dist/esm/src/transaction/broadcasters/DefaultBroadcaster.js +1 -1
  7. package/dist/esm/src/transaction/broadcasters/DefaultBroadcaster.js.map +1 -1
  8. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  9. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  10. package/dist/umd/bundle.js +1 -1
  11. package/docs/MARKDOWN_VALIDATION_GUIDE.md +175 -0
  12. package/docs/concepts/beef.md +8 -0
  13. package/docs/concepts/chain-tracking.md +12 -0
  14. package/docs/concepts/decentralized-identity.md +37 -0
  15. package/docs/concepts/fees.md +32 -0
  16. package/docs/concepts/identity-certificates.md +53 -1
  17. package/docs/concepts/index.md +15 -0
  18. package/docs/concepts/key-management.md +9 -0
  19. package/docs/concepts/script-templates.md +13 -0
  20. package/docs/concepts/sdk-philosophy.md +8 -0
  21. package/docs/concepts/signatures.md +15 -0
  22. package/docs/concepts/spv-verification.md +12 -0
  23. package/docs/concepts/transaction-encoding.md +19 -0
  24. package/docs/concepts/transaction-structure.md +4 -0
  25. package/docs/concepts/trust-model.md +16 -0
  26. package/docs/concepts/verification.md +31 -0
  27. package/docs/concepts/wallet-integration.md +6 -0
  28. package/docs/guides/development-wallet-setup.md +374 -0
  29. package/docs/guides/direct-transaction-creation.md +12 -2
  30. package/docs/guides/http-client-configuration.md +122 -48
  31. package/docs/guides/index.md +117 -9
  32. package/docs/guides/large-transactions.md +448 -0
  33. package/docs/guides/multisig-transactions.md +792 -0
  34. package/docs/guides/security-best-practices.md +494 -0
  35. package/docs/guides/transaction-batching.md +132 -0
  36. package/docs/guides/transaction-signing-methods.md +230 -79
  37. package/docs/index.md +0 -2
  38. package/docs/reference/auth.md +212 -159
  39. package/docs/reference/compat.md +120 -96
  40. package/docs/reference/configuration.md +6 -0
  41. package/docs/reference/debugging.md +5 -0
  42. package/docs/reference/errors.md +50 -0
  43. package/docs/reference/identity.md +21 -12
  44. package/docs/reference/index.md +14 -1
  45. package/docs/reference/kvstore.md +21 -19
  46. package/docs/reference/messages.md +3 -0
  47. package/docs/reference/op-codes.md +20 -1
  48. package/docs/reference/overlay-tools.md +46 -18
  49. package/docs/reference/primitives.md +571 -390
  50. package/docs/reference/registry.md +43 -20
  51. package/docs/reference/script.md +140 -105
  52. package/docs/reference/storage.md +32 -12
  53. package/docs/reference/totp.md +16 -11
  54. package/docs/reference/transaction-signatures.md +2 -1
  55. package/docs/reference/transaction.md +201 -120
  56. package/docs/reference/wallet.md +241 -64
  57. package/docs/tutorials/advanced-transaction.md +1 -4
  58. package/docs/tutorials/aes-encryption.md +3 -1
  59. package/docs/tutorials/authfetch-tutorial.md +29 -0
  60. package/docs/tutorials/ecdh-key-exchange.md +2 -0
  61. package/docs/tutorials/elliptic-curve-fundamentals.md +3 -0
  62. package/docs/tutorials/error-handling.md +1 -0
  63. package/docs/tutorials/first-transaction-low-level.md +1 -0
  64. package/docs/tutorials/first-transaction.md +5 -8
  65. package/docs/tutorials/hashes-and-hmacs.md +5 -31
  66. package/docs/tutorials/identity-management.md +27 -0
  67. package/docs/tutorials/index.md +114 -77
  68. package/docs/tutorials/key-management.md +5 -3
  69. package/docs/tutorials/protowallet-development.md +27 -0
  70. package/docs/tutorials/spv-merkle-proofs.md +9 -6
  71. package/docs/tutorials/testnet-transactions-low-level.md +25 -18
  72. package/docs/tutorials/transaction-broadcasting.md +10 -7
  73. package/docs/tutorials/transaction-types.md +5 -4
  74. package/docs/tutorials/type-42.md +0 -14
  75. package/docs/tutorials/uhrp-storage.md +23 -3
  76. package/package.json +1 -1
  77. package/src/identity/README.md +0 -1
  78. package/src/primitives/__tests/SymmetricKey.test.ts +45 -0
  79. package/src/primitives/__tests/SymmetricKeyCompatibility.test.ts +150 -0
  80. package/src/transaction/__tests/Transaction.test.ts +1 -1
  81. package/src/transaction/broadcasters/DefaultBroadcaster.ts +1 -1
  82. package/src/transaction/broadcasters/__tests/ARC.test.ts +1 -1
@@ -0,0 +1,494 @@
1
+ # Security Best Practices
2
+
3
+ This comprehensive guide covers essential security practices when developing Bitcoin applications with the BSV TypeScript SDK. Following these guidelines will help you build secure, production-ready applications that protect user funds and data.
4
+
5
+ ## Prerequisites
6
+
7
+ - Understanding of Bitcoin cryptography fundamentals
8
+ - Familiarity with the BSV TypeScript SDK
9
+ - Basic knowledge of secure coding practices
10
+ - Understanding of common attack vectors in cryptocurrency applications
11
+
12
+ > **📚 Related Concepts**: This guide builds on [Key Management](../concepts/key-management.md), [Trust Model](../concepts/trust-model.md), [Digital Signatures](../concepts/signatures.md), and [Transaction Verification](../concepts/verification.md) concepts.
13
+
14
+ ## Key Security Principles
15
+
16
+ ### 1. Private Key Management
17
+
18
+ #### Never Expose Private Keys
19
+
20
+ ```typescript
21
+ // ❌ NEVER do this - exposing private key in logs or UI
22
+ console.log('Private key:', privateKey.toWif())
23
+ alert(`Your key: ${privateKey.toWif()}`)
24
+
25
+ // ✅ Proper handling - keep private keys secure
26
+ const privateKey = PrivateKey.fromRandom()
27
+ // Use the key for operations without exposing it
28
+ const publicKey = privateKey.toPublicKey()
29
+ ```
30
+
31
+ #### Secure Key Generation
32
+
33
+ ```typescript
34
+ import { PrivateKey } from '@bsv/sdk'
35
+
36
+ // ✅ Use cryptographically secure random generation
37
+ const secureKey = PrivateKey.fromRandom()
38
+
39
+ // ❌ Never use predictable sources
40
+ // const weakKey = PrivateKey.fromString('1') // Predictable
41
+ // const timeKey = PrivateKey.fromString(Date.now().toString()) // Predictable
42
+ ```
43
+
44
+ #### Key Storage Best Practices
45
+
46
+ ```typescript
47
+ // ✅ For production applications, use secure storage
48
+ class SecureKeyManager {
49
+ private encryptionKey: SymmetricKey
50
+
51
+ constructor() {
52
+ // Derive encryption key from user password or hardware security module
53
+ this.encryptionKey = SymmetricKey.fromRandom()
54
+ }
55
+
56
+ async storePrivateKey(privateKey: PrivateKey, identifier: string): Promise<void> {
57
+ const keyData = privateKey.toWif()
58
+ const encrypted = this.encryptionKey.encrypt(keyData)
59
+
60
+ // Store encrypted key in secure storage (not localStorage for production)
61
+ await this.secureStorage.set(identifier, Buffer.from(encrypted).toString('base64'))
62
+ }
63
+
64
+ async retrievePrivateKey(identifier: string): Promise<PrivateKey> {
65
+ const encryptedData = await this.secureStorage.get(identifier)
66
+ const encryptedBuffer = Buffer.from(encryptedData, 'base64')
67
+ const decrypted = this.encryptionKey.decrypt(Array.from(encryptedBuffer), 'utf8')
68
+ return PrivateKey.fromWif(decrypted as string)
69
+ }
70
+ }
71
+ ```
72
+
73
+ ### 2. Transaction Security
74
+
75
+ #### Input Validation and Sanitization
76
+
77
+ ```typescript
78
+ import { Transaction, PrivateKey, P2PKH } from '@bsv/sdk'
79
+
80
+ class SecureTransactionBuilder {
81
+ static validateAmount(satoshis: number): void {
82
+ if (!Number.isInteger(satoshis)) {
83
+ throw new Error('Amount must be an integer')
84
+ }
85
+ if (satoshis <= 0) {
86
+ throw new Error('Amount must be positive')
87
+ }
88
+ if (satoshis > 21000000 * 100000000) {
89
+ throw new Error('Amount exceeds maximum possible Bitcoin supply')
90
+ }
91
+ }
92
+
93
+ static validateAddress(address: string): void {
94
+ try {
95
+ // Validate address format
96
+ P2PKH.unlock('', 'all', {
97
+ publicKey: address, // This will throw if invalid
98
+ signature: { inputIndex: 0, outputs: [], inputScript: '' }
99
+ })
100
+ } catch (error) {
101
+ throw new Error('Invalid Bitcoin address format')
102
+ }
103
+ }
104
+
105
+ static async createSecureTransaction(
106
+ privateKey: PrivateKey,
107
+ recipientAddress: string,
108
+ amount: number
109
+ ): Promise<Transaction> {
110
+ // Validate all inputs
111
+ this.validateAmount(amount)
112
+ this.validateAddress(recipientAddress)
113
+
114
+ // Create transaction with validated inputs
115
+ const tx = new Transaction()
116
+ // ... transaction construction logic
117
+
118
+ return tx
119
+ }
120
+ }
121
+ ```
122
+
123
+ #### Fee Calculation Security
124
+
125
+ ```typescript
126
+ // ✅ Always validate fee calculations to prevent fee attacks
127
+ class SecureFeeCalculator {
128
+ private static readonly MIN_FEE_RATE = 0.5 // satoshis per byte
129
+ private static readonly MAX_FEE_RATE = 1000 // satoshis per byte
130
+
131
+ static calculateFee(transactionSize: number, feeRate: number): number {
132
+ // Validate fee rate is within reasonable bounds
133
+ if (feeRate < this.MIN_FEE_RATE || feeRate > this.MAX_FEE_RATE) {
134
+ throw new Error(`Fee rate must be between ${this.MIN_FEE_RATE} and ${this.MAX_FEE_RATE} sat/byte`)
135
+ }
136
+
137
+ const fee = Math.ceil(transactionSize * feeRate)
138
+
139
+ // Additional validation to prevent excessive fees
140
+ if (fee > 100000) { // 0.001 BSV maximum fee
141
+ throw new Error('Calculated fee is unreasonably high')
142
+ }
143
+
144
+ return fee
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### 3. Cryptographic Operations Security
150
+
151
+ #### Secure Random Number Generation
152
+
153
+ ```typescript
154
+ import { PrivateKey, SymmetricKey } from '@bsv/sdk'
155
+
156
+ // ✅ Always use the SDK's secure random generation
157
+ const securePrivateKey = PrivateKey.fromRandom()
158
+ const secureSymmetricKey = SymmetricKey.fromRandom()
159
+
160
+ // ❌ Never use Math.random() for cryptographic purposes
161
+ // const insecureKey = PrivateKey.fromString(Math.random().toString())
162
+ ```
163
+
164
+ #### ECDH Key Exchange Security
165
+
166
+ ```typescript
167
+ import { PrivateKey, PublicKey } from '@bsv/sdk'
168
+
169
+ class SecureECDH {
170
+ static performKeyExchange(
171
+ myPrivateKey: PrivateKey,
172
+ theirPublicKey: PublicKey
173
+ ): Buffer {
174
+ try {
175
+ // The SDK automatically validates the public key and prevents twist attacks
176
+ const sharedSecret = myPrivateKey.deriveSharedSecret(theirPublicKey)
177
+
178
+ // ✅ Always derive keys from the shared secret, never use it directly
179
+ if (!sharedSecret.x) {
180
+ throw new Error('Invalid shared secret')
181
+ }
182
+ const sharedSecretBuffer = Buffer.from(sharedSecret.x.toArray())
183
+ const contextBuffer = Buffer.from('application-specific-context', 'utf8')
184
+ const combinedBuffer = Buffer.concat([sharedSecretBuffer, contextBuffer])
185
+ const derivedKey = Hash.sha256(Array.from(combinedBuffer))
186
+
187
+ return Buffer.from(derivedKey)
188
+ } catch (error) {
189
+ throw new Error('Key exchange failed: Invalid public key')
190
+ }
191
+ }
192
+ }
193
+ ```
194
+
195
+ #### AES Encryption Security
196
+
197
+ ```typescript
198
+ import { SymmetricKey, Hash } from '@bsv/sdk'
199
+
200
+ class SecureEncryption {
201
+ // ✅ Proper key derivation from passwords
202
+ static deriveKeyFromPassword(password: string, salt: Buffer): SymmetricKey {
203
+ if (password.length < 12) {
204
+ throw new Error('Password must be at least 12 characters')
205
+ }
206
+
207
+ // Use multiple rounds of hashing for key derivation
208
+ let derived = Array.from(Buffer.concat([Buffer.from(password, 'utf8'), salt]))
209
+ for (let i = 0; i < 10000; i++) {
210
+ derived = Hash.sha256(derived)
211
+ }
212
+
213
+ return new SymmetricKey(derived)
214
+ }
215
+
216
+ // ✅ Secure encryption with proper error handling
217
+ static encryptData(data: string, key: SymmetricKey): string {
218
+ try {
219
+ const encrypted = key.encrypt(data)
220
+ return Buffer.from(encrypted).toString('base64')
221
+ } catch (error) {
222
+ // Don't expose internal error details
223
+ throw new Error('Encryption failed')
224
+ }
225
+ }
226
+
227
+ // ✅ Secure decryption with validation
228
+ static decryptData(encryptedData: string, key: SymmetricKey): string {
229
+ try {
230
+ const encrypted = Buffer.from(encryptedData, 'base64')
231
+ const decrypted = key.decrypt(Array.from(encrypted), 'utf8')
232
+ return decrypted as string
233
+ } catch (error) {
234
+ throw new Error('Decryption failed: Invalid data or key')
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ ### 4. Wallet Integration Security
241
+
242
+ #### Secure WalletClient Usage
243
+
244
+ ```typescript
245
+ import { WalletClient } from '@bsv/sdk'
246
+
247
+ class SecureWalletManager {
248
+ private wallet: WalletClient | null = null
249
+ private connectionAttempts = 0
250
+ private readonly MAX_CONNECTION_ATTEMPTS = 3
251
+
252
+ async connectWallet(): Promise<void> {
253
+ if (this.connectionAttempts >= this.MAX_CONNECTION_ATTEMPTS) {
254
+ throw new Error('Maximum connection attempts exceeded')
255
+ }
256
+
257
+ try {
258
+ this.wallet = new WalletClient('auto', 'localhost')
259
+ // Connection is established during construction
260
+ this.connectionAttempts = 0 // Reset on successful connection
261
+ } catch (error) {
262
+ this.connectionAttempts++
263
+ throw new Error('Wallet connection failed')
264
+ }
265
+ }
266
+
267
+ async createSecureTransaction(outputs: any[]): Promise<any> {
268
+ if (!this.wallet) {
269
+ throw new Error('Wallet not connected')
270
+ }
271
+
272
+ // Validate all outputs before creating transaction
273
+ for (const output of outputs) {
274
+ if (!output.satoshis || output.satoshis <= 0) {
275
+ throw new Error('Invalid output amount')
276
+ }
277
+ if (!output.lockingScript) {
278
+ throw new Error('Missing locking script')
279
+ }
280
+ }
281
+
282
+ try {
283
+ return await this.wallet.createAction({
284
+ description: 'Secure transaction',
285
+ outputs,
286
+ // ✅ Always include proper error handling options
287
+ options: {
288
+ acceptDelayedBroadcast: false, // Ensure immediate feedback
289
+ randomizeOutputs: true // Enhance privacy
290
+ }
291
+ })
292
+ } catch (error) {
293
+ // Log error securely without exposing sensitive data
294
+ console.error('Transaction creation failed:', error.message)
295
+ throw new Error('Transaction creation failed')
296
+ }
297
+ }
298
+ }
299
+ ```
300
+
301
+ When using the `WalletClient` interface, follow these security practices:
302
+
303
+ ### Secure `WalletClient` Usage
304
+
305
+ The `WalletClient` provides built-in security features, but proper usage is essential:
306
+
307
+ ### `WalletClient` Connection Management
308
+
309
+ When working with `WalletClient` connections:
310
+
311
+ ### 5. Network Security
312
+
313
+ #### Secure Chain Tracker Usage
314
+
315
+ ```typescript
316
+ import { ChainTracker, WhatsOnChain } from '@bsv/sdk'
317
+
318
+ class SecureChainTracker {
319
+ private trackers: ChainTracker[]
320
+ private currentTrackerIndex = 0
321
+
322
+ constructor() {
323
+ // ✅ Use multiple chain trackers for redundancy
324
+ this.trackers = [
325
+ new WhatsOnChain('main'),
326
+ // Add additional trackers for failover
327
+ ]
328
+ }
329
+
330
+ async getTransactionWithRetry(txid: string): Promise<any> {
331
+ let lastError: Error | null = null
332
+
333
+ // Try each tracker
334
+ for (let i = 0; i < this.trackers.length; i++) {
335
+ try {
336
+ const tracker = this.trackers[this.currentTrackerIndex]
337
+ const result = await tracker.getTransaction(txid)
338
+
339
+ // Validate the response
340
+ if (!result || !result.id) {
341
+ throw new Error('Invalid transaction response')
342
+ }
343
+
344
+ return result
345
+ } catch (error) {
346
+ lastError = error as Error
347
+ this.currentTrackerIndex = (this.currentTrackerIndex + 1) % this.trackers.length
348
+ }
349
+ }
350
+
351
+ throw new Error(`All chain trackers failed: ${lastError?.message}`)
352
+ }
353
+ }
354
+ ```
355
+
356
+ ### 6. SPV Verification Security
357
+
358
+ #### Secure Merkle Proof Verification
359
+
360
+ ```typescript
361
+ import { Transaction, MerklePath } from '@bsv/sdk'
362
+
363
+ class SecureSPVVerifier {
364
+ static async verifyTransaction(
365
+ transaction: Transaction,
366
+ merklePath: MerklePath,
367
+ blockHeader: any
368
+ ): Promise<boolean> {
369
+ try {
370
+ // ✅ Always verify the merkle proof
371
+ const txid = Buffer.from(transaction.id()).toString('hex')
372
+ const computedRoot = merklePath.computeRoot(txid)
373
+
374
+ if (computedRoot !== blockHeader.merkleRoot) {
375
+ throw new Error('Merkle proof verification failed')
376
+ }
377
+
378
+ // ✅ Verify the transaction itself
379
+ const isValid = await transaction.verify()
380
+ if (!isValid) {
381
+ throw new Error('Transaction verification failed')
382
+ }
383
+
384
+ return true
385
+ } catch (error) {
386
+ console.error('SPV verification failed:', error.message)
387
+ return false
388
+ }
389
+ }
390
+ }
391
+ ```
392
+
393
+ ### 7. Error Handling Security
394
+
395
+ #### Secure Error Reporting
396
+
397
+ ```typescript
398
+ class SecureErrorHandler {
399
+ // ✅ Sanitize error messages to prevent information leakage
400
+ static sanitizeError(error: Error): string {
401
+ const sensitivePatterns = [
402
+ /private.*key/i,
403
+ /seed/i,
404
+ /mnemonic/i,
405
+ /password/i,
406
+ /secret/i
407
+ ]
408
+
409
+ let message = error.message
410
+
411
+ for (const pattern of sensitivePatterns) {
412
+ message = message.replace(pattern, '[REDACTED]')
413
+ }
414
+
415
+ return message
416
+ }
417
+
418
+ // ✅ Secure error logging
419
+ static logError(error: Error, context: string): void {
420
+ const sanitizedMessage = this.sanitizeError(error)
421
+ console.error(`[${context}] ${sanitizedMessage}`)
422
+
423
+ // In production, send to secure logging service
424
+ // Never log sensitive information
425
+ }
426
+ }
427
+ ```
428
+
429
+ ## Common Security Vulnerabilities
430
+
431
+ ### 1. Private Key Exposure
432
+
433
+ ```typescript
434
+ // ❌ Common mistakes that expose private keys
435
+ class InsecureExamples {
436
+ // Never store keys in plain text
437
+ private userKey = 'L1234567890abcdef...' // Exposed in source code
438
+
439
+ // Never log private keys
440
+ debugTransaction(privateKey: PrivateKey) {
441
+ console.log('Signing with key:', privateKey.toWif()) // Logged
442
+ }
443
+
444
+ // Never send keys over insecure channels
445
+ async sendKeyToServer(key: PrivateKey) {
446
+ await fetch('http://api.example.com/keys', { // HTTP not HTTPS
447
+ method: 'POST',
448
+ body: JSON.stringify({ key: key.toWif() })
449
+ })
450
+ }
451
+ }
452
+ ```
453
+
454
+ ### 2. Insufficient Input Validation
455
+
456
+ ```typescript
457
+ // ❌ Vulnerable to various attacks
458
+ class VulnerableTransaction {
459
+ async createTransaction(amount: string, address: string) {
460
+ // No validation - vulnerable to injection and overflow
461
+ const tx = new Transaction()
462
+ tx.addOutput({
463
+ satoshis: parseInt(amount), // No validation
464
+ lockingScript: address // No validation
465
+ })
466
+ return tx
467
+ }
468
+ }
469
+ ```
470
+
471
+ ### 3. Weak Random Number Generation
472
+
473
+ ```typescript
474
+ // ❌ Predictable and insecure
475
+ class WeakRandomness {
476
+ generatePrivateKey(): PrivateKey {
477
+ // Predictable seed
478
+ const seed = Date.now().toString() + Math.random().toString()
479
+ return PrivateKey.fromString(seed)
480
+ }
481
+ }
482
+ ```
483
+
484
+ ## Security Resources
485
+
486
+ ### Additional Reading
487
+
488
+ - [Bitcoin Security Best Practices](https://en.bitcoin.it/wiki/Securing_your_wallet)
489
+ - [Cryptographic Best Practices](https://cryptography.io/en/latest/faq/)
490
+ - [OWASP Cryptographic Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)
491
+
492
+ ## Conclusion
493
+
494
+ Security in Bitcoin applications requires constant vigilance and adherence to best practices. The BSV TypeScript SDK provides many security features out of the box, but proper implementation and configuration are crucial for maintaining security.
@@ -0,0 +1,132 @@
1
+ # Implementing Transaction Batching
2
+
3
+ This guide demonstrates how to efficiently batch multiple payments into single transactions using the BSV TypeScript SDK, optimizing for fees, network efficiency, and throughput.
4
+
5
+ ## Overview
6
+
7
+ Transaction batching combines multiple payments into a single transaction, reducing fees and network load. This guide covers various batching strategies from simple multi-output transactions to advanced batch processing systems.
8
+
9
+ ## Basic Multi-Output Batching
10
+
11
+ ### Simple Batch Payment
12
+
13
+ The most straightforward batching approach combines multiple payments into one transaction:
14
+
15
+ ```typescript
16
+ import { WalletClient } from '@bsv/sdk'
17
+
18
+ async function createBatchPayment() {
19
+ const wallet = new WalletClient('auto', 'localhost')
20
+
21
+ // Define multiple recipients with proper P2PKH locking scripts
22
+ const recipients = [
23
+ { lockingScript: '76a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac', amount: 100, description: 'Payment 1' },
24
+ { lockingScript: '76a91477bde5cfd66c6d1c83b0008b8a6e3579a6e5c6b988ac', amount: 150, description: 'Payment 2' },
25
+ { lockingScript: '76a914c42e7ef92fdb603af844d064faad95db9f7f1e9588ac', amount: 200, description: 'Payment 3' }
26
+ ]
27
+
28
+ // Create batch transaction
29
+ const actionResult = await wallet.createAction({
30
+ description: 'Batch payment to multiple recipients',
31
+ outputs: recipients.map(recipient => ({
32
+ satoshis: recipient.amount,
33
+ lockingScript: recipient.lockingScript,
34
+ outputDescription: recipient.description
35
+ })),
36
+ })
37
+
38
+ console.log('Batch transaction created:', actionResult.txid)
39
+ return actionResult
40
+ }
41
+ ```
42
+
43
+ ### Batch with Data Outputs
44
+
45
+ Combine payments with data storage in a single transaction:
46
+
47
+ ```typescript
48
+ async function createBatchWithData() {
49
+ const wallet = new WalletClient('auto', 'localhost')
50
+
51
+ const outputs = [
52
+ // Payment outputs
53
+ {
54
+ satoshis: 100,
55
+ lockingScript: '76a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac',
56
+ outputDescription: 'Payment 1'
57
+ },
58
+ {
59
+ satoshis: 150,
60
+ lockingScript: '76a91477bde5cfd66c6d1c83b0008b8a6e3579a6e5c6b988ac',
61
+ outputDescription: 'Payment 2'
62
+ },
63
+ // Data outputs (OP_RETURN outputs use 1 satoshi)
64
+ {
65
+ satoshis: 1,
66
+ lockingScript: '006a0c48656c6c6f20576f726c64', // OP_RETURN "Hello World"
67
+ outputDescription: 'Batch metadata'
68
+ },
69
+ {
70
+ satoshis: 1,
71
+ lockingScript: '006a0a42617463682044617461', // OP_RETURN "Batch Data"
72
+ outputDescription: 'Batch identifier'
73
+ }
74
+ ]
75
+
76
+ const actionResult = await wallet.createAction({
77
+ description: 'Batch payment with metadata',
78
+ outputs,
79
+ })
80
+
81
+ return actionResult
82
+ }
83
+ ```
84
+
85
+ ### UTXO Consolidation Batching
86
+
87
+ Combine payments with UTXO consolidation for better wallet health:
88
+
89
+ ```typescript
90
+ async function createConsolidationBatch() {
91
+ const wallet = new WalletClient('auto', 'localhost')
92
+
93
+ // Note: This example demonstrates the concept but uses simulated data
94
+ // In practice, you would get UTXOs from wallet.listOutputs() with appropriate basket
95
+ console.log('Creating consolidation batch with simulated UTXO data')
96
+
97
+ // Create batch with payments only (consolidation would require actual UTXOs)
98
+ const outputs = [
99
+ // Regular payments
100
+ {
101
+ satoshis: 100,
102
+ lockingScript: '76a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac',
103
+ outputDescription: 'Batch payment 1'
104
+ },
105
+ {
106
+ satoshis: 150,
107
+ lockingScript: '76a91477bde5cfd66c6d1c83b0008b8a6e3579a6e5c6b988ac',
108
+ outputDescription: 'Batch payment 2'
109
+ },
110
+ // Change output (simulating consolidation)
111
+ {
112
+ satoshis: 500,
113
+ lockingScript: '76a914c42e7ef92fdb603af844d064faad95db9f7f1e9588ac',
114
+ outputDescription: 'Change output'
115
+ }
116
+ ]
117
+
118
+ // Let wallet automatically select inputs (no manual input specification)
119
+ const actionResult = await wallet.createAction({
120
+ description: 'Batch payment with change output',
121
+ outputs,
122
+ })
123
+
124
+ return actionResult
125
+ }
126
+ ```
127
+
128
+ ## Conclusion
129
+
130
+ Transaction batching is a powerful technique for optimizing Bitcoin applications.
131
+
132
+ Understanding of `WalletClient` usage is essential for implementing these strategies effectively. While the `WalletClient` provides convenient single-transaction creation, batching multiple operations into fewer transactions can significantly improve efficiency and reduce costs. Integration with `WalletClient` is straightforward, and the benefits of batching can be substantial.