smartledger-bsv 3.3.3 → 3.3.5

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 (62) hide show
  1. package/CHANGELOG.md +50 -28
  2. package/README.md +55 -36
  3. package/bsv-covenant.min.js +6 -6
  4. package/bsv-gdaf.min.js +6 -6
  5. package/bsv-ltp.min.js +6 -6
  6. package/bsv-mnemonic.min.js +4 -4
  7. package/bsv-smartcontract.min.js +5 -5
  8. package/bsv.bundle.js +5 -5
  9. package/bsv.min.js +5 -5
  10. package/demos/README.md +188 -0
  11. package/demos/architecture_demo.js +247 -0
  12. package/demos/browser-test.html +1208 -0
  13. package/demos/bsv_wallet_demo.js +242 -0
  14. package/demos/complete_ltp_demo.js +511 -0
  15. package/demos/debug_tools_demo.js +87 -0
  16. package/demos/demo_features.js +123 -0
  17. package/demos/easy_interface_demo.js +109 -0
  18. package/demos/ecies_demo.js +182 -0
  19. package/demos/gdaf_core_test.js +131 -0
  20. package/demos/gdaf_demo.js +237 -0
  21. package/demos/ltp_demo.js +361 -0
  22. package/demos/ltp_primitives_demo.js +403 -0
  23. package/demos/message_demo.js +209 -0
  24. package/demos/preimage_separation_demo.js +383 -0
  25. package/demos/script_helper_demo.js +289 -0
  26. package/demos/security_demo.js +287 -0
  27. package/demos/shamir_demo.js +121 -0
  28. package/demos/simple_demo.js +204 -0
  29. package/demos/simple_p2pkh_demo.js +169 -0
  30. package/demos/simple_utxo_preimage_demo.js +196 -0
  31. package/demos/smart_contract_demo.html +1347 -0
  32. package/demos/smart_contract_demo.js +910 -0
  33. package/demos/utxo_generator_demo.js +244 -0
  34. package/demos/validation_pipeline_demo.js +155 -0
  35. package/demos/web3keys.html +740 -0
  36. package/docs/BUNDLE_UPDATE_SUMMARY.md +40 -0
  37. package/docs/DOCUMENTATION_REVIEW_REPORT.md +11 -11
  38. package/docs/FIX_CREATEHMAC_ISSUE.md +91 -0
  39. package/docs/MODULE_REFERENCE_COMPLETE.md +28 -28
  40. package/docs/SMARTLEDGER_BSV_USAGE_ANSWERS.md +477 -0
  41. package/docs/SMARTLEDGER_BSV_USAGE_EXAMPLES.js +372 -0
  42. package/docs/SMARTLEDGER_BSV_USAGE_GUIDE.md +555 -0
  43. package/docs/SMART_CONTRACT_DEVELOPMENT_GUIDE.md +1459 -0
  44. package/docs/advanced/UTXO_MANAGER_GUIDE.md +2 -2
  45. package/docs/getting-started/INSTALLATION.md +25 -25
  46. package/docs/getting-started/QUICK_START.md +7 -7
  47. package/docs/migration/FROM_BSV_1_5_6.md +5 -5
  48. package/examples/complete_workflow_demo.js +783 -0
  49. package/examples/definitive_working_demo.js +261 -0
  50. package/examples/final_working_contracts.js +338 -0
  51. package/examples/smart_contract_templates.js +718 -0
  52. package/examples/working_smart_contracts.js +348 -0
  53. package/index.js +7 -0
  54. package/lib/browser-utxo-manager-es5.js +316 -0
  55. package/lib/browser-utxo-manager.js +533 -0
  56. package/lib/mnemonic/pbkdf2.browser.js +69 -0
  57. package/lib/mnemonic/pbkdf2.js +2 -68
  58. package/lib/mnemonic/pbkdf2.node.js +68 -0
  59. package/package.json +19 -8
  60. package/tests/browser-compatibility/README.md +35 -0
  61. package/tests/browser-compatibility/test-cdn-vs-local.html +186 -0
  62. package/tests/browser-compatibility/test-pbkdf2.html +51 -0
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Working Smart Contract Examples - Fixed Version
3
+ * ==============================================
4
+ *
5
+ * This file contains working, tested smart contract examples that properly
6
+ * integrate with smartledger-bsv's preimage validation system.
7
+ */
8
+
9
+ const bsv = require('../index.js')
10
+
11
+ // Enable colored output if available
12
+ let chalk
13
+ try {
14
+ chalk = require('chalk')
15
+ } catch (e) {
16
+ chalk = {
17
+ green: (text) => `✅ ${text}`,
18
+ red: (text) => `❌ ${text}`,
19
+ yellow: (text) => `⚠️ ${text}`,
20
+ blue: (text) => `ℹ️ ${text}`,
21
+ magenta: (text) => `🔮 ${text}`,
22
+ cyan: (text) => `🌊 ${text}`,
23
+ bold: (text) => `**${text}**`
24
+ }
25
+ }
26
+
27
+ console.log(chalk.bold.blue('\n🔧 Working Smart Contract Examples (Fixed)\n'))
28
+
29
+ /**
30
+ * WorkingAmountContract - A properly functioning amount validation contract
31
+ */
32
+ class WorkingAmountContract {
33
+ constructor(expectedAmount) {
34
+ this.expectedAmount = expectedAmount
35
+ this.privateKey = new bsv.PrivateKey()
36
+
37
+ // Create the contract script using the quick covenant method
38
+ this.script = this._createWorkingScript()
39
+ this.address = bsv.SmartContract.utils.createCovenantAddress(this.script)
40
+
41
+ console.log(chalk.green(`✅ WorkingAmountContract created`))
42
+ console.log(chalk.blue(` Expected Amount: ${expectedAmount} satoshis`))
43
+ console.log(chalk.blue(` Contract Address: ${this.address.toString()}`))
44
+ }
45
+
46
+ _createWorkingScript() {
47
+ // Use the createQuickCovenant method which is known to work
48
+ const covenant = bsv.SmartContract.createQuickCovenant('value_lock', {
49
+ value: this.expectedAmount
50
+ })
51
+
52
+ // Convert the ASM to a proper BSV Script
53
+ return bsv.Script.fromASM(covenant.asm)
54
+ }
55
+
56
+ // Test validation with a real preimage
57
+ validatePayment(preimageHex) {
58
+ try {
59
+ console.log(chalk.yellow('🔍 Testing payment validation with working script...'))
60
+
61
+ const result = bsv.SmartContract.testScript(
62
+ preimageHex,
63
+ this.script.toHex(),
64
+ { verbose: true }
65
+ )
66
+
67
+ if (result.valid) {
68
+ console.log(chalk.green('✅ Payment validation PASSED'))
69
+ } else {
70
+ console.log(chalk.red('❌ Payment validation FAILED'))
71
+ console.log(chalk.red(` Error: ${result.error || 'Unknown error'}`))
72
+ }
73
+
74
+ return {
75
+ success: result.valid,
76
+ error: result.error,
77
+ details: result
78
+ }
79
+ } catch (error) {
80
+ console.log(chalk.red('❌ Validation error:', error.message))
81
+ return {
82
+ success: false,
83
+ error: error.message
84
+ }
85
+ }
86
+ }
87
+
88
+ // Create a proper test preimage for this contract
89
+ createTestPreimage(inputAmount) {
90
+ try {
91
+ console.log(chalk.yellow('📝 Creating test preimage...'))
92
+
93
+ // Create a mock transaction that would spend from this contract
94
+ const mockTx = new bsv.Transaction()
95
+
96
+ // Add input from contract (mock)
97
+ mockTx.addInput({
98
+ prevTxId: 'a'.repeat(64),
99
+ outputIndex: 0,
100
+ script: this.script.toHex(),
101
+ sequenceNumber: 0xffffffff
102
+ })
103
+
104
+ // Add output with the input amount
105
+ mockTx.addOutput({
106
+ script: bsv.Script.buildPublicKeyHashOut(new bsv.Address('1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2')).toHex(),
107
+ satoshis: inputAmount
108
+ })
109
+
110
+ // Generate preimage
111
+ const preimage = bsv.Transaction.sighash.sighashPreimage(
112
+ mockTx,
113
+ bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID,
114
+ 0,
115
+ this.script,
116
+ new bsv.crypto.BN(inputAmount + 1000) // Contract input amount
117
+ )
118
+
119
+ console.log(chalk.blue(` Generated preimage: ${preimage.toString('hex').substring(0, 64)}...`))
120
+ return preimage.toString('hex')
121
+
122
+ } catch (error) {
123
+ console.log(chalk.red('❌ Preimage generation error:', error.message))
124
+ return null
125
+ }
126
+ }
127
+
128
+ // Complete test of the contract
129
+ runCompleteTest() {
130
+ console.log(chalk.cyan('\n🧪 Running Complete Contract Test'))
131
+ console.log(chalk.cyan('==================================='))
132
+
133
+ // Test with correct amount
134
+ console.log(chalk.yellow('\n1. Testing with CORRECT amount'))
135
+ const correctPreimage = this.createTestPreimage(this.expectedAmount)
136
+ if (correctPreimage) {
137
+ const correctResult = this.validatePayment(correctPreimage)
138
+ console.log(chalk.blue(` Result: ${correctResult.success ? 'PASS' : 'FAIL'}`))
139
+ }
140
+
141
+ // Test with incorrect amount
142
+ console.log(chalk.yellow('\n2. Testing with INCORRECT amount'))
143
+ const incorrectPreimage = this.createTestPreimage(this.expectedAmount - 10000)
144
+ if (incorrectPreimage) {
145
+ const incorrectResult = this.validatePayment(incorrectPreimage)
146
+ console.log(chalk.blue(` Result: ${incorrectResult.success ? 'UNEXPECTED PASS' : 'EXPECTED FAIL'}`))
147
+ }
148
+
149
+ console.log(chalk.cyan('\n✅ Complete test finished'))
150
+ }
151
+
152
+ getContractInfo() {
153
+ return {
154
+ expectedAmount: this.expectedAmount,
155
+ address: this.address.toString(),
156
+ script: {
157
+ hex: this.script.toHex(),
158
+ asm: this.script.toASM(),
159
+ size: this.script.toBuffer().length
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ * SimpleFieldExtractionDemo - Shows how field extraction actually works
167
+ */
168
+ class SimpleFieldExtractionDemo {
169
+ constructor() {
170
+ console.log(chalk.bold.magenta('\n🔬 Field Extraction Demo\n'))
171
+ }
172
+
173
+ demonstrateFieldExtraction() {
174
+ console.log(chalk.cyan('Testing Field Extraction with Real Preimage'))
175
+ console.log(chalk.cyan('===========================================\n'))
176
+
177
+ // Create a simple transaction to get a real preimage
178
+ try {
179
+ const tx = new bsv.Transaction()
180
+ .from({
181
+ txId: 'a'.repeat(64),
182
+ outputIndex: 0,
183
+ script: bsv.Script.buildPublicKeyHashOut(new bsv.Address('1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2')).toHex(),
184
+ satoshis: 100000
185
+ })
186
+ .to('1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2', 90000)
187
+
188
+ const preimage = bsv.Transaction.sighash.sighashPreimage(
189
+ tx,
190
+ bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID,
191
+ 0,
192
+ bsv.Script.buildPublicKeyHashOut(new bsv.Address('1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2')),
193
+ new bsv.crypto.BN(100000)
194
+ )
195
+
196
+ console.log(chalk.blue(`Generated preimage: ${preimage.toString('hex').substring(0, 64)}...`))
197
+
198
+ // Test field extraction
199
+ const fields = ['value', 'nVersion', 'nLocktime', 'hashOutputs']
200
+
201
+ fields.forEach(field => {
202
+ console.log(chalk.yellow(`\nTesting ${field} extraction:`))
203
+
204
+ try {
205
+ const result = bsv.SmartContract.testFieldExtraction(
206
+ preimage.toString('hex'),
207
+ field
208
+ )
209
+
210
+ if (result.success) {
211
+ console.log(chalk.green(`✅ ${field} extraction successful`))
212
+ if (result.fieldExtraction) {
213
+ console.log(chalk.blue(` Value: ${result.fieldExtraction.value}`))
214
+ if (result.fieldExtraction.interpretation) {
215
+ console.log(chalk.blue(` Interpreted: ${result.fieldExtraction.interpretation.description || result.fieldExtraction.interpretation.satoshis || 'N/A'}`))
216
+ }
217
+ }
218
+ } else {
219
+ console.log(chalk.red(`❌ ${field} extraction failed`))
220
+ console.log(chalk.red(` Error: ${result.error || 'Unknown error'}`))
221
+ }
222
+ } catch (error) {
223
+ console.log(chalk.red(`❌ ${field} extraction error: ${error.message}`))
224
+ }
225
+ })
226
+
227
+ } catch (error) {
228
+ console.log(chalk.red('❌ Demo error:', error.message))
229
+ }
230
+ }
231
+ }
232
+
233
+ /**
234
+ * CovenantBuilderTest - Test the covenant builder functionality
235
+ */
236
+ class CovenantBuilderTest {
237
+ constructor() {
238
+ console.log(chalk.bold.magenta('\n🏗️ Covenant Builder Test\n'))
239
+ }
240
+
241
+ testCovenantBuilder() {
242
+ console.log(chalk.cyan('Testing CovenantBuilder Functionality'))
243
+ console.log(chalk.cyan('===================================\n'))
244
+
245
+ try {
246
+ // Test 1: Simple covenant using CovenantTemplates
247
+ console.log(chalk.yellow('1. Testing CovenantTemplates.valueLock'))
248
+
249
+ const valueLockCovenant = bsv.SmartContract.CovenantTemplates.valueLock('50c3000000000000') // 50000 satoshis in little-endian hex
250
+ const builtCovenant = valueLockCovenant.build()
251
+
252
+ console.log(chalk.blue(` ASM: ${builtCovenant.cleanedASM}`))
253
+ console.log(chalk.blue(` Hex: ${builtCovenant.hex}`))
254
+ console.log(chalk.blue(` Size: ${builtCovenant.size} operations`))
255
+
256
+ // Test 2: Custom covenant using CovenantBuilder
257
+ console.log(chalk.yellow('\n2. Testing Custom CovenantBuilder'))
258
+
259
+ const customBuilder = new bsv.SmartContract.CovenantBuilder()
260
+ const customCovenant = customBuilder
261
+ .comment('Simple value validation')
262
+ .extractField('value')
263
+ .push(100000)
264
+ .greaterThanOrEqual()
265
+ .verify()
266
+ .build()
267
+
268
+ console.log(chalk.blue(` Custom ASM: ${customCovenant.cleanedASM}`))
269
+ console.log(chalk.blue(` Custom Hex: ${customCovenant.hex}`))
270
+
271
+ // Test 3: Quick covenant creation
272
+ console.log(chalk.yellow('\n3. Testing Quick Covenant Creation'))
273
+
274
+ const quickCovenant = bsv.SmartContract.createQuickCovenant('value_lock', {
275
+ value: 75000
276
+ })
277
+
278
+ console.log(chalk.blue(` Quick ASM: ${quickCovenant.asm}`))
279
+ console.log(chalk.blue(` Quick Hex: ${quickCovenant.hex}`))
280
+
281
+ console.log(chalk.green('\n✅ All covenant builder tests completed'))
282
+
283
+ } catch (error) {
284
+ console.log(chalk.red('❌ Covenant builder test error:', error.message))
285
+ console.error(error.stack)
286
+ }
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Main execution function
292
+ */
293
+ async function main() {
294
+ try {
295
+ console.log(chalk.bold.blue('Working Smart Contract Examples'))
296
+ console.log(chalk.blue('===============================\n'))
297
+
298
+ // 1. Test field extraction first
299
+ const fieldDemo = new SimpleFieldExtractionDemo()
300
+ fieldDemo.demonstrateFieldExtraction()
301
+
302
+ // 2. Test covenant builder
303
+ const builderTest = new CovenantBuilderTest()
304
+ builderTest.testCovenantBuilder()
305
+
306
+ // 3. Test working contract
307
+ console.log(chalk.bold.magenta('\n💰 Working Amount Contract Test\n'))
308
+
309
+ const workingContract = new WorkingAmountContract(100000) // 100k sats
310
+ workingContract.runCompleteTest()
311
+
312
+ // 4. Show contract info
313
+ console.log(chalk.bold.yellow('\n📋 Contract Information'))
314
+ console.log(chalk.yellow('======================='))
315
+
316
+ const info = workingContract.getContractInfo()
317
+ console.log(chalk.blue(`Expected Amount: ${info.expectedAmount} satoshis`))
318
+ console.log(chalk.blue(`Contract Address: ${info.address}`))
319
+ console.log(chalk.blue(`Script Size: ${info.script.size} bytes`))
320
+ console.log(chalk.blue(`Script ASM: ${info.script.asm}`))
321
+
322
+ console.log(chalk.bold.green('\n🎉 All working examples completed successfully!'))
323
+
324
+ // Usage instructions
325
+ console.log(chalk.bold.yellow('\n📚 Key Learnings:'))
326
+ console.log(chalk.yellow('1. Use bsv.SmartContract.createQuickCovenant() for simple contracts'))
327
+ console.log(chalk.yellow('2. Field extraction works with proper preimage format'))
328
+ console.log(chalk.yellow('3. CovenantBuilder.build() returns an object, not a Script'))
329
+ console.log(chalk.yellow('4. Convert covenant ASM to Script using bsv.Script.fromASM()'))
330
+ console.log(chalk.yellow('5. Test with real preimages for accurate validation'))
331
+
332
+ } catch (error) {
333
+ console.error(chalk.bold.red('\nDemo Error:'), error.message)
334
+ console.error(chalk.red('Stack:'), error.stack)
335
+ }
336
+ }
337
+
338
+ // Export for use in other modules
339
+ module.exports = {
340
+ WorkingAmountContract,
341
+ SimpleFieldExtractionDemo,
342
+ CovenantBuilderTest
343
+ }
344
+
345
+ // Run demo if executed directly
346
+ if (require.main === module) {
347
+ main().catch(console.error)
348
+ }
package/index.js CHANGED
@@ -115,6 +115,13 @@ try {
115
115
  }
116
116
  }
117
117
 
118
+ // Browser-compatible UTXO Manager (always available)
119
+ try {
120
+ bsv.BrowserUTXOManager = require('./lib/browser-utxo-manager-es5')
121
+ } catch (e) {
122
+ // BrowserUTXOManager not available
123
+ }
124
+
118
125
  // Node.js specific tools (advanced development tools)
119
126
  if (typeof window === 'undefined' && typeof require === 'function') {
120
127
  try {
@@ -0,0 +1,316 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Browser-Compatible UTXO Manager (ES5 Compatible)
5
+ * Lightweight UTXO management for browser environments with configurable storage
6
+ */
7
+
8
+ var STORAGE_TYPES = {
9
+ MEMORY: 'memory',
10
+ SESSION: 'session',
11
+ LOCAL: 'local'
12
+ }
13
+
14
+ function BrowserUTXOManager(options) {
15
+ options = options || {}
16
+
17
+ this.options = {
18
+ storage: options.storage || STORAGE_TYPES.MEMORY,
19
+ storageKey: options.storageKey || 'smartledger-utxos',
20
+ autoSave: options.autoSave !== false,
21
+ maxUTXOs: options.maxUTXOs || 1000
22
+ }
23
+
24
+ // Validate storage type
25
+ var validTypes = [STORAGE_TYPES.MEMORY, STORAGE_TYPES.SESSION, STORAGE_TYPES.LOCAL]
26
+ if (validTypes.indexOf(this.options.storage) === -1) {
27
+ throw new Error('Invalid storage type: ' + this.options.storage)
28
+ }
29
+
30
+ this.utxos = new Map()
31
+ this.addressIndex = new Map()
32
+ this.spentUTXOs = new Map()
33
+ this.metadata = {
34
+ totalUTXOs: 0,
35
+ totalValue: 0,
36
+ createdAt: new Date().toISOString(),
37
+ lastModified: new Date().toISOString()
38
+ }
39
+
40
+ this.loadFromStorage()
41
+ }
42
+
43
+ BrowserUTXOManager.prototype.getStorage = function() {
44
+ if (this.options.storage === STORAGE_TYPES.SESSION) {
45
+ return typeof sessionStorage !== 'undefined' ? sessionStorage : null
46
+ } else if (this.options.storage === STORAGE_TYPES.LOCAL) {
47
+ return typeof localStorage !== 'undefined' ? localStorage : null
48
+ }
49
+ return null
50
+ }
51
+
52
+ BrowserUTXOManager.prototype.loadFromStorage = function() {
53
+ var storage = this.getStorage()
54
+ if (!storage) return
55
+
56
+ try {
57
+ var data = storage.getItem(this.options.storageKey)
58
+ if (!data) return
59
+
60
+ var parsed = JSON.parse(data)
61
+ if (!parsed.utxos) return
62
+
63
+ var self = this
64
+ parsed.utxos.forEach(function(utxoData) {
65
+ var key = utxoData.txid + ':' + utxoData.vout
66
+ self.utxos.set(key, utxoData)
67
+
68
+ if (!self.addressIndex.has(utxoData.address)) {
69
+ self.addressIndex.set(utxoData.address, new Set())
70
+ }
71
+ self.addressIndex.get(utxoData.address).add(key)
72
+ })
73
+
74
+ if (parsed.metadata) {
75
+ this.metadata = parsed.metadata
76
+ }
77
+
78
+ console.log('✅ BrowserUTXOManager: Loaded ' + this.utxos.size + ' UTXOs from ' + this.options.storage + ' storage')
79
+ } catch (e) {
80
+ console.error('Failed to load UTXOs from storage:', e)
81
+ }
82
+ }
83
+
84
+ BrowserUTXOManager.prototype.saveToStorage = function() {
85
+ if (this.options.storage === STORAGE_TYPES.MEMORY) return
86
+
87
+ var storage = this.getStorage()
88
+ if (!storage) return
89
+
90
+ try {
91
+ var data = {
92
+ utxos: Array.from(this.utxos.values()),
93
+ metadata: this.metadata,
94
+ version: '1.0.0',
95
+ timestamp: new Date().toISOString()
96
+ }
97
+
98
+ storage.setItem(this.options.storageKey, JSON.stringify(data))
99
+ console.log('💾 BrowserUTXOManager: Saved ' + this.utxos.size + ' UTXOs to ' + this.options.storage + ' storage')
100
+ } catch (e) {
101
+ console.error('Failed to save UTXOs to storage:', e)
102
+ }
103
+ }
104
+
105
+ BrowserUTXOManager.prototype.addUTXO = function(utxo) {
106
+ if (!utxo || !utxo.txid || typeof utxo.vout !== 'number') {
107
+ throw new Error('Invalid UTXO: missing txid or vout')
108
+ }
109
+
110
+ var key = utxo.txid + ':' + utxo.vout
111
+
112
+ if (this.utxos.has(key)) {
113
+ console.log('⚠️ UTXO already exists: ' + key)
114
+ return false
115
+ }
116
+
117
+ // Add required fields
118
+ var utxoData = {
119
+ txid: utxo.txid,
120
+ vout: utxo.vout,
121
+ address: utxo.address || '',
122
+ satoshis: utxo.satoshis || 0,
123
+ script: utxo.script || '',
124
+ status: 'available',
125
+ addedAt: new Date().toISOString()
126
+ }
127
+
128
+ this.utxos.set(key, utxoData)
129
+
130
+ // Update address index
131
+ if (utxoData.address) {
132
+ if (!this.addressIndex.has(utxoData.address)) {
133
+ this.addressIndex.set(utxoData.address, new Set())
134
+ }
135
+ this.addressIndex.get(utxoData.address).add(key)
136
+ }
137
+
138
+ this.updateMetadata()
139
+ if (this.options.autoSave) {
140
+ this.saveToStorage()
141
+ }
142
+
143
+ return true
144
+ }
145
+
146
+ BrowserUTXOManager.prototype.getUTXOsForAddress = function(address) {
147
+ var utxoKeys = this.addressIndex.get(address)
148
+ if (!utxoKeys) return []
149
+
150
+ var results = []
151
+ var self = this
152
+ utxoKeys.forEach(function(key) {
153
+ var utxo = self.utxos.get(key)
154
+ if (utxo && utxo.status === 'available') {
155
+ results.push(utxo)
156
+ }
157
+ })
158
+
159
+ return results
160
+ }
161
+
162
+ BrowserUTXOManager.prototype.getBalance = function(address) {
163
+ var utxos = this.getUTXOsForAddress(address)
164
+ return utxos.reduce(function(sum, utxo) {
165
+ return sum + (utxo.satoshis || 0)
166
+ }, 0)
167
+ }
168
+
169
+ BrowserUTXOManager.prototype.spendUTXOs = function(inputs, spentInTx) {
170
+ spentInTx = spentInTx || 'browser-spend'
171
+ var spentUTXOs = []
172
+
173
+ for (var i = 0; i < inputs.length; i++) {
174
+ var input = inputs[i]
175
+ var key = input.txid + ':' + input.vout
176
+ var utxo = this.utxos.get(key)
177
+
178
+ if (utxo && utxo.status === 'available') {
179
+ utxo.status = 'spent'
180
+ utxo.spentAt = new Date().toISOString()
181
+ utxo.spentInTx = spentInTx
182
+
183
+ this.spentUTXOs.set(key, {
184
+ txid: utxo.txid,
185
+ vout: utxo.vout,
186
+ spentInTx: spentInTx,
187
+ spentAt: utxo.spentAt
188
+ })
189
+
190
+ spentUTXOs.push(utxo)
191
+ }
192
+ }
193
+
194
+ this.updateMetadata()
195
+ if (this.options.autoSave) {
196
+ this.saveToStorage()
197
+ }
198
+
199
+ return spentUTXOs
200
+ }
201
+
202
+ BrowserUTXOManager.prototype.getUTXO = function(txid, vout) {
203
+ var key = txid + ':' + vout
204
+ var utxo = this.utxos.get(key)
205
+
206
+ return {
207
+ exists: !!utxo,
208
+ utxo: utxo,
209
+ status: utxo ? utxo.status : 'not-found'
210
+ }
211
+ }
212
+
213
+ BrowserUTXOManager.prototype.createMockUTXOs = function(address, count, satoshis) {
214
+ count = count || 5
215
+ satoshis = satoshis || 100000
216
+ var mockUTXOs = []
217
+
218
+ for (var i = 0; i < count; i++) {
219
+ // Generate random-like txid
220
+ var txid = ''
221
+ for (var j = 0; j < 64; j++) {
222
+ txid += Math.floor(Math.random() * 16).toString(16)
223
+ }
224
+
225
+ var mockUTXO = {
226
+ txid: txid,
227
+ vout: i,
228
+ address: address,
229
+ satoshis: satoshis + Math.floor(Math.random() * 10000),
230
+ script: '',
231
+ status: 'available'
232
+ }
233
+
234
+ this.addUTXO(mockUTXO)
235
+ mockUTXOs.push(mockUTXO)
236
+ }
237
+
238
+ return mockUTXOs
239
+ }
240
+
241
+ BrowserUTXOManager.prototype.getStats = function() {
242
+ var totalValue = 0
243
+ var availableCount = 0
244
+ var spentCount = 0
245
+
246
+ this.utxos.forEach(function(utxo) {
247
+ totalValue += utxo.satoshis || 0
248
+ if (utxo.status === 'available') {
249
+ availableCount++
250
+ } else if (utxo.status === 'spent') {
251
+ spentCount++
252
+ }
253
+ })
254
+
255
+ return {
256
+ totalUTXOs: this.utxos.size,
257
+ totalAvailable: availableCount,
258
+ totalSpent: spentCount,
259
+ totalValue: totalValue,
260
+ totalAddresses: this.addressIndex.size,
261
+ storageType: this.options.storage,
262
+ metadata: this.metadata
263
+ }
264
+ }
265
+
266
+ BrowserUTXOManager.prototype.updateMetadata = function() {
267
+ var stats = this.getStats()
268
+ this.metadata.totalUTXOs = stats.totalUTXOs
269
+ this.metadata.totalValue = stats.totalValue
270
+ this.metadata.lastModified = new Date().toISOString()
271
+ }
272
+
273
+ BrowserUTXOManager.prototype.exportData = function() {
274
+ return JSON.stringify({
275
+ utxos: Array.from(this.utxos.values()),
276
+ metadata: this.metadata,
277
+ version: '1.0.0',
278
+ exportedAt: new Date().toISOString()
279
+ })
280
+ }
281
+
282
+ BrowserUTXOManager.prototype.importData = function(jsonData, merge) {
283
+ merge = merge || false
284
+
285
+ try {
286
+ var data = JSON.parse(jsonData)
287
+
288
+ if (!merge) {
289
+ this.utxos.clear()
290
+ this.addressIndex.clear()
291
+ this.spentUTXOs.clear()
292
+ }
293
+
294
+ if (data.utxos && Array.isArray(data.utxos)) {
295
+ var self = this
296
+ data.utxos.forEach(function(utxo) {
297
+ self.addUTXO(utxo)
298
+ })
299
+ }
300
+
301
+ console.log('✅ BrowserUTXOManager: Imported ' + (data.utxos && data.utxos.length || 0) + ' UTXOs')
302
+ return true
303
+ } catch (e) {
304
+ console.error('Failed to import UTXO data:', e)
305
+ return false
306
+ }
307
+ }
308
+
309
+ // Export
310
+ if (typeof module !== 'undefined' && module.exports) {
311
+ module.exports = BrowserUTXOManager
312
+ module.exports.STORAGE_TYPES = STORAGE_TYPES
313
+ } else if (typeof window !== 'undefined') {
314
+ window.BrowserUTXOManager = BrowserUTXOManager
315
+ window.BrowserUTXOManager.STORAGE_TYPES = STORAGE_TYPES
316
+ }