smartledger-bsv 3.3.4 → 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.
@@ -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
+ }