@bsv/sdk 1.9.2 → 1.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/kvstore/GlobalKVStore.js +116 -98
- package/dist/cjs/src/kvstore/GlobalKVStore.js.map +1 -1
- package/dist/cjs/src/kvstore/types.js.map +1 -1
- package/dist/cjs/src/overlay-tools/index.js +1 -0
- package/dist/cjs/src/overlay-tools/index.js.map +1 -1
- package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js +55 -0
- package/dist/cjs/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/kvstore/GlobalKVStore.js +117 -99
- package/dist/esm/src/kvstore/GlobalKVStore.js.map +1 -1
- package/dist/esm/src/kvstore/types.js.map +1 -1
- package/dist/esm/src/overlay-tools/index.js +1 -0
- package/dist/esm/src/overlay-tools/index.js.map +1 -1
- package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js +48 -0
- package/dist/esm/src/overlay-tools/withDoubleSpendRetry.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/kvstore/GlobalKVStore.d.ts.map +1 -1
- package/dist/types/src/kvstore/types.d.ts +2 -0
- package/dist/types/src/kvstore/types.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/index.d.ts +1 -0
- package/dist/types/src/overlay-tools/index.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts +14 -0
- package/dist/types/src/overlay-tools/withDoubleSpendRetry.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +2 -2
- package/dist/umd/bundle.js.map +1 -1
- package/docs/fast-docs.png +0 -0
- package/docs/index.md +49 -44
- package/docs/reference/kvstore.md +9 -0
- package/docs/reference/overlay-tools.md +32 -0
- package/docs/swagger.png +0 -0
- package/package.json +1 -1
- package/src/kvstore/GlobalKVStore.ts +134 -114
- package/src/kvstore/__tests/GlobalKVStore.test.ts +11 -1
- package/src/kvstore/types.ts +2 -0
- package/src/overlay-tools/index.ts +1 -0
- package/src/overlay-tools/withDoubleSpendRetry.ts +71 -0
- package/docs/MARKDOWN_VALIDATION_GUIDE.md +0 -175
- package/docs/concepts/beef.md +0 -92
- package/docs/concepts/chain-tracking.md +0 -134
- package/docs/concepts/decentralized-identity.md +0 -221
- package/docs/concepts/fees.md +0 -249
- package/docs/concepts/identity-certificates.md +0 -307
- package/docs/concepts/index.md +0 -77
- package/docs/concepts/key-management.md +0 -185
- package/docs/concepts/script-templates.md +0 -176
- package/docs/concepts/sdk-philosophy.md +0 -80
- package/docs/concepts/signatures.md +0 -194
- package/docs/concepts/spv-verification.md +0 -118
- package/docs/concepts/transaction-encoding.md +0 -167
- package/docs/concepts/transaction-structure.md +0 -67
- package/docs/concepts/trust-model.md +0 -139
- package/docs/concepts/verification.md +0 -250
- package/docs/concepts/wallet-integration.md +0 -101
- package/docs/guides/development-wallet-setup.md +0 -374
- package/docs/guides/direct-transaction-creation.md +0 -147
- package/docs/guides/http-client-configuration.md +0 -488
- package/docs/guides/index.md +0 -138
- package/docs/guides/large-transactions.md +0 -448
- package/docs/guides/multisig-transactions.md +0 -792
- package/docs/guides/security-best-practices.md +0 -494
- package/docs/guides/transaction-batching.md +0 -132
- package/docs/guides/transaction-signing-methods.md +0 -419
- package/docs/reference/arc-config.md +0 -698
- package/docs/reference/brc-100.md +0 -33
- package/docs/reference/configuration.md +0 -835
- package/docs/reference/debugging.md +0 -705
- package/docs/reference/errors.md +0 -597
- package/docs/reference/index.md +0 -111
- package/docs/reference/network-config.md +0 -914
- package/docs/reference/op-codes.md +0 -325
- package/docs/reference/transaction-signatures.md +0 -95
- package/docs/tutorials/advanced-transaction.md +0 -572
- package/docs/tutorials/aes-encryption.md +0 -949
- package/docs/tutorials/authfetch-tutorial.md +0 -986
- package/docs/tutorials/ecdh-key-exchange.md +0 -549
- package/docs/tutorials/elliptic-curve-fundamentals.md +0 -606
- package/docs/tutorials/error-handling.md +0 -1216
- package/docs/tutorials/first-transaction-low-level.md +0 -205
- package/docs/tutorials/first-transaction.md +0 -275
- package/docs/tutorials/hashes-and-hmacs.md +0 -788
- package/docs/tutorials/identity-management.md +0 -729
- package/docs/tutorials/index.md +0 -219
- package/docs/tutorials/key-management.md +0 -538
- package/docs/tutorials/protowallet-development.md +0 -743
- package/docs/tutorials/script-construction.md +0 -690
- package/docs/tutorials/spv-merkle-proofs.md +0 -685
- package/docs/tutorials/testnet-transactions-low-level.md +0 -359
- package/docs/tutorials/transaction-broadcasting.md +0 -538
- package/docs/tutorials/transaction-types.md +0 -420
- package/docs/tutorials/type-42.md +0 -568
- package/docs/tutorials/uhrp-storage.md +0 -599
|
@@ -1,572 +0,0 @@
|
|
|
1
|
-
**Duration**: 75 minutes
|
|
2
|
-
**Prerequisites**: Completed "Transaction Types and Data" tutorial, Node.js, basic TypeScript knowledge
|
|
3
|
-
|
|
4
|
-
## Learning Goals
|
|
5
|
-
|
|
6
|
-
- Master multi-input/multi-output transaction construction
|
|
7
|
-
- Implement advanced fee calculation and optimization strategies
|
|
8
|
-
- Handle change outputs efficiently
|
|
9
|
-
- Use advanced `WalletClient` options for transaction control
|
|
10
|
-
- Implement UTXO selection and management strategies
|
|
11
|
-
- Handle complex transaction scenarios and error recovery
|
|
12
|
-
|
|
13
|
-
## Introduction
|
|
14
|
-
|
|
15
|
-
This tutorial builds on your knowledge of basic `WalletClient` usage to explore advanced transaction construction patterns. You'll learn how to create sophisticated transactions that handle multiple inputs and outputs, optimize fees, and manage UTXOs effectively for production applications.
|
|
16
|
-
|
|
17
|
-
## Prerequisites
|
|
18
|
-
|
|
19
|
-
- Complete the [Transaction Types and Data](./transaction-types.md) tutorial
|
|
20
|
-
- Have a BRC-100 compliant wallet (such as the [MetaNet Desktop Wallet](https://desktop.bsvb.tech/)) installed and configured
|
|
21
|
-
- Some BSV in your wallet
|
|
22
|
-
- Understanding of Bitcoin transaction fundamentals
|
|
23
|
-
|
|
24
|
-
## Advanced `WalletClient` Options
|
|
25
|
-
|
|
26
|
-
The `createAction` method supports many advanced options through the `options` parameter. Let's explore these capabilities:
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
import { WalletClient } from '@bsv/sdk'
|
|
30
|
-
|
|
31
|
-
async function exploreAdvancedOptions() {
|
|
32
|
-
try {
|
|
33
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
34
|
-
|
|
35
|
-
// Authenticate with the wallet
|
|
36
|
-
const { authenticated } = await wallet.isAuthenticated()
|
|
37
|
-
if (!authenticated) {
|
|
38
|
-
await wallet.waitForAuthentication()
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Create a transaction with advanced options
|
|
42
|
-
const actionResult = await wallet.createAction({
|
|
43
|
-
description: 'Advanced options demonstration',
|
|
44
|
-
outputs: [
|
|
45
|
-
{
|
|
46
|
-
satoshis: 100,
|
|
47
|
-
lockingScript: '006a0e416476616e636564206f7074696f6e73', // OP_RETURN "Advanced options"
|
|
48
|
-
outputDescription: 'Advanced options demo'
|
|
49
|
-
}
|
|
50
|
-
],
|
|
51
|
-
options: {
|
|
52
|
-
// Don't automatically sign and process - gives you more control
|
|
53
|
-
signAndProcess: false,
|
|
54
|
-
|
|
55
|
-
// Accept delayed broadcast for better fee optimization
|
|
56
|
-
acceptDelayedBroadcast: true,
|
|
57
|
-
|
|
58
|
-
// Return only the transaction ID to save bandwidth
|
|
59
|
-
returnTXIDOnly: false,
|
|
60
|
-
|
|
61
|
-
// Don't send the transaction immediately
|
|
62
|
-
noSend: true,
|
|
63
|
-
|
|
64
|
-
// Randomize output order for privacy
|
|
65
|
-
randomizeOutputs: true
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
console.log('Transaction created with advanced options:')
|
|
70
|
-
console.log('Signable transaction available for manual processing')
|
|
71
|
-
|
|
72
|
-
if (actionResult.signableTransaction) {
|
|
73
|
-
console.log('Transaction reference:', actionResult.signableTransaction.reference)
|
|
74
|
-
console.log('Transaction ready for signing')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
} catch (error: any) {
|
|
78
|
-
console.error('Error:', error)
|
|
79
|
-
|
|
80
|
-
// Provide helpful troubleshooting
|
|
81
|
-
if (error.message?.includes('unlockingScript')) {
|
|
82
|
-
console.log('\nTroubleshooting: When specifying custom inputs, you must provide:')
|
|
83
|
-
console.log('- unlockingScript (valid hexadecimal string, not empty)')
|
|
84
|
-
console.log('- unlockingScriptLength (typically 107 for P2PKH)')
|
|
85
|
-
console.log('\nRecommendation: Let the wallet auto-select inputs by omitting the inputs array')
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
exploreAdvancedOptions().catch(console.error)
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Multi-Input Transaction Construction
|
|
94
|
-
|
|
95
|
-
Multi-input transactions combine multiple UTXOs to fund larger outputs. The BSV TypeScript SDK provides two approaches:
|
|
96
|
-
|
|
97
|
-
1. **Automatic Input Selection (Recommended)**: Let the wallet automatically select UTXOs by creating outputs that require more satoshis than any single UTXO can provide.
|
|
98
|
-
|
|
99
|
-
2. **Manual Input Specification (Advanced)**: Explicitly specify which UTXOs to use as inputs. This requires providing complete unlocking script information and is typically only needed for specialized use cases.
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import { WalletClient } from '@bsv/sdk'
|
|
103
|
-
|
|
104
|
-
async function createMultiInputTransaction() {
|
|
105
|
-
try {
|
|
106
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
107
|
-
|
|
108
|
-
const { authenticated } = await wallet.isAuthenticated()
|
|
109
|
-
if (!authenticated) {
|
|
110
|
-
await wallet.waitForAuthentication()
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// First, let's see what UTXOs we have available (basket name is 'bsv-tutorial' for consistency with previous tutorials, where we created some outputs)
|
|
114
|
-
const { outputs } = await wallet.listOutputs({
|
|
115
|
-
basket: 'bsv-tutorial',
|
|
116
|
-
include: 'locking scripts',
|
|
117
|
-
limit: 10
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
console.log(`Found ${outputs.length} available outputs:`)
|
|
121
|
-
outputs.forEach((output, index) => {
|
|
122
|
-
console.log(` ${index + 1}. ${output.outpoint}: ${output.satoshis} satoshis (spendable: ${output.spendable})`)
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
// Check if we have enough UTXOs for a multi-input transaction
|
|
126
|
-
const spendableOutputs = outputs.filter(output => output.spendable && output.satoshis >= 100)
|
|
127
|
-
|
|
128
|
-
if (spendableOutputs.length < 2) {
|
|
129
|
-
console.log('Need at least 2 spendable outputs for this demo')
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Method 1: Automatic Input Selection (Recommended)
|
|
134
|
-
|
|
135
|
-
// Create a transaction that requires multiple inputs by using a large output amount
|
|
136
|
-
// The wallet will automatically select multiple UTXOs if needed
|
|
137
|
-
const totalAvailable = spendableOutputs.reduce((sum, output) => sum + output.satoshis, 0)
|
|
138
|
-
const largeAmount = Math.min(1500, Math.floor(totalAvailable * 0.8)) // Use 80% of available funds
|
|
139
|
-
|
|
140
|
-
console.log(`Creating transaction requiring ${largeAmount} satoshis (should trigger multi-input selection)`)
|
|
141
|
-
|
|
142
|
-
const actionResult = await wallet.createAction({
|
|
143
|
-
description: 'Multi-input transaction example',
|
|
144
|
-
outputs: [
|
|
145
|
-
{
|
|
146
|
-
satoshis: largeAmount,
|
|
147
|
-
lockingScript: '006a0c4d756c74692d696e707574', // OP_RETURN "Multi-input"
|
|
148
|
-
outputDescription: 'Multi-input demo output'
|
|
149
|
-
}
|
|
150
|
-
]
|
|
151
|
-
// No inputs specified - let wallet auto-select
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
console.log('Transaction created successfully!')
|
|
155
|
-
console.log('Transaction ID:', actionResult.txid)
|
|
156
|
-
console.log(`View on explorer: https://whatsonchain.com/tx/${actionResult.txid}`)
|
|
157
|
-
|
|
158
|
-
// Method 2: Manual Input Specification (Advanced)
|
|
159
|
-
// Manual input specification requires unlocking script details
|
|
160
|
-
// This is commented out as it requires proper unlocking script construction
|
|
161
|
-
/*
|
|
162
|
-
const manualActionResult = await wallet.createAction({
|
|
163
|
-
description: 'Manual multi-input transaction',
|
|
164
|
-
inputs: [
|
|
165
|
-
{
|
|
166
|
-
outpoint: spendableOutputs[0].outpoint,
|
|
167
|
-
unlockingScript: '00', // Placeholder - would need actual unlocking script
|
|
168
|
-
unlockingScriptLength: 107, // Typical P2PKH unlocking script length
|
|
169
|
-
inputDescription: 'First manually selected input'
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
outpoint: spendableOutputs[1].outpoint,
|
|
173
|
-
unlockingScript: '00', // Placeholder - would need actual unlocking script
|
|
174
|
-
unlockingScriptLength: 107, // Typical P2PKH unlocking script length
|
|
175
|
-
inputDescription: 'Second manually selected input'
|
|
176
|
-
}
|
|
177
|
-
],
|
|
178
|
-
outputs: [
|
|
179
|
-
{
|
|
180
|
-
satoshis: 150,
|
|
181
|
-
lockingScript: '006a104d616e75616c2d6d756c74692d696e707574', // OP_RETURN "Manual-multi-input"
|
|
182
|
-
outputDescription: 'Manual multi-input demo output'
|
|
183
|
-
}
|
|
184
|
-
]
|
|
185
|
-
})
|
|
186
|
-
*/
|
|
187
|
-
|
|
188
|
-
} catch (error: any) {
|
|
189
|
-
console.error('Error:', error)
|
|
190
|
-
|
|
191
|
-
// Provide helpful troubleshooting
|
|
192
|
-
if (error.message?.includes('unlockingScript')) {
|
|
193
|
-
console.log('\nTroubleshooting: When specifying custom inputs, you must provide:')
|
|
194
|
-
console.log('- unlockingScript (valid hexadecimal string, not empty)')
|
|
195
|
-
console.log('- unlockingScriptLength (typically 107 for P2PKH)')
|
|
196
|
-
console.log('\nRecommendation: Let the wallet auto-select inputs by omitting the inputs array')
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
createMultiInputTransaction().catch(console.error)
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## Batch Processing Multiple Payments
|
|
205
|
-
|
|
206
|
-
For businesses and applications that need to send multiple payments efficiently, batch processing reduces fees and blockchain footprint:
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
import { WalletClient } from '@bsv/sdk'
|
|
210
|
-
|
|
211
|
-
async function createBatchPayment() {
|
|
212
|
-
try {
|
|
213
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
214
|
-
|
|
215
|
-
const { authenticated } = await wallet.isAuthenticated()
|
|
216
|
-
if (!authenticated) {
|
|
217
|
-
await wallet.waitForAuthentication()
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Simulate multiple payment recipients
|
|
221
|
-
const payments = [
|
|
222
|
-
{ amount: 100, description: 'Payment to Alice', data: 'Invoice #001' },
|
|
223
|
-
{ amount: 150, description: 'Payment to Bob', data: 'Invoice #002' },
|
|
224
|
-
{ amount: 200, description: 'Payment to Carol', data: 'Invoice #003' }
|
|
225
|
-
]
|
|
226
|
-
|
|
227
|
-
// Create outputs for each payment
|
|
228
|
-
const outputs = payments.map((payment, index) => {
|
|
229
|
-
// Create OP_RETURN with payment data
|
|
230
|
-
const dataHex = Buffer.from(payment.data).toString('hex')
|
|
231
|
-
const dataLength = Buffer.from(payment.data).length
|
|
232
|
-
const dataLengthHex = dataLength.toString(16).padStart(2, '0')
|
|
233
|
-
const lockingScript = `006a${dataLengthHex}${dataHex}`
|
|
234
|
-
|
|
235
|
-
return {
|
|
236
|
-
satoshis: payment.amount,
|
|
237
|
-
lockingScript: lockingScript,
|
|
238
|
-
outputDescription: payment.description,
|
|
239
|
-
basket: 'payments', // Organize outputs in a specific basket
|
|
240
|
-
tags: ['batch-payment', `invoice-${index + 1}`] // Tag for easy tracking
|
|
241
|
-
}
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
// Create the batch transaction
|
|
245
|
-
const actionResult = await wallet.createAction({
|
|
246
|
-
description: 'Batch payment transaction',
|
|
247
|
-
outputs: outputs,
|
|
248
|
-
labels: ['batch-processing', 'business-payments'], // Labels for transaction tracking
|
|
249
|
-
options: {
|
|
250
|
-
randomizeOutputs: false // Keep payment order for accounting
|
|
251
|
-
}
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
if (actionResult.txid) {
|
|
255
|
-
console.log(`Batch payment transaction created: ${actionResult.txid}`)
|
|
256
|
-
console.log(`Processed ${payments.length} payments in a single transaction`)
|
|
257
|
-
console.log(`Total amount: ${payments.reduce((sum, p) => sum + p.amount, 0)} satoshis`)
|
|
258
|
-
console.log(`View on explorer: https://whatsonchain.com/tx/${actionResult.txid}`)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
} catch (error: unknown) {
|
|
262
|
-
console.error('Error:', error)
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
createBatchPayment().catch(console.error)
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## Advanced UTXO Management with Baskets
|
|
270
|
-
|
|
271
|
-
Baskets provide powerful UTXO organization capabilities. Here's how to use them for advanced transaction patterns:
|
|
272
|
-
|
|
273
|
-
```typescript
|
|
274
|
-
import { WalletClient } from '@bsv/sdk'
|
|
275
|
-
|
|
276
|
-
async function advancedBasketManagement() {
|
|
277
|
-
try {
|
|
278
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
279
|
-
|
|
280
|
-
const { authenticated } = await wallet.isAuthenticated()
|
|
281
|
-
if (!authenticated) {
|
|
282
|
-
await wallet.waitForAuthentication()
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Create outputs in different baskets for different purposes
|
|
286
|
-
console.log('Creating specialized UTXO baskets...')
|
|
287
|
-
|
|
288
|
-
const actionResult = await wallet.createAction({
|
|
289
|
-
description: 'UTXO organization with baskets',
|
|
290
|
-
outputs: [
|
|
291
|
-
{
|
|
292
|
-
satoshis: 500,
|
|
293
|
-
lockingScript: '006a0c48696768207072696f72697479', // OP_RETURN "High priority"
|
|
294
|
-
outputDescription: 'High priority reserve',
|
|
295
|
-
basket: 'high-priority',
|
|
296
|
-
tags: ['reserve', 'urgent-use'],
|
|
297
|
-
customInstructions: 'Reserved for urgent transactions'
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
satoshis: 300,
|
|
301
|
-
lockingScript: '006a0d4d656469756d207072696f72697479', // OP_RETURN "Medium priority"
|
|
302
|
-
outputDescription: 'Medium priority operations',
|
|
303
|
-
basket: 'medium-priority',
|
|
304
|
-
tags: ['operations', 'daily-use']
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
satoshis: 200,
|
|
308
|
-
lockingScript: '006a0b4c6f77207072696f72697479', // OP_RETURN "Low priority"
|
|
309
|
-
outputDescription: 'Low priority batch',
|
|
310
|
-
basket: 'low-priority',
|
|
311
|
-
tags: ['batch', 'bulk-operations']
|
|
312
|
-
}
|
|
313
|
-
]
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
if (actionResult.txid) {
|
|
317
|
-
console.log(`Basket organization transaction: ${actionResult.txid}`)
|
|
318
|
-
console.log('Note: OP_RETURN outputs are unspendable by design (for data storage)')
|
|
319
|
-
console.log('Waiting for wallet to process new outputs...')
|
|
320
|
-
// Give the wallet a moment to process the new outputs
|
|
321
|
-
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Now let's examine our organized UTXOs
|
|
325
|
-
console.log('\nExamining basket organization...')
|
|
326
|
-
console.log('Note: OP_RETURN outputs show spendable: false because they are data-only outputs')
|
|
327
|
-
const baskets = ['high-priority', 'medium-priority', 'low-priority']
|
|
328
|
-
|
|
329
|
-
for (const basketName of baskets) {
|
|
330
|
-
const { outputs, totalOutputs } = await wallet.listOutputs({
|
|
331
|
-
basket: basketName,
|
|
332
|
-
includeTags: true,
|
|
333
|
-
includeCustomInstructions: true,
|
|
334
|
-
limit: 10
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
console.log(`\n${basketName.toUpperCase()} Basket:`)
|
|
338
|
-
console.log(` Total outputs: ${totalOutputs}`)
|
|
339
|
-
|
|
340
|
-
if (totalOutputs === 0) {
|
|
341
|
-
console.log(` Note: No outputs found. This could be because:`)
|
|
342
|
-
console.log(` - Outputs need confirmation time`)
|
|
343
|
-
console.log(` - Wallet needs time to process new baskets`)
|
|
344
|
-
console.log(` - OP_RETURN outputs might not be tracked as spendable UTXOs`)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
outputs.forEach((output, index) => {
|
|
348
|
-
console.log(` Output ${index + 1}:`)
|
|
349
|
-
console.log(` Outpoint: ${output.outpoint}`)
|
|
350
|
-
console.log(` Amount: ${output.satoshis} satoshis`)
|
|
351
|
-
console.log(` Spendable: ${output.spendable}`)
|
|
352
|
-
if (output.tags) {
|
|
353
|
-
console.log(` Tags: ${output.tags.join(', ')}`)
|
|
354
|
-
}
|
|
355
|
-
if (output.customInstructions) {
|
|
356
|
-
console.log(` Instructions: ${output.customInstructions}`)
|
|
357
|
-
}
|
|
358
|
-
})
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Add processing delay
|
|
362
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
363
|
-
|
|
364
|
-
} catch (error: unknown) {
|
|
365
|
-
console.error('Error:', error)
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
advancedBasketManagement().catch(console.error)
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
## Transaction Chaining and Dependencies
|
|
373
|
-
|
|
374
|
-
Sometimes you need to create transactions that depend on previous ones. Here's how to handle transaction chaining:
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
import { WalletClient } from '@bsv/sdk'
|
|
378
|
-
|
|
379
|
-
async function createTransactionChain() {
|
|
380
|
-
try {
|
|
381
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
382
|
-
|
|
383
|
-
const { authenticated } = await wallet.isAuthenticated()
|
|
384
|
-
if (!authenticated) {
|
|
385
|
-
await wallet.waitForAuthentication()
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
console.log('Creating transaction chain...')
|
|
389
|
-
console.log('Transaction chaining allows you to create a series of transactions that depend on each other.')
|
|
390
|
-
console.log('This is useful for scenarios where you need to ensure that a transaction is only processed after a previous one has been confirmed.')
|
|
391
|
-
console.log('In this example, we will create two transactions: the first one creates an output, and the second one spends that output.')
|
|
392
|
-
|
|
393
|
-
// First transaction - creates outputs for the chain
|
|
394
|
-
const firstTx = await wallet.createAction({
|
|
395
|
-
description: 'Chain starter transaction',
|
|
396
|
-
outputs: [
|
|
397
|
-
{
|
|
398
|
-
satoshis: 400,
|
|
399
|
-
lockingScript: '006a0d436861696e207374617274657220', // OP_RETURN "Chain starter"
|
|
400
|
-
outputDescription: 'Chain starter output',
|
|
401
|
-
basket: 'chain-demo',
|
|
402
|
-
tags: ['chain', 'step-1']
|
|
403
|
-
}
|
|
404
|
-
],
|
|
405
|
-
options: {
|
|
406
|
-
acceptDelayedBroadcast: true // Allow some delay for better fee optimization
|
|
407
|
-
}
|
|
408
|
-
})
|
|
409
|
-
|
|
410
|
-
if (!firstTx.txid) {
|
|
411
|
-
console.log('First transaction failed')
|
|
412
|
-
return
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
console.log(`First transaction: ${firstTx.txid}`)
|
|
416
|
-
|
|
417
|
-
// Wait a moment for the transaction to be processed
|
|
418
|
-
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
419
|
-
|
|
420
|
-
// Second transaction - demonstrates chaining with sendWith option
|
|
421
|
-
// Note: We use automatic input selection rather than manual specification
|
|
422
|
-
// Manual input specification requires unlockingScript and unlockingScriptLength
|
|
423
|
-
// which adds complexity for tutorial purposes
|
|
424
|
-
const secondTx = await wallet.createAction({
|
|
425
|
-
description: 'Chain continuation transaction',
|
|
426
|
-
outputs: [
|
|
427
|
-
{
|
|
428
|
-
satoshis: 150,
|
|
429
|
-
lockingScript: '006a0c436861696e20636f6e74696e756174696f6e', // OP_RETURN "Chain continuation"
|
|
430
|
-
outputDescription: 'Chain continuation output',
|
|
431
|
-
basket: 'chain-demo',
|
|
432
|
-
tags: ['chain', 'step-2']
|
|
433
|
-
}
|
|
434
|
-
],
|
|
435
|
-
options: {
|
|
436
|
-
sendWith: [firstTx.txid] // Ensure this transaction is sent with the first one
|
|
437
|
-
}
|
|
438
|
-
})
|
|
439
|
-
|
|
440
|
-
if (secondTx.txid) {
|
|
441
|
-
console.log(`Second transaction: ${secondTx.txid}`)
|
|
442
|
-
console.log('Transaction chain completed successfully')
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
} catch (error: unknown) {
|
|
446
|
-
console.error('Error in transaction chain:', error)
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
createTransactionChain().catch(console.error)
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
## Performance Optimization Strategies
|
|
454
|
-
|
|
455
|
-
For high-throughput applications, consider these optimization techniques:
|
|
456
|
-
|
|
457
|
-
```typescript
|
|
458
|
-
import { WalletClient } from '@bsv/sdk'
|
|
459
|
-
|
|
460
|
-
async function optimizedTransactionPatterns() {
|
|
461
|
-
try {
|
|
462
|
-
const wallet = new WalletClient('auto', 'localhost')
|
|
463
|
-
|
|
464
|
-
const { authenticated } = await wallet.isAuthenticated()
|
|
465
|
-
if (!authenticated) {
|
|
466
|
-
await wallet.waitForAuthentication()
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// Strategy 1: Pre-allocate UTXOs for different purposes
|
|
470
|
-
console.log('Pre-allocating UTXOs for optimal performance...')
|
|
471
|
-
|
|
472
|
-
const allocationTx = await wallet.createAction({
|
|
473
|
-
description: 'UTXO pre-allocation for performance',
|
|
474
|
-
outputs: [
|
|
475
|
-
// Create multiple small UTXOs for frequent operations
|
|
476
|
-
...Array(5).fill(null).map((_, i) => ({
|
|
477
|
-
satoshis: 200,
|
|
478
|
-
lockingScript: `006a0f507265616c6c6f636174656420${i.toString(16).padStart(2, '0')}`, // "Preallocated" + index
|
|
479
|
-
outputDescription: `Pre-allocated UTXO ${i + 1}`,
|
|
480
|
-
basket: 'pre-allocated',
|
|
481
|
-
tags: ['performance', 'ready-to-use']
|
|
482
|
-
}))
|
|
483
|
-
],
|
|
484
|
-
options: {
|
|
485
|
-
acceptDelayedBroadcast: true
|
|
486
|
-
}
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
if (allocationTx.txid) {
|
|
490
|
-
console.log(`Pre-allocation transaction: ${allocationTx.txid}`)
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Strategy 2: Batch multiple operations efficiently
|
|
494
|
-
await new Promise(resolve => setTimeout(resolve, 3000))
|
|
495
|
-
|
|
496
|
-
const operations = [
|
|
497
|
-
{ type: 'data', content: 'Operation 1' },
|
|
498
|
-
{ type: 'data', content: 'Operation 2' },
|
|
499
|
-
{ type: 'data', content: 'Operation 3' }
|
|
500
|
-
]
|
|
501
|
-
|
|
502
|
-
const batchOutputs = operations.map((op, index) => {
|
|
503
|
-
const dataHex = Buffer.from(op.content).toString('hex')
|
|
504
|
-
const dataLength = Buffer.from(op.content).length
|
|
505
|
-
const dataLengthHex = dataLength.toString(16).padStart(2, '0')
|
|
506
|
-
|
|
507
|
-
return {
|
|
508
|
-
satoshis: 100,
|
|
509
|
-
lockingScript: `006a${dataLengthHex}${dataHex}`,
|
|
510
|
-
outputDescription: `Batch operation ${index + 1}`,
|
|
511
|
-
basket: 'operations',
|
|
512
|
-
tags: ['batch', `op-${index + 1}`]
|
|
513
|
-
}
|
|
514
|
-
})
|
|
515
|
-
|
|
516
|
-
// Use pre-allocated UTXOs for the batch
|
|
517
|
-
const { outputs: preAllocated } = await wallet.listOutputs({
|
|
518
|
-
basket: 'pre-allocated',
|
|
519
|
-
limit: 3
|
|
520
|
-
})
|
|
521
|
-
|
|
522
|
-
console.log(`Found ${preAllocated.length} pre-allocated UTXOs`)
|
|
523
|
-
|
|
524
|
-
if (preAllocated.length > 0) {
|
|
525
|
-
// Let the wallet automatically select inputs instead of manual specification
|
|
526
|
-
const batchTx = await wallet.createAction({
|
|
527
|
-
description: 'Optimized batch operation',
|
|
528
|
-
outputs: batchOutputs,
|
|
529
|
-
options: {
|
|
530
|
-
randomizeOutputs: false, // Maintain order for processing
|
|
531
|
-
acceptDelayedBroadcast: true
|
|
532
|
-
}
|
|
533
|
-
})
|
|
534
|
-
|
|
535
|
-
if (batchTx.txid) {
|
|
536
|
-
console.log(`Optimized batch transaction: ${batchTx.txid}`)
|
|
537
|
-
console.log(`Processed ${operations.length} operations efficiently`)
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
} catch (error: unknown) {
|
|
542
|
-
console.error('Optimization error:', error)
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
optimizedTransactionPatterns().catch(console.error)
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
## Conclusion
|
|
550
|
-
|
|
551
|
-
You've now mastered advanced transaction construction with the BSV TypeScript SDK's `WalletClient`. You can:
|
|
552
|
-
|
|
553
|
-
- Create sophisticated multi-input/multi-output transactions
|
|
554
|
-
- Implement robust error handling and retry strategies
|
|
555
|
-
- Optimize transaction patterns for performance
|
|
556
|
-
- Use advanced `WalletClient` features like baskets, tags, and options
|
|
557
|
-
- Handle complex scenarios like transaction chaining and batch processing
|
|
558
|
-
|
|
559
|
-
These techniques enable you to build production-ready applications that efficiently manage Bitcoin transactions while maintaining reliability and performance.
|
|
560
|
-
|
|
561
|
-
## Next Steps
|
|
562
|
-
|
|
563
|
-
- Review the [Transaction Signing Methods](../guides/transaction-signing-methods.md) for custom signing scenarios
|
|
564
|
-
- Check out specialized tutorials for your specific use case
|
|
565
|
-
|
|
566
|
-
- Advanced transaction construction requires robust error handling for production applications. For comprehensive coverage of error handling patterns, retry mechanisms, and recovery strategies, see the dedicated [Error Handling Tutorial](./error-handling.md).
|
|
567
|
-
|
|
568
|
-
## Additional Resources
|
|
569
|
-
|
|
570
|
-
- [Wallet Reference](../reference/wallet.md)
|
|
571
|
-
- [BSV Blockchain Documentation](https://docs.bsvblockchain.org/)
|
|
572
|
-
- [MetaNet Desktop Wallet](https://desktop.bsvb.tech/)
|