@bsv/sdk 1.9.3 → 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/docs/fast-docs.png +0 -0
- package/docs/index.md +49 -44
- package/docs/swagger.png +0 -0
- package/package.json +1 -1
- 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,374 +0,0 @@
|
|
|
1
|
-
# Setting up Development Wallets
|
|
2
|
-
|
|
3
|
-
Learn how to set up and configure ProtoWallet for development, testing, and prototyping scenarios.
|
|
4
|
-
|
|
5
|
-
## Problem
|
|
6
|
-
|
|
7
|
-
You need a lightweight wallet solution for development and testing that doesn't require full blockchain integration but provides all necessary cryptographic operations.
|
|
8
|
-
|
|
9
|
-
## Solution
|
|
10
|
-
|
|
11
|
-
Use ProtoWallet for development environments with proper key management, signing capabilities, and testing workflows.
|
|
12
|
-
|
|
13
|
-
### Basic Development Wallet Setup
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { ProtoWallet, PrivateKey } from '@bsv/sdk'
|
|
17
|
-
|
|
18
|
-
class DevelopmentWalletManager {
|
|
19
|
-
private wallets: Map<string, ProtoWallet> = new Map()
|
|
20
|
-
private walletKeys: Map<string, PrivateKey> = new Map()
|
|
21
|
-
|
|
22
|
-
async createWallet(name: string, privateKey?: PrivateKey): Promise<ProtoWallet> {
|
|
23
|
-
const key = privateKey || PrivateKey.fromRandom()
|
|
24
|
-
const wallet = new ProtoWallet(key)
|
|
25
|
-
|
|
26
|
-
this.wallets.set(name, wallet)
|
|
27
|
-
this.walletKeys.set(name, key)
|
|
28
|
-
|
|
29
|
-
// Get identity public key for display
|
|
30
|
-
const { publicKey } = await wallet.getPublicKey({ identityKey: true })
|
|
31
|
-
console.log(`Created wallet "${name}" with public key: ${publicKey}`)
|
|
32
|
-
|
|
33
|
-
return wallet
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
getWallet(name: string): ProtoWallet | undefined {
|
|
37
|
-
return this.wallets.get(name)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async listWallets(): Promise<Array<{ name: string; publicKey: string }>> {
|
|
41
|
-
const walletList = []
|
|
42
|
-
for (const [name, wallet] of this.wallets.entries()) {
|
|
43
|
-
const { publicKey } = await wallet.getPublicKey({ identityKey: true })
|
|
44
|
-
walletList.push({ name, publicKey })
|
|
45
|
-
}
|
|
46
|
-
return walletList
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
exportWallet(name: string): string | null {
|
|
50
|
-
const privateKey = this.walletKeys.get(name)
|
|
51
|
-
if (!privateKey) return null
|
|
52
|
-
|
|
53
|
-
return privateKey.toString()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async importWallet(name: string, privateKeyString: string): Promise<ProtoWallet> {
|
|
57
|
-
const privateKey = PrivateKey.fromString(privateKeyString)
|
|
58
|
-
return await this.createWallet(name, privateKey)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### Testing Wallet with Mock Transactions
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
import { ProtoWallet, PrivateKey, P2PKH } from '@bsv/sdk'
|
|
67
|
-
|
|
68
|
-
class TestingWallet {
|
|
69
|
-
private wallet: ProtoWallet
|
|
70
|
-
private privateKey: PrivateKey
|
|
71
|
-
private mockUTXOs: Array<{
|
|
72
|
-
txid: string
|
|
73
|
-
vout: number
|
|
74
|
-
satoshis: number
|
|
75
|
-
script: string
|
|
76
|
-
}> = []
|
|
77
|
-
|
|
78
|
-
constructor(privateKey?: PrivateKey) {
|
|
79
|
-
this.privateKey = privateKey || PrivateKey.fromRandom()
|
|
80
|
-
this.wallet = new ProtoWallet(this.privateKey)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Add mock UTXOs for testing
|
|
84
|
-
async addMockUTXO(satoshis: number): Promise<void> {
|
|
85
|
-
const mockTxid = Array.from(crypto.getRandomValues(new Uint8Array(32)))
|
|
86
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
87
|
-
.join('')
|
|
88
|
-
|
|
89
|
-
// Create a simple P2PKH locking script using a mock address
|
|
90
|
-
// In a real implementation, you'd derive the proper address from the public key
|
|
91
|
-
const mockAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' // Example Bitcoin address
|
|
92
|
-
const p2pkh = new P2PKH()
|
|
93
|
-
const lockingScript = p2pkh.lock(mockAddress)
|
|
94
|
-
|
|
95
|
-
this.mockUTXOs.push({
|
|
96
|
-
txid: mockTxid,
|
|
97
|
-
vout: 0,
|
|
98
|
-
satoshis,
|
|
99
|
-
script: lockingScript.toHex()
|
|
100
|
-
})
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
getMockBalance(): number {
|
|
104
|
-
return this.mockUTXOs.reduce((sum, utxo) => sum + utxo.satoshis, 0)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async createMockTransaction(
|
|
108
|
-
recipientPublicKey: string,
|
|
109
|
-
amount: number
|
|
110
|
-
): Promise<string> {
|
|
111
|
-
if (this.getMockBalance() < amount + 100) { // 100 sat fee
|
|
112
|
-
throw new Error('Insufficient mock balance')
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// For this demo, we'll create a simple transaction representation
|
|
116
|
-
// In a real implementation, you'd use the full Transaction class
|
|
117
|
-
let inputAmount = 0
|
|
118
|
-
const usedUTXOs: number[] = []
|
|
119
|
-
|
|
120
|
-
for (let i = 0; i < this.mockUTXOs.length && inputAmount < amount + 100; i++) {
|
|
121
|
-
const utxo = this.mockUTXOs[i]
|
|
122
|
-
inputAmount += utxo.satoshis
|
|
123
|
-
usedUTXOs.push(i)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Calculate change
|
|
127
|
-
const change = inputAmount - amount - 100
|
|
128
|
-
|
|
129
|
-
// Create transaction summary
|
|
130
|
-
const txSummary = {
|
|
131
|
-
inputs: usedUTXOs.length,
|
|
132
|
-
outputs: change > 0 ? 2 : 1,
|
|
133
|
-
amount,
|
|
134
|
-
change,
|
|
135
|
-
fee: 100,
|
|
136
|
-
recipient: recipientPublicKey
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Remove used UTXOs
|
|
140
|
-
usedUTXOs.reverse().forEach(index => {
|
|
141
|
-
this.mockUTXOs.splice(index, 1)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
return JSON.stringify(txSummary, null, 2)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async getPublicKey(): Promise<string> {
|
|
148
|
-
const { publicKey } = await this.wallet.getPublicKey({ identityKey: true })
|
|
149
|
-
return publicKey
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Multi-Wallet Development Environment
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
import { ProtoWallet, PrivateKey } from '@bsv/sdk'
|
|
158
|
-
|
|
159
|
-
interface WalletConfig {
|
|
160
|
-
name: string
|
|
161
|
-
purpose: string
|
|
162
|
-
balance?: number
|
|
163
|
-
privateKey?: string
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
class DevelopmentEnvironment {
|
|
167
|
-
private wallets: Map<string, ProtoWallet> = new Map()
|
|
168
|
-
private walletConfigs: Map<string, WalletConfig> = new Map()
|
|
169
|
-
|
|
170
|
-
async setupEnvironment(configs: WalletConfig[]): Promise<void> {
|
|
171
|
-
console.log('Setting up development environment...')
|
|
172
|
-
|
|
173
|
-
for (const config of configs) {
|
|
174
|
-
const privateKey = config.privateKey
|
|
175
|
-
? PrivateKey.fromString(config.privateKey)
|
|
176
|
-
: PrivateKey.fromRandom()
|
|
177
|
-
|
|
178
|
-
const wallet = new ProtoWallet(privateKey)
|
|
179
|
-
|
|
180
|
-
this.wallets.set(config.name, wallet)
|
|
181
|
-
this.walletConfigs.set(config.name, config)
|
|
182
|
-
|
|
183
|
-
// Get identity public key for display
|
|
184
|
-
const { publicKey } = await wallet.getPublicKey({ identityKey: true })
|
|
185
|
-
|
|
186
|
-
console.log(`✓ Created ${config.name} wallet (${config.purpose})`)
|
|
187
|
-
console.log(` Public Key: ${publicKey}`)
|
|
188
|
-
|
|
189
|
-
if (config.balance) {
|
|
190
|
-
console.log(` Mock Balance: ${config.balance} satoshis`)
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
console.log('Development environment ready!')
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
getWallet(name: string): ProtoWallet | undefined {
|
|
198
|
-
return this.wallets.get(name)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async demonstrateSigningFlow(
|
|
202
|
-
signerName: string,
|
|
203
|
-
message: string
|
|
204
|
-
): Promise<void> {
|
|
205
|
-
const wallet = this.wallets.get(signerName)
|
|
206
|
-
if (!wallet) {
|
|
207
|
-
throw new Error(`Wallet ${signerName} not found`)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
console.log(`\n--- Signing Demo with ${signerName} ---`)
|
|
211
|
-
console.log(`Message: "${message}"`)
|
|
212
|
-
|
|
213
|
-
const messageBytes = new TextEncoder().encode(message)
|
|
214
|
-
|
|
215
|
-
// Create signature using ProtoWallet API
|
|
216
|
-
const { signature } = await wallet.createSignature({
|
|
217
|
-
data: Array.from(messageBytes),
|
|
218
|
-
protocolID: [1, 'demo signing'],
|
|
219
|
-
keyID: 'message-key',
|
|
220
|
-
counterparty: 'self'
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
console.log(`Signature created successfully`)
|
|
224
|
-
|
|
225
|
-
// Verify signature
|
|
226
|
-
try {
|
|
227
|
-
const { valid } = await wallet.verifySignature({
|
|
228
|
-
data: Array.from(messageBytes),
|
|
229
|
-
signature,
|
|
230
|
-
protocolID: [1, 'demo signing'],
|
|
231
|
-
keyID: 'message-key',
|
|
232
|
-
counterparty: 'self'
|
|
233
|
-
})
|
|
234
|
-
console.log(`Verification: ${valid ? '✓ Valid' : '✗ Invalid'}`)
|
|
235
|
-
} catch (error: any) {
|
|
236
|
-
console.log(`Verification: ✓ Valid (signature verification successful)`)
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
async demonstrateEncryption(
|
|
241
|
-
senderName: string,
|
|
242
|
-
recipientName: string,
|
|
243
|
-
message: string
|
|
244
|
-
): Promise<void> {
|
|
245
|
-
const sender = this.wallets.get(senderName)
|
|
246
|
-
const recipient = this.wallets.get(recipientName)
|
|
247
|
-
|
|
248
|
-
if (!sender || !recipient) {
|
|
249
|
-
throw new Error('Both wallets must exist for encryption demo')
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
console.log(`\n--- Encryption Demo: ${senderName} → ${recipientName} ---`)
|
|
253
|
-
console.log(`Original message: "${message}"`)
|
|
254
|
-
|
|
255
|
-
const messageBytes = new TextEncoder().encode(message)
|
|
256
|
-
|
|
257
|
-
// Get recipient's public key for encryption
|
|
258
|
-
const { publicKey: recipientPubKey } = await recipient.getPublicKey({ identityKey: true })
|
|
259
|
-
|
|
260
|
-
// Encrypt using ProtoWallet API
|
|
261
|
-
const { ciphertext } = await sender.encrypt({
|
|
262
|
-
plaintext: Array.from(messageBytes),
|
|
263
|
-
protocolID: [1, 'demo encryption'],
|
|
264
|
-
keyID: 'message-key',
|
|
265
|
-
counterparty: recipientPubKey
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
console.log(`Encrypted successfully`)
|
|
269
|
-
|
|
270
|
-
// Get sender's public key for decryption
|
|
271
|
-
const { publicKey: senderPubKey } = await sender.getPublicKey({ identityKey: true })
|
|
272
|
-
|
|
273
|
-
// Decrypt
|
|
274
|
-
const { plaintext } = await recipient.decrypt({
|
|
275
|
-
ciphertext,
|
|
276
|
-
protocolID: [1, 'demo encryption'],
|
|
277
|
-
keyID: 'message-key',
|
|
278
|
-
counterparty: senderPubKey
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
const decryptedMessage = new TextDecoder().decode(new Uint8Array(plaintext))
|
|
282
|
-
|
|
283
|
-
console.log(`Decrypted: "${decryptedMessage}"`)
|
|
284
|
-
console.log(`Match: ${message === decryptedMessage ? '✓ Success' : '✗ Failed'}`)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async exportEnvironment(): Promise<any> {
|
|
288
|
-
const exported: any = {}
|
|
289
|
-
|
|
290
|
-
for (const [name, wallet] of this.wallets) {
|
|
291
|
-
const config = this.walletConfigs.get(name)!
|
|
292
|
-
const { publicKey } = await wallet.getPublicKey({ identityKey: true })
|
|
293
|
-
|
|
294
|
-
exported[name] = {
|
|
295
|
-
...config,
|
|
296
|
-
publicKey
|
|
297
|
-
// Note: Private key export would require additional security measures in production
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
return exported
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
async saveEnvironment(filename: string): Promise<void> {
|
|
305
|
-
const exported = await this.exportEnvironment()
|
|
306
|
-
const json = JSON.stringify(exported, null, 2)
|
|
307
|
-
|
|
308
|
-
// In a real environment, you'd save to file
|
|
309
|
-
console.log(`Environment configuration:\n${json}`)
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Example usage
|
|
314
|
-
async function setupDevelopmentEnvironment() {
|
|
315
|
-
const env = new DevelopmentEnvironment()
|
|
316
|
-
|
|
317
|
-
await env.setupEnvironment([
|
|
318
|
-
{
|
|
319
|
-
name: 'alice',
|
|
320
|
-
purpose: 'Primary test user',
|
|
321
|
-
balance: 100000
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
name: 'bob',
|
|
325
|
-
purpose: 'Secondary test user',
|
|
326
|
-
balance: 50000
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
name: 'merchant',
|
|
330
|
-
purpose: 'Payment recipient',
|
|
331
|
-
balance: 10000
|
|
332
|
-
},
|
|
333
|
-
{
|
|
334
|
-
name: 'service',
|
|
335
|
-
purpose: 'API service wallet'
|
|
336
|
-
}
|
|
337
|
-
])
|
|
338
|
-
|
|
339
|
-
// Demonstrate functionality
|
|
340
|
-
await env.demonstrateSigningFlow('alice', 'Hello, BSV!')
|
|
341
|
-
await env.demonstrateEncryption('alice', 'bob', 'Secret message')
|
|
342
|
-
|
|
343
|
-
await env.saveEnvironment('dev-environment.json')
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
## Best Practices
|
|
349
|
-
|
|
350
|
-
1. **Use deterministic keys** for reproducible testing environments
|
|
351
|
-
2. **Implement proper key storage** for development wallets
|
|
352
|
-
3. **Create wallet profiles** for different testing scenarios
|
|
353
|
-
4. **Use mock UTXOs** for transaction testing without blockchain interaction
|
|
354
|
-
5. **Document wallet purposes** and configurations
|
|
355
|
-
|
|
356
|
-
## Common Issues
|
|
357
|
-
|
|
358
|
-
- **Key management**: Use secure storage even in development
|
|
359
|
-
- **Mock transaction validation**: Ensure realistic transaction structures
|
|
360
|
-
- **Environment consistency**: Use configuration files for reproducible setups
|
|
361
|
-
- **Testing isolation**: Separate development and production environments
|
|
362
|
-
|
|
363
|
-
## Security Considerations
|
|
364
|
-
|
|
365
|
-
- **Never use development keys** in production
|
|
366
|
-
- **Secure development environments** appropriately
|
|
367
|
-
- **Use separate networks** (testnet/regtest) for development
|
|
368
|
-
- **Implement proper cleanup** of development data
|
|
369
|
-
|
|
370
|
-
## Related
|
|
371
|
-
|
|
372
|
-
- [ProtoWallet Tutorial](../tutorials/protowallet-development.md)
|
|
373
|
-
- [Security Best Practices](./security-best-practices.md)
|
|
374
|
-
- [Transaction Construction](./transaction-construction.md)
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# Creating Transactions with Direct Interfaces
|
|
2
|
-
|
|
3
|
-
This guide demonstrates how to create Bitcoin SV transactions using the lower-level direct interfaces provided by the BSV TypeScript SDK. This approach gives you more control over transaction construction and is useful for specialized use cases where the `WalletClient` abstraction isn't suitable.
|
|
4
|
-
|
|
5
|
-
## When to Use Direct Interfaces
|
|
6
|
-
|
|
7
|
-
- When creating custom transaction types not supported by `WalletClient`
|
|
8
|
-
- When you need precise control over UTXO selection
|
|
9
|
-
- When building specialized applications like data storage services that require custom optimization
|
|
10
|
-
- When integrating with systems that require direct management of transactions
|
|
11
|
-
- For educational purposes to understand the underlying transaction structure
|
|
12
|
-
|
|
13
|
-
## Basic Transaction Construction
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { PrivateKey, P2PKH, Transaction } from '@bsv/sdk'
|
|
17
|
-
|
|
18
|
-
async function createBasicTransaction() {
|
|
19
|
-
// Create a private key
|
|
20
|
-
const privateKey = PrivateKey.fromRandom()
|
|
21
|
-
console.log(`Private key WIF: ${privateKey.toWif()}`)
|
|
22
|
-
|
|
23
|
-
// Derive the address
|
|
24
|
-
const address = privateKey.toAddress()
|
|
25
|
-
console.log(`Address: ${address.toString()}`)
|
|
26
|
-
|
|
27
|
-
// Create a new transaction
|
|
28
|
-
const tx = new Transaction()
|
|
29
|
-
|
|
30
|
-
// Add an output
|
|
31
|
-
tx.addOutput({
|
|
32
|
-
lockingScript: new P2PKH().lock(address),
|
|
33
|
-
satoshis: 1000
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
// Serialize the transaction
|
|
37
|
-
const txHex = tx.toHex()
|
|
38
|
-
console.log(`Transaction (hex): ${txHex}`)
|
|
39
|
-
|
|
40
|
-
// Get transaction ID as a hex string
|
|
41
|
-
const txid = Buffer.from(tx.id()).toString('hex')
|
|
42
|
-
console.log(`Transaction ID: ${txid}`)
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Complete Transaction with Inputs and Outputs
|
|
47
|
-
|
|
48
|
-
For a complete transaction that can be broadcast, you need to add inputs, calculate fees, and sign it:
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
import { Transaction, PrivateKey, P2PKH, ARC } from '@bsv/sdk'
|
|
52
|
-
|
|
53
|
-
async function createCompleteTransaction() {
|
|
54
|
-
// Set up your wallet
|
|
55
|
-
const privateKey = PrivateKey.fromWif('your_private_key_here')
|
|
56
|
-
const myAddress = privateKey.toAddress()
|
|
57
|
-
const recipientAddress = 'recipient_address_here'
|
|
58
|
-
|
|
59
|
-
// You need the hex of the source transaction
|
|
60
|
-
const sourceTxHex = '...' // Hex string of the source transaction
|
|
61
|
-
|
|
62
|
-
// Create a transaction
|
|
63
|
-
const tx = new Transaction()
|
|
64
|
-
|
|
65
|
-
// Add the input
|
|
66
|
-
tx.addInput({
|
|
67
|
-
sourceTransaction: Transaction.fromHex(sourceTxHex),
|
|
68
|
-
sourceOutputIndex: 0, // The output index you want to spend
|
|
69
|
-
unlockingScriptTemplate: new P2PKH().unlock(privateKey)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
// Add the recipient output
|
|
73
|
-
tx.addOutput({
|
|
74
|
-
lockingScript: new P2PKH().lock(recipientAddress),
|
|
75
|
-
satoshis: 100 // Amount to send
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
// Add the change output back to our address
|
|
79
|
-
tx.addOutput({
|
|
80
|
-
lockingScript: new P2PKH().lock(myAddress),
|
|
81
|
-
change: true // SDK will automatically calculate the change amount
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
// Calculate fee and sign the transaction
|
|
85
|
-
await tx.fee()
|
|
86
|
-
await tx.sign()
|
|
87
|
-
|
|
88
|
-
// Get the transaction hex ready for broadcasting
|
|
89
|
-
const signedTxHex = tx.toHex()
|
|
90
|
-
console.log(`Signed transaction hex: ${signedTxHex}`)
|
|
91
|
-
|
|
92
|
-
// Get the transaction ID
|
|
93
|
-
const txid = Buffer.from(tx.id()).toString('hex')
|
|
94
|
-
console.log(`Transaction ID: ${txid}`)
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Broadcasting the Transaction
|
|
99
|
-
|
|
100
|
-
To broadcast your signed transaction:
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
import { ARC, NodejsHttpClient } from '@bsv/sdk'
|
|
104
|
-
import https from 'https'
|
|
105
|
-
|
|
106
|
-
async function broadcastTransaction(signedTx) {
|
|
107
|
-
// Create an HTTP client
|
|
108
|
-
const httpClient = new NodejsHttpClient(https)
|
|
109
|
-
|
|
110
|
-
// Initialize the ARC client
|
|
111
|
-
const arc = new ARC('https://api.arc.taal.com', {
|
|
112
|
-
apiKey: 'your_api_key_here',
|
|
113
|
-
httpClient,
|
|
114
|
-
deploymentId: 'your-deployment-id'
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
// Broadcast the transaction
|
|
118
|
-
const result = await signedTx.broadcast(arc)
|
|
119
|
-
console.log('Broadcast result:', result)
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Key Implementation Details
|
|
124
|
-
|
|
125
|
-
When working with direct interfaces, remember these important details:
|
|
126
|
-
|
|
127
|
-
1. Use `toWif()` (lowercase 'f') not `toWIF()` for private key WIF format
|
|
128
|
-
2. Use `toHex()` instead of `toString()` for transaction serialization
|
|
129
|
-
3. Transaction IDs need to be converted from byte arrays: `Buffer.from(tx.id()).toString('hex')`
|
|
130
|
-
4. For script objects, use `toHex()` or `toASM()` rather than `toString()`
|
|
131
|
-
5. Method chaining doesn't work well with current API - use separate method calls
|
|
132
|
-
|
|
133
|
-
## Direct Creation vs `WalletClient` Approach
|
|
134
|
-
|
|
135
|
-
| Feature | Direct Creation | `WalletClient` |
|
|
136
|
-
| --- | --- | --- |
|
|
137
|
-
| Control over transaction structure | High | Low |
|
|
138
|
-
| Complexity | High | Low |
|
|
139
|
-
| Recommended use case | Specialized applications | Production applications |
|
|
140
|
-
|
|
141
|
-
This guide focuses on direct transaction creation using low-level APIs, which gives you complete control over every aspect of the transaction. For simpler applications, consider using the `WalletClient` approach covered in other tutorials.
|
|
142
|
-
|
|
143
|
-
## Related Resources
|
|
144
|
-
|
|
145
|
-
- For simpler implementations, see the [Creating Transactions with WalletClient](../tutorials/first-transaction.md) tutorial
|
|
146
|
-
- Learn about [Advanced Transaction Signing](./advanced-transaction-signing.md)
|
|
147
|
-
- Explore [HTTP Client Configuration](./http-client-configuration.md) for optimizing API requests
|