minimal-xec-wallet 1.0.1

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -0
  3. package/dist/minimal-xec-wallet.js +66268 -0
  4. package/dist/minimal-xec-wallet.min.js +55 -0
  5. package/examples/README.md +380 -0
  6. package/examples/advanced/browser-compatibility-test.js +263 -0
  7. package/examples/advanced/get-xec-price.js +149 -0
  8. package/examples/advanced/optimize-utxos.js +255 -0
  9. package/examples/advanced/send-op-return.js +216 -0
  10. package/examples/browser-test.html +350 -0
  11. package/examples/key-management/derive-addresses.js +191 -0
  12. package/examples/key-management/export-to-wif.js +114 -0
  13. package/examples/key-management/validate-address.js +214 -0
  14. package/examples/optimization/simple-consolidation-test.js +79 -0
  15. package/examples/optimization/test-utxo-consolidation.js +179 -0
  16. package/examples/test-examples.js +1204 -0
  17. package/examples/tokens/burn-tokens.js +293 -0
  18. package/examples/tokens/get-token-balance.js +169 -0
  19. package/examples/tokens/get-token-info.js +269 -0
  20. package/examples/tokens/list-all-tokens.js +162 -0
  21. package/examples/tokens/send-any-token.js +260 -0
  22. package/examples/tokens/test-main-wallet-integration.js +193 -0
  23. package/examples/transactions/send-all-xec.js +205 -0
  24. package/examples/transactions/send-to-multiple.js +217 -0
  25. package/examples/transactions/send-xec.js +191 -0
  26. package/examples/utils/show-qr.js +119 -0
  27. package/examples/utils/wallet-helper.js +176 -0
  28. package/examples/validation/comprehensive-infrastructure-test.js +210 -0
  29. package/examples/wallet-creation/create-new-wallet.js +67 -0
  30. package/examples/wallet-creation/import-from-wif.js +135 -0
  31. package/examples/wallet-creation/restore-from-mnemonic.js +100 -0
  32. package/examples/wallet-info/get-balance.js +99 -0
  33. package/examples/wallet-info/get-transactions.js +157 -0
  34. package/examples/wallet-info/get-utxos.js +145 -0
  35. package/examples/wallet.json +11 -0
  36. package/lib/adapters/robust-chronik-router.js +507 -0
  37. package/lib/adapters/router.js +651 -0
  38. package/lib/alp-token-handler.js +581 -0
  39. package/lib/browser-wasm-loader.js +271 -0
  40. package/lib/consolidate-utxos.js +338 -0
  41. package/lib/hybrid-token-manager.js +322 -0
  42. package/lib/key-derivation.js +466 -0
  43. package/lib/op-return.js +314 -0
  44. package/lib/security.js +270 -0
  45. package/lib/send-xec.js +396 -0
  46. package/lib/slp-token-handler.js +572 -0
  47. package/lib/token-protocol-detector.js +307 -0
  48. package/lib/utxos.js +303 -0
  49. package/package.json +125 -0
@@ -0,0 +1,193 @@
1
+ /*
2
+ Test Step 1 Main Wallet Integration with Real Tokens
3
+
4
+ This script validates that our Step 1 integration works correctly by:
5
+ 1. Using the main MinimalXECWallet API (not low-level libraries)
6
+ 2. Testing with real FLCT (SLP) and TGR (ALP) tokens
7
+ 3. Validating core token operations work through main wallet interface
8
+ */
9
+
10
+ const MinimalXECWallet = require('../../index')
11
+ const WalletHelper = require('../utils/wallet-helper')
12
+
13
+ async function testMainWalletIntegration () {
14
+ try {
15
+ console.log('๐Ÿงช Testing Step 1: Main Wallet Integration')
16
+ console.log('โ•'.repeat(60))
17
+ console.log('Testing with real FLCT (SLP) and TGR (ALP) tokens...\n')
18
+
19
+ // Load wallet data
20
+ const walletData = WalletHelper.loadWallet()
21
+ if (!walletData) {
22
+ console.log('โŒ No wallet found')
23
+ console.log(' Run: node examples/wallet-creation/create-new-wallet.js')
24
+ return
25
+ }
26
+
27
+ console.log('โœ… Wallet loaded:')
28
+ console.log(` Address: ${walletData.xecAddress}`)
29
+ console.log(` Using mnemonic: ${walletData.mnemonic ? 'Yes' : 'No'}`)
30
+ console.log('')
31
+
32
+ // Initialize main wallet using our Step 1 integration
33
+ console.log('๐Ÿ”ง Initializing MinimalXECWallet (Step 1 Integration)...')
34
+ const wallet = new MinimalXECWallet(walletData.mnemonic)
35
+
36
+ // Wait for wallet creation
37
+ await wallet.walletInfoPromise
38
+
39
+ // Initialize UTXO store
40
+ console.log('๐Ÿ“ฆ Initializing wallet...')
41
+ await wallet.initialize()
42
+
43
+ console.log('โœ… Main wallet initialized successfully!')
44
+ console.log('')
45
+
46
+ // Test 1: List all tokens using main wallet API
47
+ console.log('๐Ÿ“‹ Test 1: List all tokens (main wallet API)')
48
+ console.log('โ”€'.repeat(50))
49
+
50
+ try {
51
+ const tokens = await wallet.listETokens()
52
+ console.log(`โœ… Found ${tokens.length} token type(s):`)
53
+
54
+ for (const token of tokens) {
55
+ console.log(` โ€ข ${token.ticker} (${token.protocol}): ${token.balance.display} ${token.ticker}`)
56
+ console.log(` Name: ${token.name}`)
57
+ console.log(` Token ID: ${token.tokenId}`)
58
+ console.log(` UTXOs: ${token.utxoCount}`)
59
+ }
60
+
61
+ if (tokens.length === 0) {
62
+ console.log('โ„น๏ธ No tokens found - this may be expected if testing with empty wallet')
63
+ }
64
+ } catch (err) {
65
+ console.log(`โŒ Failed to list tokens: ${err.message}`)
66
+ throw err
67
+ }
68
+
69
+ console.log('')
70
+
71
+ // Test 2: Get specific token balances using main wallet API
72
+ console.log('๐Ÿ’ฐ Test 2: Get specific token balances (main wallet API)')
73
+ console.log('โ”€'.repeat(50))
74
+
75
+ // Test FLCT (SLP) balance
76
+ const FLCT_TOKEN_ID = '5e40dda12765d0b3819286f4bd50ec58a4bf8d7dbfd277152693ad9d34912135'
77
+ try {
78
+ console.log('Checking FLCT (SLP) balance...')
79
+ const flctBalance = await wallet.getETokenBalance({ tokenId: FLCT_TOKEN_ID })
80
+ console.log(`โœ… FLCT Balance: ${flctBalance.balance.display} ${flctBalance.ticker}`)
81
+ console.log(` Protocol: ${flctBalance.protocol}`)
82
+ console.log(` Name: ${flctBalance.name}`)
83
+ console.log(` UTXOs: ${flctBalance.utxoCount}`)
84
+ } catch (err) {
85
+ console.log(`โ„น๏ธ FLCT not found or error: ${err.message}`)
86
+ }
87
+
88
+ // Test TGR (ALP) balance
89
+ const TGR_TOKEN_ID = '6887ab3749e0d5168a04838216895c95fce61f99237626b08d50db804fcb1801'
90
+ try {
91
+ console.log('Checking TGR (ALP) balance...')
92
+ const tgrBalance = await wallet.getETokenBalance({ tokenId: TGR_TOKEN_ID })
93
+ console.log(`โœ… TGR Balance: ${tgrBalance.balance.display} ${tgrBalance.ticker}`)
94
+ console.log(` Protocol: ${tgrBalance.protocol}`)
95
+ console.log(` Name: ${tgrBalance.name}`)
96
+ console.log(` UTXOs: ${tgrBalance.utxoCount}`)
97
+ } catch (err) {
98
+ console.log(`โ„น๏ธ TGR not found or error: ${err.message}`)
99
+ }
100
+
101
+ console.log('')
102
+
103
+ // Test 3: Get token metadata using main wallet API
104
+ console.log('๐Ÿ“„ Test 3: Get token metadata (main wallet API)')
105
+ console.log('โ”€'.repeat(50))
106
+
107
+ try {
108
+ console.log('Getting FLCT metadata...')
109
+ const flctData = await wallet.getETokenData(FLCT_TOKEN_ID)
110
+ console.log('โœ… FLCT Metadata:')
111
+ console.log(` Ticker: ${flctData.ticker}`)
112
+ console.log(` Name: ${flctData.name}`)
113
+ console.log(` Protocol: ${flctData.protocol}`)
114
+ console.log(` Type: ${flctData.type}`)
115
+ console.log(` Decimals: ${flctData.decimals}`)
116
+ if (flctData.url) console.log(` URL: ${flctData.url}`)
117
+ } catch (err) {
118
+ console.log(`โ„น๏ธ FLCT metadata error: ${err.message}`)
119
+ }
120
+
121
+ try {
122
+ console.log('Getting TGR metadata...')
123
+ const tgrData = await wallet.getETokenData(TGR_TOKEN_ID)
124
+ console.log('โœ… TGR Metadata:')
125
+ console.log(` Ticker: ${tgrData.ticker}`)
126
+ console.log(` Name: ${tgrData.name}`)
127
+ console.log(` Protocol: ${tgrData.protocol}`)
128
+ console.log(` Type: ${tgrData.type}`)
129
+ console.log(` Decimals: ${tgrData.decimals}`)
130
+ if (tgrData.url) console.log(` URL: ${tgrData.url}`)
131
+ } catch (err) {
132
+ console.log(`โ„น๏ธ TGR metadata error: ${err.message}`)
133
+ }
134
+
135
+ console.log('')
136
+
137
+ // Test 4: Validate wallet state and integration
138
+ console.log('๐Ÿ” Test 4: Validate wallet state and integration')
139
+ console.log('โ”€'.repeat(50))
140
+
141
+ try {
142
+ const xecBalance = await wallet.getXecBalance()
143
+ console.log(`โœ… XEC Balance: ${xecBalance} XEC`)
144
+
145
+ const utxos = await wallet.getUtxos()
146
+ console.log(`โœ… Total UTXOs: ${utxos.utxos.length}`)
147
+
148
+ // Check if wallet has proper hybrid token manager integration
149
+ console.log(`โœ… HybridTokenManager: ${wallet.hybridTokens ? 'Integrated' : 'Missing'}`)
150
+ console.log(`โœ… Wallet initialized: ${wallet.isInitialized}`)
151
+ } catch (err) {
152
+ console.log(`โŒ Wallet state validation error: ${err.message}`)
153
+ throw err
154
+ }
155
+
156
+ console.log('')
157
+
158
+ // Summary
159
+ console.log('๐Ÿ“Š INTEGRATION TEST SUMMARY')
160
+ console.log('โ•'.repeat(60))
161
+ console.log('โœ… Main wallet initialization: PASSED')
162
+ console.log('โœ… Token listing via main API: PASSED')
163
+ console.log('โœ… Token balance via main API: PASSED')
164
+ console.log('โœ… Token metadata via main API: PASSED')
165
+ console.log('โœ… Wallet state validation: PASSED')
166
+ console.log('')
167
+ console.log('๐ŸŽ‰ Step 1 Main Wallet Integration: VALIDATED!')
168
+ console.log('')
169
+ console.log('๐Ÿ’ก Next Steps:')
170
+ console.log(' โ€ข Test real token sending: node examples/tokens/send-any-token.js')
171
+ console.log(' โ€ข Test token burning: node examples/tokens/burn-tokens.js')
172
+ console.log(' โ€ข Update all examples to use main wallet API')
173
+ } catch (err) {
174
+ console.error('\nโŒ Integration test failed:', err.message)
175
+ console.error('\n๐Ÿ“ This indicates an issue with Step 1 integration')
176
+
177
+ if (err.stack) {
178
+ console.error('\nStack trace:')
179
+ console.error(err.stack)
180
+ }
181
+
182
+ console.log('\n๐Ÿ”ง Debugging suggestions:')
183
+ console.log(' โ€ข Check wallet initialization')
184
+ console.log(' โ€ข Verify HybridTokenManager integration')
185
+ console.log(' โ€ข Test with unit tests: npm test')
186
+ console.log(' โ€ข Check network connectivity')
187
+
188
+ process.exit(1)
189
+ }
190
+ }
191
+
192
+ // Run the integration test
193
+ testMainWalletIntegration()
@@ -0,0 +1,205 @@
1
+ /*
2
+ Send all available XEC to another address.
3
+ This example shows how to empty your wallet by sending all funds to a recipient.
4
+ */
5
+
6
+ const MinimalXECWallet = require('../../index')
7
+ const WalletHelper = require('../utils/wallet-helper')
8
+
9
+ // Get command line arguments
10
+ const args = process.argv.slice(2)
11
+
12
+ function showUsage () {
13
+ console.log('Usage: node send-all-xec.js <recipient_address>')
14
+ console.log('')
15
+ console.log('Examples:')
16
+ console.log(' node send-all-xec.js ecash:qp1234...abc')
17
+ console.log('')
18
+ console.log('Parameters:')
19
+ console.log(' recipient_address: XEC address starting with ecash:')
20
+ console.log('')
21
+ console.log('Warning: This will send ALL XEC in your wallet to the recipient!')
22
+ console.log(' Transaction fees will be automatically deducted from the amount.')
23
+ }
24
+
25
+ async function sendAllXec () {
26
+ try {
27
+ console.log('๐Ÿ’ธ Sending ALL XEC from wallet...\n')
28
+
29
+ // Check arguments
30
+ if (args.length !== 1) {
31
+ console.log('โŒ Invalid arguments')
32
+ showUsage()
33
+ return
34
+ }
35
+
36
+ const recipientAddress = args[0]
37
+
38
+ // Validate arguments
39
+ if (!recipientAddress || !recipientAddress.startsWith('ecash:')) {
40
+ console.log('โŒ Invalid recipient address. Must start with "ecash:"')
41
+ showUsage()
42
+ return
43
+ }
44
+
45
+ // Load wallet from file
46
+ const walletData = WalletHelper.loadWallet()
47
+ if (!walletData) {
48
+ console.log(' Run: node examples/wallet-creation/create-new-wallet.js')
49
+ return
50
+ }
51
+
52
+ // Create wallet instance from saved data
53
+ const wallet = new MinimalXECWallet(walletData.mnemonic || walletData.privateKey)
54
+ await wallet.walletInfoPromise
55
+
56
+ // Initialize wallet
57
+ await wallet.initialize()
58
+
59
+ console.log('๐Ÿ’ฐ Checking wallet balance...')
60
+ const balance = await wallet.getXecBalance()
61
+
62
+ if (balance === 0) {
63
+ console.log('\n๐Ÿ’ธ Wallet is already empty!')
64
+ console.log(' There is no XEC to send.')
65
+ console.log(' Fund your wallet first to use this function.')
66
+ return
67
+ }
68
+
69
+ console.log('\n๐Ÿ“‹ Send All Transaction Details:')
70
+ console.log('โ•'.repeat(60))
71
+ console.log(`From: ${walletData.xecAddress}`)
72
+ console.log(`To: ${recipientAddress}`)
73
+ console.log(`Available Balance: ${balance.toLocaleString()} XEC`)
74
+ console.log('โ•'.repeat(60))
75
+
76
+ // Show UTXOs for transparency
77
+ console.log('\n๐Ÿ” Analyzing wallet UTXOs...')
78
+ const utxoData = await wallet.getUtxos()
79
+ const utxos = utxoData.utxos || []
80
+ const spendableUtxos = utxos.filter(utxo => utxo.blockHeight !== -1)
81
+
82
+ console.log(`Total UTXOs: ${utxos.length}`)
83
+ console.log(`Spendable UTXOs: ${spendableUtxos.length}`)
84
+
85
+ if (spendableUtxos.length === 0) {
86
+ console.log('\nโณ No spendable UTXOs found!')
87
+ console.log(' All your XEC might be in unconfirmed transactions.')
88
+ console.log(' Wait for confirmations before trying to send.')
89
+ return
90
+ }
91
+
92
+ // Estimate the transaction fee
93
+ const estimatedFeeRate = 1.2 // sats per byte
94
+ const estimatedTxSize = (spendableUtxos.length * 150) + (1 * 34) + 10 // Rough estimate
95
+ const estimatedFee = (estimatedTxSize * estimatedFeeRate) / 100 // Convert to XEC
96
+ const estimatedAmountToSend = balance - estimatedFee
97
+
98
+ console.log(`Estimated Fee: ~${estimatedFee.toFixed(4)} XEC`)
99
+ console.log(`Estimated Amount to Send: ~${estimatedAmountToSend.toLocaleString()} XEC`)
100
+
101
+ // Warning about emptying wallet
102
+ console.log('\nโš ๏ธ WALLET EMPTYING WARNING')
103
+ console.log(' This transaction will send ALL your XEC to the recipient!')
104
+ console.log(' After this transaction:')
105
+ console.log(' โ€ข Your wallet balance will be 0 XEC')
106
+ console.log(' โ€ข You will need to fund the wallet again to make new transactions')
107
+ console.log(' โ€ข Make sure the recipient address is correct!')
108
+ console.log('')
109
+ console.log(' ๐Ÿ” SECURITY CHECK:')
110
+ console.log(' โ€ข Are you migrating to a new wallet?')
111
+ console.log(' โ€ข Are you sending to your own address?')
112
+ console.log(' โ€ข Have you verified the recipient address?')
113
+
114
+ // Confirm transaction
115
+ const readline = require('readline')
116
+ const rl = readline.createInterface({
117
+ input: process.stdin,
118
+ output: process.stdout
119
+ })
120
+
121
+ console.log(`\n Recipient: ${recipientAddress}`)
122
+ const confirmed = await new Promise((resolve) => {
123
+ rl.question('\nAre you ABSOLUTELY SURE you want to empty your wallet? (type "YES" to confirm): ', (answer) => {
124
+ rl.close()
125
+ resolve(answer === 'YES')
126
+ })
127
+ })
128
+
129
+ if (!confirmed) {
130
+ console.log('โŒ Transaction cancelled by user')
131
+ console.log(' Your XEC is safe in your wallet.')
132
+ return
133
+ }
134
+
135
+ console.log('\n๐Ÿš€ Broadcasting send-all transaction...')
136
+
137
+ // Send all XEC to the recipient
138
+ const txid = await wallet.sendAllXec(recipientAddress)
139
+
140
+ console.log('\nโœ… Send-all transaction sent successfully!')
141
+ console.log('โ•'.repeat(60))
142
+ console.log(`Transaction ID: ${txid}`)
143
+ console.log(`Recipient: ${recipientAddress}`)
144
+ console.log('โ•'.repeat(60))
145
+
146
+ // Get updated balance
147
+ console.log('\n๐Ÿ’ฐ Checking final balance...')
148
+ const newBalance = await wallet.getXecBalance()
149
+ const actualAmountSent = balance - newBalance
150
+
151
+ console.log('\n๐Ÿ“Š Final Transaction Summary:')
152
+ console.log(`Previous Balance: ${balance.toLocaleString()} XEC`)
153
+ console.log(`Amount Sent: ${actualAmountSent.toLocaleString()} XEC`)
154
+ console.log(`Final Balance: ${newBalance.toLocaleString()} XEC`)
155
+
156
+ if (newBalance > 0) {
157
+ console.log(`\n๐Ÿ’ก Note: ${newBalance.toLocaleString()} XEC remains (dust or unconfirmed)`)
158
+ } else {
159
+ console.log('\n๐ŸŽ‰ Wallet successfully emptied!')
160
+ }
161
+
162
+ console.log('\n๐Ÿ”— View Transaction:')
163
+ console.log(` Explorer: https://explorer.e.cash/tx/${txid}`)
164
+ console.log(' Note: It may take a few minutes to appear in the explorer')
165
+
166
+ console.log('\n๐Ÿ“ What happens next:')
167
+ console.log(' โ€ข The transaction is now being processed by the network')
168
+ console.log(' โ€ข Confirmation usually takes 1-10 minutes')
169
+ console.log(' โ€ข Your wallet is now empty (balance: 0 XEC)')
170
+ console.log(' โ€ข To use this wallet again, send XEC to:', walletData.xecAddress)
171
+
172
+ // Suggest next steps
173
+ if (newBalance === 0) {
174
+ console.log('\n๐Ÿ’ก Suggested next steps:')
175
+ console.log(' โ€ข If migrating: Import your wallet elsewhere using the mnemonic')
176
+ console.log(' โ€ข If consolidating: You can delete the wallet.json file')
177
+ console.log(' โ€ข If temporarily emptying: Keep the wallet.json for future use')
178
+ }
179
+ } catch (err) {
180
+ console.error('โŒ Failed to send all XEC:', err.message)
181
+
182
+ // Provide helpful error context
183
+ if (err.message.includes('insufficient')) {
184
+ console.log('\n๐Ÿ’ธ Insufficient Funds:')
185
+ console.log(' โ€ข Your wallet might not have enough XEC to cover transaction fees')
186
+ console.log(' โ€ข Try waiting for unconfirmed transactions to confirm')
187
+ console.log(' โ€ข Check if you have any spendable UTXOs')
188
+ } else if (err.message.includes('address')) {
189
+ console.log('\n๐Ÿ“ Address Error:')
190
+ console.log(' โ€ข Check that the recipient address is valid')
191
+ console.log(' โ€ข Make sure it starts with "ecash:"')
192
+ console.log(' โ€ข Verify the address with the recipient')
193
+ } else if (err.message.includes('network')) {
194
+ console.log('\n๐ŸŒ Network Error:')
195
+ console.log(' โ€ข Check your internet connection')
196
+ console.log(' โ€ข The network might be temporarily unavailable')
197
+ console.log(' โ€ข Try again in a few moments')
198
+ }
199
+
200
+ process.exit(1)
201
+ }
202
+ }
203
+
204
+ // Run the example
205
+ sendAllXec()
@@ -0,0 +1,217 @@
1
+ /*
2
+ Send XEC to multiple recipients in a single transaction.
3
+ This example shows how to create a transaction with multiple outputs.
4
+ */
5
+
6
+ const MinimalXECWallet = require('../../index')
7
+ const WalletHelper = require('../utils/wallet-helper')
8
+
9
+ // Get command line arguments
10
+ const args = process.argv.slice(2)
11
+
12
+ function showUsage () {
13
+ console.log('Usage: node send-to-multiple.js <address1> <amount1> <address2> <amount2> [...]')
14
+ console.log('')
15
+ console.log('Examples:')
16
+ console.log(' node send-to-multiple.js ecash:qp1234...abc 50 ecash:qr5678...def 30')
17
+ console.log(' node send-to-multiple.js ecash:qa1111...111 100 ecash:qb2222...222 25.5 ecash:qc3333...333 75')
18
+ console.log('')
19
+ console.log('Parameters:')
20
+ console.log(' addressN: XEC address starting with ecash:')
21
+ console.log(' amountN: Amount in XEC (minimum 5.46 XEC per output due to dust limit)')
22
+ console.log('')
23
+ console.log('Note: You can send to up to 100 recipients in a single transaction')
24
+ console.log(' Transaction fee will be automatically calculated and deducted')
25
+ }
26
+
27
+ function parseRecipients (args) {
28
+ if (args.length < 4 || args.length % 2 !== 0) {
29
+ return null
30
+ }
31
+
32
+ const recipients = []
33
+ for (let i = 0; i < args.length; i += 2) {
34
+ const address = args[i]
35
+ const amount = parseFloat(args[i + 1])
36
+
37
+ if (!address || !address.startsWith('ecash:')) {
38
+ console.log(`โŒ Invalid address at position ${i + 1}: ${address}`)
39
+ return null
40
+ }
41
+
42
+ if (isNaN(amount) || amount <= 0) {
43
+ console.log(`โŒ Invalid amount at position ${i + 2}: ${args[i + 1]}`)
44
+ return null
45
+ }
46
+
47
+ if (amount < 5.46) {
48
+ console.log(`โŒ Amount too small at position ${i + 2}: ${amount} XEC (minimum 5.46 XEC due to dust limit)`)
49
+ return null
50
+ }
51
+
52
+ recipients.push({ address, amount })
53
+ }
54
+
55
+ return recipients
56
+ }
57
+
58
+ async function sendToMultiple () {
59
+ try {
60
+ console.log('๐Ÿ“ค Sending XEC to multiple recipients...\n')
61
+
62
+ // Check arguments
63
+ if (args.length < 4) {
64
+ console.log('โŒ Not enough arguments')
65
+ showUsage()
66
+ return
67
+ }
68
+
69
+ // Parse recipients
70
+ const recipients = parseRecipients(args)
71
+ if (!recipients) {
72
+ showUsage()
73
+ return
74
+ }
75
+
76
+ // Load wallet from file
77
+ const walletData = WalletHelper.loadWallet()
78
+ if (!walletData) {
79
+ console.log(' Run: node examples/wallet-creation/create-new-wallet.js')
80
+ return
81
+ }
82
+
83
+ // Create wallet instance from saved data
84
+ const wallet = new MinimalXECWallet(walletData.mnemonic || walletData.privateKey)
85
+ await wallet.walletInfoPromise
86
+
87
+ // Initialize wallet
88
+ await wallet.initialize()
89
+
90
+ console.log('๐Ÿ’ฐ Checking wallet balance...')
91
+ const balance = await wallet.getXecBalance()
92
+
93
+ // Calculate total amount to send
94
+ const totalAmount = recipients.reduce((sum, recipient) => sum + recipient.amount, 0)
95
+
96
+ console.log('\n๐Ÿ“‹ Multi-Output Transaction Details:')
97
+ console.log('โ•'.repeat(70))
98
+ console.log(`From: ${walletData.xecAddress}`)
99
+ console.log(`Recipients: ${recipients.length}`)
100
+ console.log(`Total Amount: ${totalAmount.toLocaleString()} XEC`)
101
+ console.log(`Current Balance: ${balance.toLocaleString()} XEC`)
102
+ console.log('โ•'.repeat(70))
103
+
104
+ // Show all recipients
105
+ console.log('\n๐Ÿ“จ Recipients:')
106
+ recipients.forEach((recipient, index) => {
107
+ const addressShort = `${recipient.address.substring(0, 20)}...${recipient.address.substring(recipient.address.length - 8)}`
108
+ console.log(`${index + 1}. ${addressShort}: ${recipient.amount.toLocaleString()} XEC`)
109
+ })
110
+
111
+ // Check if we have enough balance (rough estimate)
112
+ const estimatedFee = recipients.length * 0.01 // Rough estimate: 0.01 XEC per output
113
+ if (balance < totalAmount + estimatedFee) {
114
+ console.log('\nโŒ Insufficient balance!')
115
+ console.log(` Required: ~${(totalAmount + estimatedFee).toLocaleString()} XEC (including estimated fee)`)
116
+ console.log(` Available: ${balance.toLocaleString()} XEC`)
117
+ console.log(' Fund your wallet or reduce the amounts')
118
+ return
119
+ }
120
+
121
+ // Confirm transaction
122
+ console.log('\nโš ๏ธ Transaction Confirmation Required')
123
+ console.log(' This will send real XEC from your wallet!')
124
+ console.log(' Make sure all recipient addresses are correct.')
125
+ console.log(` Total recipients: ${recipients.length}`)
126
+ console.log(` Total amount: ${totalAmount.toLocaleString()} XEC`)
127
+
128
+ const readline = require('readline')
129
+ const rl = readline.createInterface({
130
+ input: process.stdin,
131
+ output: process.stdout
132
+ })
133
+
134
+ const confirmed = await new Promise((resolve) => {
135
+ rl.question('\nDo you want to proceed? (yes/no): ', (answer) => {
136
+ rl.close()
137
+ resolve(answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y')
138
+ })
139
+ })
140
+
141
+ if (!confirmed) {
142
+ console.log('โŒ Transaction cancelled by user')
143
+ return
144
+ }
145
+
146
+ console.log('\n๐Ÿš€ Broadcasting multi-output transaction...')
147
+
148
+ // Prepare the outputs
149
+ const outputs = recipients.map(recipient => ({
150
+ address: recipient.address,
151
+ amountSat: Math.round(recipient.amount * 100) // Convert XEC to satoshis
152
+ }))
153
+
154
+ // Send the transaction
155
+ const txid = await wallet.sendXec(outputs)
156
+
157
+ console.log('\nโœ… Multi-output transaction sent successfully!')
158
+ console.log('โ•'.repeat(70))
159
+ console.log(`Transaction ID: ${txid}`)
160
+ console.log(`Recipients: ${recipients.length}`)
161
+ console.log(`Total Amount: ${totalAmount.toLocaleString()} XEC`)
162
+ console.log('โ•'.repeat(70))
163
+
164
+ // Get updated balance
165
+ console.log('\n๐Ÿ’ฐ Getting updated balance...')
166
+ const newBalance = await wallet.getXecBalance()
167
+ const totalCost = balance - newBalance
168
+ const feePaid = totalCost - totalAmount
169
+
170
+ console.log('\n๐Ÿ“Š Transaction Summary:')
171
+ console.log(`Previous Balance: ${balance.toLocaleString()} XEC`)
172
+ console.log(`Total Sent: ${totalAmount.toLocaleString()} XEC`)
173
+ console.log(`Fee Paid: ${feePaid.toLocaleString()} XEC`)
174
+ console.log(`Total Cost: ${totalCost.toLocaleString()} XEC`)
175
+ console.log(`New Balance: ${newBalance.toLocaleString()} XEC`)
176
+
177
+ console.log('\n๐Ÿ“จ Delivery Summary:')
178
+ recipients.forEach((recipient, index) => {
179
+ const addressShort = `${recipient.address.substring(0, 15)}...${recipient.address.substring(recipient.address.length - 6)}`
180
+ console.log(`${index + 1}. ${addressShort}: ${recipient.amount.toLocaleString()} XEC โœ…`)
181
+ })
182
+
183
+ console.log('\n๐Ÿ”— View Transaction:')
184
+ console.log(` Explorer: https://explorer.e.cash/tx/${txid}`)
185
+ console.log(' Note: It may take a few minutes to appear in the explorer')
186
+
187
+ console.log('\n๐Ÿ’ก Benefits of Multi-Output Transactions:')
188
+ console.log(' โ€ข Lower total fees compared to separate transactions')
189
+ console.log(' โ€ข Single confirmation for all recipients')
190
+ console.log(' โ€ข More efficient use of blockchain space')
191
+ } catch (err) {
192
+ console.error('โŒ Failed to send multi-output transaction:', err.message)
193
+
194
+ // Provide helpful error context
195
+ if (err.message.includes('insufficient')) {
196
+ console.log('\n๐Ÿ’ธ Insufficient Funds:')
197
+ console.log(' โ€ข Your wallet does not have enough XEC for this transaction')
198
+ console.log(' โ€ข Multi-output transactions require higher fees')
199
+ console.log(' โ€ข Try reducing amounts or number of recipients')
200
+ } else if (err.message.includes('address')) {
201
+ console.log('\n๐Ÿ“ Address Error:')
202
+ console.log(' โ€ข Check that all recipient addresses are valid')
203
+ console.log(' โ€ข Make sure they all start with "ecash:"')
204
+ console.log(' โ€ข Verify addresses with recipients')
205
+ } else if (err.message.includes('network')) {
206
+ console.log('\n๐ŸŒ Network Error:')
207
+ console.log(' โ€ข Check your internet connection')
208
+ console.log(' โ€ข The network might be temporarily unavailable')
209
+ console.log(' โ€ข Try again in a few moments')
210
+ }
211
+
212
+ process.exit(1)
213
+ }
214
+ }
215
+
216
+ // Run the example
217
+ sendToMultiple()