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,322 @@
1
+ /*
2
+ Hybrid Token Manager - Coordinates SLP and ALP token operations
3
+ Provides unified interface for both protocols
4
+ */
5
+
6
+ const TokenProtocolDetector = require('./token-protocol-detector')
7
+ const SLPTokenHandler = require('./slp-token-handler')
8
+ const ALPTokenHandler = require('./alp-token-handler')
9
+
10
+ class HybridTokenManager {
11
+ constructor (localConfig = {}) {
12
+ this.chronik = localConfig.chronik
13
+ this.ar = localConfig.ar
14
+
15
+ if (!this.chronik) {
16
+ throw new Error('Chronik client required for token operations')
17
+ }
18
+
19
+ if (!this.ar) {
20
+ throw new Error('AdapterRouter required for token operations')
21
+ }
22
+
23
+ // Initialize protocol handlers
24
+ this.slpHandler = new SLPTokenHandler(localConfig)
25
+ this.alpHandler = new ALPTokenHandler(localConfig)
26
+
27
+ // Cache for token metadata
28
+ this.tokenMetadataCache = new Map()
29
+ }
30
+
31
+ async sendTokens (tokenId, outputs, walletInfo, utxos, satsPerByte = 1.2) {
32
+ try {
33
+ // Detect protocol for this token
34
+ const protocol = await this._detectTokenProtocol(tokenId, utxos)
35
+
36
+ // Route to appropriate handler
37
+ switch (protocol) {
38
+ case 'SLP':
39
+ return await this.slpHandler.sendTokens(tokenId, outputs, walletInfo, utxos, satsPerByte)
40
+ case 'ALP':
41
+ return await this.alpHandler.sendTokens(tokenId, outputs, walletInfo, utxos, satsPerByte)
42
+ default:
43
+ throw new Error(`Unsupported token protocol: ${protocol}`)
44
+ }
45
+ } catch (err) {
46
+ throw new Error(`Token send failed: ${err.message}`)
47
+ }
48
+ }
49
+
50
+ async burnTokens (tokenId, amount, walletInfo, utxos, satsPerByte = 1.2) {
51
+ try {
52
+ // Detect protocol for this token
53
+ const protocol = await this._detectTokenProtocol(tokenId, utxos)
54
+
55
+ // Route to appropriate handler
56
+ switch (protocol) {
57
+ case 'SLP':
58
+ return await this.slpHandler.burnTokens(tokenId, amount, walletInfo, utxos, satsPerByte)
59
+ case 'ALP':
60
+ return await this.alpHandler.burnTokens(tokenId, amount, walletInfo, utxos, satsPerByte)
61
+ default:
62
+ throw new Error(`Unsupported token protocol: ${protocol}`)
63
+ }
64
+ } catch (err) {
65
+ throw new Error(`Token burn failed: ${err.message}`)
66
+ }
67
+ }
68
+
69
+ async burnAllTokens (tokenId, walletInfo, utxos) {
70
+ try {
71
+ // Get token metadata and calculate total balance
72
+ const tokenInfo = await this._getTokenInfo(tokenId)
73
+ const filtered = TokenProtocolDetector.filterUtxosForToken(utxos, tokenId)
74
+
75
+ if (filtered.tokenUtxos.length === 0) {
76
+ throw new Error(`No ${tokenInfo.genesisInfo.tokenTicker} tokens found to burn`)
77
+ }
78
+
79
+ // Calculate total balance in display units
80
+ const totalAtoms = filtered.tokenSummary.totalAtoms
81
+ const displayAmount = this._atomsToDisplay(totalAtoms, tokenInfo.genesisInfo.decimals)
82
+
83
+ // Burn all tokens
84
+ return await this.burnTokens(tokenId, displayAmount, walletInfo, utxos)
85
+ } catch (err) {
86
+ throw new Error(`Burn all tokens failed: ${err.message}`)
87
+ }
88
+ }
89
+
90
+ async listTokensFromAddress (address) {
91
+ try {
92
+ // Get UTXOs for the address using the adapter router
93
+ const utxoData = await this.ar.getUtxos(address)
94
+ const utxos = utxoData.utxos || []
95
+
96
+ // Use the existing listTokensFromUtxos method
97
+ return await this.listTokensFromUtxos(utxos)
98
+ } catch (err) {
99
+ throw new Error(`List tokens failed: ${err.message}`)
100
+ }
101
+ }
102
+
103
+ async listTokensFromUtxos (utxos) {
104
+ try {
105
+ const inventory = TokenProtocolDetector.getTokenInventory(utxos)
106
+ const tokens = []
107
+
108
+ for (const tokenEntry of inventory) {
109
+ try {
110
+ const tokenInfo = await this._getTokenInfo(tokenEntry.tokenId)
111
+ const filtered = TokenProtocolDetector.filterUtxosForToken(utxos, tokenEntry.tokenId)
112
+
113
+ // Calculate balance based on protocol
114
+ const displayBalance = this._atomsToDisplay(
115
+ filtered.tokenSummary.totalAtoms,
116
+ tokenInfo.genesisInfo.decimals
117
+ )
118
+
119
+ tokens.push({
120
+ tokenId: tokenEntry.tokenId,
121
+ protocol: tokenEntry.protocol,
122
+ ticker: tokenInfo.genesisInfo.tokenTicker,
123
+ name: tokenInfo.genesisInfo.tokenName,
124
+ decimals: tokenInfo.genesisInfo.decimals,
125
+ url: tokenInfo.genesisInfo.url,
126
+ balance: {
127
+ display: displayBalance,
128
+ atoms: filtered.tokenSummary.totalAtoms
129
+ },
130
+ utxoCount: filtered.tokenSummary.utxoCount,
131
+ utxos: filtered.tokenUtxos
132
+ })
133
+ } catch (err) {
134
+ console.warn(`Failed to process token ${tokenEntry.tokenId}: ${err.message}`)
135
+ }
136
+ }
137
+
138
+ return tokens
139
+ } catch (err) {
140
+ throw new Error(`List tokens from UTXOs failed: ${err.message}`)
141
+ }
142
+ }
143
+
144
+ async getTokenBalance (tokenId, utxos) {
145
+ try {
146
+ const filtered = TokenProtocolDetector.filterUtxosForToken(utxos, tokenId)
147
+
148
+ if (filtered.tokenUtxos.length === 0) {
149
+ // Try to get token info, but handle case where token doesn't exist
150
+ try {
151
+ const tokenInfo = await this._getTokenInfo(tokenId)
152
+ return {
153
+ tokenId,
154
+ protocol: tokenInfo.tokenType.protocol,
155
+ ticker: tokenInfo.genesisInfo.tokenTicker,
156
+ name: tokenInfo.genesisInfo.tokenName,
157
+ decimals: tokenInfo.genesisInfo.decimals,
158
+ balance: {
159
+ display: 0,
160
+ atoms: 0n
161
+ },
162
+ utxoCount: 0
163
+ }
164
+ } catch (err) {
165
+ // Token doesn't exist, return minimal info
166
+ return {
167
+ tokenId,
168
+ protocol: 'UNKNOWN',
169
+ ticker: 'UNKNOWN',
170
+ name: 'Unknown Token',
171
+ decimals: 0,
172
+ balance: {
173
+ display: 0,
174
+ atoms: 0n
175
+ },
176
+ utxoCount: 0
177
+ }
178
+ }
179
+ }
180
+
181
+ const tokenInfo = await this._getTokenInfo(tokenId)
182
+
183
+ const displayBalance = this._atomsToDisplay(
184
+ filtered.tokenSummary.totalAtoms,
185
+ tokenInfo.genesisInfo.decimals
186
+ )
187
+
188
+ return {
189
+ tokenId,
190
+ protocol: filtered.protocol,
191
+ ticker: tokenInfo.genesisInfo.tokenTicker,
192
+ name: tokenInfo.genesisInfo.tokenName,
193
+ decimals: tokenInfo.genesisInfo.decimals,
194
+ balance: {
195
+ display: displayBalance,
196
+ atoms: filtered.tokenSummary.totalAtoms
197
+ },
198
+ utxoCount: filtered.tokenSummary.utxoCount
199
+ }
200
+ } catch (err) {
201
+ throw new Error(`Get token balance failed: ${err.message}`)
202
+ }
203
+ }
204
+
205
+ async getTokenData (tokenId, withTxHistory = false, sortOrder = 'DESCENDING') {
206
+ try {
207
+ // Get basic token metadata
208
+ const tokenInfo = await this._getTokenInfo(tokenId)
209
+
210
+ const result = {
211
+ tokenId,
212
+ protocol: tokenInfo.tokenType.protocol,
213
+ type: tokenInfo.tokenType.type,
214
+ ticker: tokenInfo.genesisInfo.tokenTicker,
215
+ name: tokenInfo.genesisInfo.tokenName,
216
+ decimals: tokenInfo.genesisInfo.decimals,
217
+ url: tokenInfo.genesisInfo.url,
218
+ data: tokenInfo.genesisInfo.data,
219
+ authPubkey: tokenInfo.genesisInfo.authPubkey,
220
+ timeFirstSeen: tokenInfo.timeFirstSeen
221
+ }
222
+
223
+ // Add transaction history if requested
224
+ if (withTxHistory) {
225
+ // This would require additional chronik calls
226
+ // For now, just note that it's not implemented
227
+ result.txHistory = 'Transaction history not implemented yet'
228
+ }
229
+
230
+ return result
231
+ } catch (err) {
232
+ throw new Error(`Get token data failed: ${err.message}`)
233
+ }
234
+ }
235
+
236
+ // Utility methods
237
+
238
+ getProtocolStats (utxos) {
239
+ return TokenProtocolDetector.getProtocolStats(utxos)
240
+ }
241
+
242
+ hasTokens (utxos) {
243
+ return TokenProtocolDetector.hasTokens(utxos)
244
+ }
245
+
246
+ hasProtocolTokens (utxos, protocol) {
247
+ return TokenProtocolDetector.hasProtocolTokens(utxos, protocol)
248
+ }
249
+
250
+ categorizeUtxos (utxos) {
251
+ return TokenProtocolDetector.categorizeUtxos(utxos)
252
+ }
253
+
254
+ // Private helper methods
255
+
256
+ async _detectTokenProtocol (tokenId, utxos) {
257
+ // First try to detect from UTXOs
258
+ const tokenUtxo = utxos.find(utxo =>
259
+ utxo.token && utxo.token.tokenId === tokenId
260
+ )
261
+
262
+ if (tokenUtxo) {
263
+ return TokenProtocolDetector.detectProtocol(tokenUtxo)
264
+ }
265
+
266
+ // Fallback to metadata lookup
267
+ try {
268
+ const tokenInfo = await this._getTokenInfo(tokenId)
269
+ return TokenProtocolDetector.detectProtocolFromMetadata(tokenInfo)
270
+ } catch (err) {
271
+ throw new Error(`Cannot determine protocol for token ${tokenId}: ${err.message}`)
272
+ }
273
+ }
274
+
275
+ async _getTokenInfo (tokenId) {
276
+ // Check cache first
277
+ if (this.tokenMetadataCache.has(tokenId)) {
278
+ return this.tokenMetadataCache.get(tokenId)
279
+ }
280
+
281
+ // Fetch from chronik
282
+ try {
283
+ const tokenInfo = await this.chronik.token(tokenId)
284
+ this.tokenMetadataCache.set(tokenId, tokenInfo)
285
+ return tokenInfo
286
+ } catch (err) {
287
+ throw new Error(`Failed to fetch token metadata: ${err.message}`)
288
+ }
289
+ }
290
+
291
+ _atomsToDisplay (atoms, decimals) {
292
+ if (decimals === 0) {
293
+ return Number(atoms)
294
+ }
295
+
296
+ return Number(atoms) / Math.pow(10, decimals)
297
+ }
298
+
299
+ _displayToAtoms (displayAmount, decimals) {
300
+ if (decimals === 0) {
301
+ return BigInt(Math.floor(displayAmount))
302
+ }
303
+
304
+ const atoms = Math.floor(displayAmount * Math.pow(10, decimals))
305
+ return BigInt(atoms)
306
+ }
307
+
308
+ // Clear metadata cache
309
+ clearCache () {
310
+ this.tokenMetadataCache.clear()
311
+ }
312
+
313
+ // Get cache statistics
314
+ getCacheStats () {
315
+ return {
316
+ cachedTokens: this.tokenMetadataCache.size,
317
+ tokenIds: Array.from(this.tokenMetadataCache.keys())
318
+ }
319
+ }
320
+ }
321
+
322
+ module.exports = HybridTokenManager