@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.
- package/CHANGELOG.md +30 -21
- package/README.md +37 -35
- package/bsv-covenant.min.js +5 -5
- package/bsv-gdaf.min.js +6 -6
- package/bsv-ltp.min.js +6 -6
- package/bsv-smartcontract.min.js +5 -5
- package/bsv.bundle.js +5 -5
- package/bsv.min.js +5 -5
- package/demos/README.md +1 -1
- package/demos/browser-test.html +1208 -0
- package/demos/smart_contract_demo.html +1 -1
- package/demos/smart_contract_demo.js +1 -1
- package/demos/web3keys.html +3 -3
- package/docs/DOCUMENTATION_REVIEW_REPORT.md +11 -11
- package/docs/FIX_CREATEHMAC_ISSUE.md +1 -1
- package/docs/MODULE_REFERENCE_COMPLETE.md +28 -28
- package/docs/SMARTLEDGER_BSV_USAGE_ANSWERS.md +4 -4
- package/docs/SMARTLEDGER_BSV_USAGE_EXAMPLES.js +2 -2
- package/docs/SMARTLEDGER_BSV_USAGE_GUIDE.md +3 -3
- package/docs/SMART_CONTRACT_DEVELOPMENT_GUIDE.md +1 -1
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +2 -2
- package/docs/getting-started/INSTALLATION.md +25 -25
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/index.js +7 -0
- package/lib/browser-utxo-manager-es5.js +316 -0
- package/lib/browser-utxo-manager.js +533 -0
- package/package.json +3 -3
- package/tests/browser-compatibility/test-cdn-vs-local.html +2 -2
|
@@ -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
|
+
}
|