@zubari/sdk 0.2.8 → 0.3.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 (39) hide show
  1. package/dist/{TransactionService-8xSEGoWA.d.mts → TransactionService-DURp3bRL.d.ts} +34 -10
  2. package/dist/{TransactionService-CaIcCoqY.d.ts → TransactionService-DuMJmrG3.d.mts} +34 -10
  3. package/dist/{WalletManager-CCs4Jsv7.d.ts → WalletManager-CEjN-YBF.d.ts} +12 -12
  4. package/dist/{WalletManager-B1qvFF4K.d.mts → WalletManager-bo35aHOJ.d.mts} +12 -12
  5. package/dist/{index-BOc9U2TK.d.mts → index-BpjMiC3n.d.ts} +22 -11
  6. package/dist/{index-Cx389p_j.d.mts → index-DF0Gf8NK.d.mts} +7 -1
  7. package/dist/{index-Cx389p_j.d.ts → index-DF0Gf8NK.d.ts} +7 -1
  8. package/dist/{index-Cqrpp3XA.d.ts → index-DJnsirV5.d.mts} +22 -11
  9. package/dist/index.d.mts +4 -4
  10. package/dist/index.d.ts +4 -4
  11. package/dist/index.js +3064 -1770
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +3064 -1770
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/protocols/index.d.mts +54 -22
  16. package/dist/protocols/index.d.ts +54 -22
  17. package/dist/protocols/index.js +1008 -76
  18. package/dist/protocols/index.js.map +1 -1
  19. package/dist/protocols/index.mjs +1008 -76
  20. package/dist/protocols/index.mjs.map +1 -1
  21. package/dist/react/index.d.mts +3 -3
  22. package/dist/react/index.d.ts +3 -3
  23. package/dist/react/index.js +884 -884
  24. package/dist/react/index.js.map +1 -1
  25. package/dist/react/index.mjs +884 -884
  26. package/dist/react/index.mjs.map +1 -1
  27. package/dist/services/index.d.mts +2 -2
  28. package/dist/services/index.d.ts +2 -2
  29. package/dist/services/index.js +152 -75
  30. package/dist/services/index.js.map +1 -1
  31. package/dist/services/index.mjs +152 -75
  32. package/dist/services/index.mjs.map +1 -1
  33. package/dist/wallet/index.d.mts +3 -3
  34. package/dist/wallet/index.d.ts +3 -3
  35. package/dist/wallet/index.js +1352 -1105
  36. package/dist/wallet/index.js.map +1 -1
  37. package/dist/wallet/index.mjs +1352 -1105
  38. package/dist/wallet/index.mjs.map +1 -1
  39. package/package.json +13 -12
@@ -1,14 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  var ethers = require('ethers');
4
- var viem = require('viem');
5
- var chains = require('viem/chains');
6
4
  var bip39 = require('@scure/bip39');
7
5
  var english = require('@scure/bip39/wordlists/english');
8
6
  var bip32 = require('@scure/bip32');
9
7
  var base = require('@scure/base');
10
8
  var sha256 = require('@noble/hashes/sha256');
11
9
  var ripemd160 = require('@noble/hashes/ripemd160');
10
+ var viem = require('viem');
11
+ var chains = require('viem/chains');
12
12
 
13
13
  // src/config/networks.ts
14
14
  var NETWORKS = {
@@ -180,1292 +180,1539 @@ function getContractAddresses(network) {
180
180
  return ZUBARI_CONTRACTS[network];
181
181
  }
182
182
 
183
- // src/wallet/ZubariWallet.ts
184
- var ZubariWallet = class {
185
- seed;
183
+ // src/services/WdkApiClient.ts
184
+ var WdkApiClient = class {
186
185
  config;
187
- accounts = /* @__PURE__ */ new Map();
188
- initialized = false;
189
- constructor(seed, config) {
190
- this.seed = seed;
186
+ constructor(config) {
191
187
  this.config = {
192
- network: config.network || "mainnet",
193
- enabledNetworks: config.enabledNetworks || ["ethereum"],
194
- gasless: config.gasless ?? false,
195
- paymasterUrl: config.paymasterUrl,
196
- bundlerUrl: config.bundlerUrl,
197
- rpcUrls: config.rpcUrls
198
- };
199
- }
200
- /**
201
- * Initialize the wallet by deriving accounts for all enabled networks
202
- */
203
- async initialize() {
204
- if (this.initialized) return;
205
- for (const network of this.config.enabledNetworks) {
206
- await this.deriveAccount(network);
207
- }
208
- this.initialized = true;
209
- }
210
- /**
211
- * Derive account for a specific network using BIP-44
212
- */
213
- async deriveAccount(network, index = 0) {
214
- getNetworkConfig(network, this.config.network === "testnet");
215
- const basePath = DERIVATION_PATHS[network];
216
- const derivationPath = `${basePath}/${index}`;
217
- const account = {
218
- network,
219
- address: "",
220
- // Will be derived using WDK
221
- publicKey: "",
222
- derivationPath
188
+ baseUrl: config.baseUrl,
189
+ timeout: config.timeout || 3e4
223
190
  };
224
- this.accounts.set(network, account);
225
- return account;
226
191
  }
227
192
  /**
228
- * Get account for a specific network
193
+ * Generate a new BIP-39 seed phrase using Tether WDK
229
194
  */
230
- async getAccount(network, index = 0) {
231
- const existing = this.accounts.get(network);
232
- if (existing && existing.derivationPath.endsWith(`/${index}`)) {
233
- return existing;
195
+ async generateSeed() {
196
+ try {
197
+ const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/generate-seed`, {
198
+ method: "POST",
199
+ headers: {
200
+ "Content-Type": "application/json"
201
+ }
202
+ });
203
+ return await response.json();
204
+ } catch (error) {
205
+ return {
206
+ success: false,
207
+ error: error instanceof Error ? error.message : "Failed to generate seed"
208
+ };
234
209
  }
235
- return this.deriveAccount(network, index);
236
- }
237
- /**
238
- * Get address for a specific network
239
- */
240
- async getAddress(network) {
241
- const account = await this.getAccount(network);
242
- return account.address;
243
210
  }
244
211
  /**
245
- * Get all addresses for enabled networks
212
+ * Validate a BIP-39 seed phrase
246
213
  */
247
- async getAllAddresses() {
248
- const addresses = {};
249
- for (const network of this.config.enabledNetworks) {
250
- addresses[network] = await this.getAddress(network);
214
+ async validateSeed(seed) {
215
+ try {
216
+ const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/validate-seed`, {
217
+ method: "POST",
218
+ headers: {
219
+ "Content-Type": "application/json"
220
+ },
221
+ body: JSON.stringify({ seed })
222
+ });
223
+ return await response.json();
224
+ } catch (error) {
225
+ return {
226
+ success: false,
227
+ error: error instanceof Error ? error.message : "Failed to validate seed"
228
+ };
251
229
  }
252
- return addresses;
253
- }
254
- /**
255
- * Get balance for a specific network
256
- */
257
- async getBalance(network) {
258
- const networkConfig = getNetworkConfig(network, this.config.network === "testnet");
259
- return {
260
- network,
261
- native: {
262
- symbol: networkConfig.nativeCurrency.symbol,
263
- balance: BigInt(0),
264
- balanceFormatted: "0",
265
- balanceUsd: 0
266
- },
267
- tokens: []
268
- };
269
230
  }
270
231
  /**
271
- * Get balances for all enabled networks
232
+ * Derive address for a specific chain using Tether WDK
272
233
  */
273
- async getAllBalances() {
274
- const balances = [];
275
- for (const network of this.config.enabledNetworks) {
276
- balances.push(await this.getBalance(network));
234
+ async deriveAddress(seed, chain, network = "testnet") {
235
+ try {
236
+ const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/derive-address`, {
237
+ method: "POST",
238
+ headers: {
239
+ "Content-Type": "application/json"
240
+ },
241
+ body: JSON.stringify({ seed, chain, network })
242
+ });
243
+ return await response.json();
244
+ } catch (error) {
245
+ return {
246
+ success: false,
247
+ error: error instanceof Error ? error.message : "Failed to derive address"
248
+ };
277
249
  }
278
- return balances;
279
250
  }
280
251
  /**
281
- * Get total portfolio value in USD
252
+ * Derive addresses for all chains using Tether WDK
282
253
  */
283
- async getTotalPortfolioUsd() {
284
- const balances = await this.getAllBalances();
285
- let total = 0;
286
- for (const balance of balances) {
287
- total += balance.native.balanceUsd;
288
- for (const token of balance.tokens) {
289
- total += token.balanceUsd;
290
- }
254
+ async deriveAllAddresses(seed, network = "testnet") {
255
+ try {
256
+ const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/derive-all`, {
257
+ method: "POST",
258
+ headers: {
259
+ "Content-Type": "application/json"
260
+ },
261
+ body: JSON.stringify({ seed, network })
262
+ });
263
+ return await response.json();
264
+ } catch (error) {
265
+ return {
266
+ success: false,
267
+ error: error instanceof Error ? error.message : "Failed to derive addresses"
268
+ };
291
269
  }
292
- return total;
293
- }
294
- /**
295
- * Send native currency on a specific network
296
- */
297
- async send(network, params) {
298
- const { to, amount, gasless } = params;
299
- gasless ?? (this.config.gasless && network === "ethereum");
300
- return {
301
- hash: "",
302
- network,
303
- status: "pending"
304
- };
305
270
  }
306
271
  /**
307
- * Send ERC-20 token on EVM networks
272
+ * Send a transaction on a specific chain using Tether WDK
308
273
  */
309
- async sendToken(network, token, to, amount) {
310
- const networkConfig = getNetworkConfig(network, this.config.network === "testnet");
311
- if (!networkConfig.isEvm) {
312
- throw new Error(`sendToken is only supported on EVM networks. ${network} is not an EVM chain.`);
274
+ async sendTransaction(seed, chain, to, amount, network = "testnet") {
275
+ try {
276
+ const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/send`, {
277
+ method: "POST",
278
+ headers: {
279
+ "Content-Type": "application/json"
280
+ },
281
+ body: JSON.stringify({ seed, chain, to, amount, network })
282
+ });
283
+ return await response.json();
284
+ } catch (error) {
285
+ return {
286
+ success: false,
287
+ error: error instanceof Error ? error.message : "Failed to send transaction"
288
+ };
313
289
  }
314
- return {
315
- hash: "",
316
- network,
317
- status: "pending"
318
- };
319
290
  }
320
291
  /**
321
- * Send Bitcoin on-chain using WalletManagerBtc
322
- * @param to - Destination address (bc1q... for native segwit)
323
- * @param amount - Amount in satoshis
292
+ * Get transaction history for an address on a specific chain
293
+ * Fetches from blockchain explorers (Etherscan, mempool.space, etc.)
324
294
  */
325
- async sendBitcoin(to, amount) {
326
- if (!this.isValidBitcoinAddress(to)) {
327
- throw new Error("Invalid Bitcoin address. Expected bc1q... (native segwit) format.");
295
+ async getTransactionHistory(seed, chain, network = "testnet", limit = 10) {
296
+ try {
297
+ const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/history`, {
298
+ method: "POST",
299
+ headers: {
300
+ "Content-Type": "application/json"
301
+ },
302
+ body: JSON.stringify({ seed, chain, network, limit })
303
+ });
304
+ return await response.json();
305
+ } catch (error) {
306
+ return {
307
+ success: false,
308
+ error: error instanceof Error ? error.message : "Failed to get transaction history"
309
+ };
328
310
  }
329
- return this.send("bitcoin", { to, amount });
330
311
  }
331
312
  /**
332
- * Validate Bitcoin address format
313
+ * Get transaction status by hash
314
+ * Fetches from blockchain explorers to check confirmation status
333
315
  */
334
- isValidBitcoinAddress(address) {
335
- if (address.startsWith("bc1q") || address.startsWith("tb1q")) {
336
- return address.length >= 42 && address.length <= 62;
337
- }
338
- if (address.startsWith("1") || address.startsWith("m") || address.startsWith("n")) {
339
- return address.length >= 25 && address.length <= 34;
340
- }
341
- if (address.startsWith("3") || address.startsWith("2")) {
342
- return address.length >= 25 && address.length <= 34;
316
+ async getTransactionStatus(txHash, chain, network = "testnet") {
317
+ try {
318
+ const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/tx-status`, {
319
+ method: "POST",
320
+ headers: {
321
+ "Content-Type": "application/json"
322
+ },
323
+ body: JSON.stringify({ txHash, chain, network })
324
+ });
325
+ return await response.json();
326
+ } catch (error) {
327
+ return {
328
+ success: false,
329
+ error: error instanceof Error ? error.message : "Failed to get transaction status"
330
+ };
343
331
  }
344
- return false;
345
- }
346
- /**
347
- * Get Bitcoin address (native segwit bc1q...)
348
- */
349
- async getBitcoinAddress() {
350
- const account = await this.getAccount("bitcoin");
351
- return account.address;
352
332
  }
353
- /**
354
- * Pay Lightning invoice via Spark network
355
- * Uses WDK WalletManagerSpark (m/44'/998')
356
- * @param invoice - Lightning invoice string (lnbc...)
357
- */
358
- async sendLightning(invoice) {
359
- if (!this.isValidLightningInvoice(invoice)) {
360
- throw new Error("Invalid Lightning invoice format. Expected lnbc... or lntb...");
361
- }
362
- const invoiceDetails = this.decodeLightningInvoice(invoice);
363
- return {
364
- hash: "",
365
- // Will be payment hash from Spark
366
- network: "spark",
367
- status: "pending",
368
- metadata: {
369
- invoice,
370
- amount: invoiceDetails.amount,
371
- destination: invoiceDetails.destination
372
- }
373
- };
333
+ };
334
+ var DEFAULT_API_URL = process.env.NEXT_PUBLIC_API_URL || "https://ckgwifsxka.us-east-2.awsapprunner.com";
335
+ var wdkApiClient = null;
336
+ function getWdkApiClient(baseUrl) {
337
+ if (!wdkApiClient || baseUrl && wdkApiClient["config"].baseUrl !== baseUrl) {
338
+ wdkApiClient = new WdkApiClient({
339
+ baseUrl: baseUrl || DEFAULT_API_URL
340
+ });
374
341
  }
375
- /**
376
- * Create Lightning invoice via Spark
377
- * @param amount - Amount in millisatoshis
378
- * @param memo - Optional payment memo
379
- * @returns Lightning invoice string (lnbc...)
380
- */
381
- async createLightningInvoice(amount, memo) {
382
- if (amount <= 0) {
383
- throw new Error("Invoice amount must be greater than 0");
342
+ return wdkApiClient;
343
+ }
344
+ var DERIVATION_PATHS2 = {
345
+ ethereum: "m/44'/60'/0'/0/0",
346
+ bitcoin_mainnet: "m/84'/0'/0'/0/0",
347
+ bitcoin_testnet: "m/84'/1'/0'/0/0",
348
+ ton: "m/44'/607'/0'/0'/0'",
349
+ tron: "m/44'/195'/0'/0/0",
350
+ solana: "m/44'/501'/0'/0'",
351
+ spark: "m/44'/998'/0'/0/0"
352
+ };
353
+ function deriveEthereumAddress(seed) {
354
+ const hdNode = ethers.HDNodeWallet.fromPhrase(seed, void 0, DERIVATION_PATHS2.ethereum);
355
+ return hdNode.address;
356
+ }
357
+ function deriveBitcoinAddress(seed, network = "testnet") {
358
+ try {
359
+ const seedBytes = bip39.mnemonicToSeedSync(seed);
360
+ const hdKey = bip32.HDKey.fromMasterSeed(seedBytes);
361
+ const path = network === "testnet" ? DERIVATION_PATHS2.bitcoin_testnet : DERIVATION_PATHS2.bitcoin_mainnet;
362
+ const child = hdKey.derive(path);
363
+ if (!child.publicKey) {
364
+ throw new Error("Failed to derive public key");
384
365
  }
385
- const isTestnet = this.config.network === "testnet";
386
- const prefix = isTestnet ? "lntb" : "lnbc";
387
- return `${prefix}${amount}m1...`;
366
+ const pubKeyHash = ripemd160.ripemd160(sha256.sha256(child.publicKey));
367
+ const witnessVersion = 0;
368
+ const words = base.bech32.toWords(pubKeyHash);
369
+ words.unshift(witnessVersion);
370
+ const hrp = network === "testnet" ? "tb" : "bc";
371
+ const address = base.bech32.encode(hrp, words);
372
+ return address;
373
+ } catch (error) {
374
+ console.error("Bitcoin address derivation failed:", error);
375
+ throw error;
388
376
  }
389
- /**
390
- * Validate Lightning invoice format
391
- */
392
- isValidLightningInvoice(invoice) {
393
- const lowerInvoice = invoice.toLowerCase();
394
- return lowerInvoice.startsWith("lnbc") || // Mainnet
395
- lowerInvoice.startsWith("lntb") || // Testnet
396
- lowerInvoice.startsWith("lnbcrt");
377
+ }
378
+ async function deriveSolanaAddress(seed) {
379
+ try {
380
+ const [ed25519, nacl, bs58Module] = await Promise.all([
381
+ import('ed25519-hd-key'),
382
+ import('tweetnacl'),
383
+ import('bs58')
384
+ ]);
385
+ const bs58 = bs58Module.default || bs58Module;
386
+ const seedBytes = bip39.mnemonicToSeedSync(seed);
387
+ const derived = ed25519.derivePath(DERIVATION_PATHS2.solana, Buffer.from(seedBytes).toString("hex"));
388
+ const keypair = nacl.sign.keyPair.fromSeed(new Uint8Array(derived.key));
389
+ return bs58.encode(keypair.publicKey);
390
+ } catch (error) {
391
+ console.error("Solana address derivation failed:", error);
392
+ throw error;
397
393
  }
398
- /**
399
- * Decode Lightning invoice (basic parsing)
400
- */
401
- decodeLightningInvoice(invoice) {
402
- return {
403
- amount: BigInt(0),
404
- destination: "",
405
- expiry: 3600
406
- };
394
+ }
395
+ async function deriveTonAddress(seed) {
396
+ try {
397
+ const [ed25519, nacl] = await Promise.all([
398
+ import('ed25519-hd-key'),
399
+ import('tweetnacl')
400
+ ]);
401
+ const seedBytes = bip39.mnemonicToSeedSync(seed);
402
+ const derived = ed25519.derivePath(DERIVATION_PATHS2.ton, Buffer.from(seedBytes).toString("hex"));
403
+ const keypair = nacl.sign.keyPair.fromSeed(new Uint8Array(derived.key));
404
+ const publicKey = keypair.publicKey;
405
+ const workchain = 0;
406
+ const flags = 17;
407
+ const hash = sha256.sha256(publicKey);
408
+ const addressData = new Uint8Array(34);
409
+ addressData[0] = flags;
410
+ addressData[1] = workchain;
411
+ addressData.set(hash, 2);
412
+ const crc = crc16(addressData);
413
+ const fullAddress = new Uint8Array(36);
414
+ fullAddress.set(addressData);
415
+ fullAddress[34] = crc >> 8 & 255;
416
+ fullAddress[35] = crc & 255;
417
+ const base64 = btoa(String.fromCharCode(...fullAddress)).replace(/\+/g, "-").replace(/\//g, "_");
418
+ return base64;
419
+ } catch (error) {
420
+ console.error("TON address derivation failed:", error);
421
+ throw error;
407
422
  }
408
- /**
409
- * Get Lightning (Spark) balance
410
- */
411
- async getLightningBalance() {
412
- return {
413
- available: BigInt(0),
414
- pending: BigInt(0)
415
- };
423
+ }
424
+ function crc16(data) {
425
+ let crc = 0;
426
+ for (const byte of data) {
427
+ crc ^= byte << 8;
428
+ for (let i = 0; i < 8; i++) {
429
+ crc = crc & 32768 ? crc << 1 ^ 4129 : crc << 1;
430
+ crc &= 65535;
431
+ }
416
432
  }
417
- /**
418
- * Get configuration
419
- */
420
- getConfig() {
421
- return { ...this.config };
433
+ return crc;
434
+ }
435
+ function deriveTronAddress(seed) {
436
+ try {
437
+ const hdNode = ethers.HDNodeWallet.fromPhrase(seed, void 0, DERIVATION_PATHS2.tron);
438
+ const ethAddressHex = hdNode.address.slice(2).toLowerCase();
439
+ const addressBytes = new Uint8Array(21);
440
+ addressBytes[0] = 65;
441
+ for (let i = 0; i < 20; i++) {
442
+ addressBytes[i + 1] = parseInt(ethAddressHex.slice(i * 2, i * 2 + 2), 16);
443
+ }
444
+ const tronBase58check = base.base58check(sha256.sha256);
445
+ return tronBase58check.encode(addressBytes);
446
+ } catch (error) {
447
+ console.error("TRON address derivation failed:", error);
448
+ throw error;
422
449
  }
423
- /**
424
- * Check if wallet is initialized
425
- */
426
- isInitialized() {
427
- return this.initialized;
450
+ }
451
+ function deriveSparkAddress(seed, network = "testnet") {
452
+ try {
453
+ const seedBytes = bip39.mnemonicToSeedSync(seed);
454
+ const hdKey = bip32.HDKey.fromMasterSeed(seedBytes);
455
+ const child = hdKey.derive(DERIVATION_PATHS2.spark);
456
+ if (!child.publicKey) {
457
+ throw new Error("Failed to derive public key");
458
+ }
459
+ const pubKeyHash = ripemd160.ripemd160(sha256.sha256(child.publicKey));
460
+ const witnessVersion = 0;
461
+ const words = base.bech32.toWords(pubKeyHash);
462
+ words.unshift(witnessVersion);
463
+ const hrp = network === "testnet" ? "tsp" : "sp";
464
+ const address = base.bech32.encode(hrp, words);
465
+ return address;
466
+ } catch (error) {
467
+ console.error("Spark address derivation failed:", error);
468
+ throw error;
428
469
  }
429
- /**
430
- * Get contract addresses for current network
431
- */
432
- getContractAddresses() {
433
- return getContractAddresses(this.config.network);
470
+ }
471
+ async function deriveAllAddresses(seed, network = "testnet") {
472
+ const addresses = {
473
+ ethereum: null,
474
+ bitcoin: null,
475
+ ton: null,
476
+ tron: null,
477
+ solana: null,
478
+ spark: null
479
+ };
480
+ try {
481
+ addresses.ethereum = deriveEthereumAddress(seed);
482
+ } catch (e) {
483
+ console.error("ETH derivation failed:", e);
434
484
  }
435
- };
436
-
437
- // src/security/KeyManager.ts
438
- var KeyManager = class {
439
- static ALGORITHM = "AES-GCM";
440
- static KEY_LENGTH = 256;
441
- static IV_LENGTH = 12;
442
- static SALT_LENGTH = 16;
443
- static PBKDF2_ITERATIONS = 1e5;
444
- /**
445
- * Encrypt a seed phrase with a password
446
- */
447
- static async encryptSeed(seed, password) {
448
- const encoder = new TextEncoder();
449
- const seedData = encoder.encode(seed);
450
- const salt = crypto.getRandomValues(new Uint8Array(this.SALT_LENGTH));
451
- const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));
452
- const key = await this.deriveKey(password, salt);
453
- const encrypted = await crypto.subtle.encrypt(
454
- { name: this.ALGORITHM, iv },
455
- key,
456
- seedData
457
- );
458
- const combined = new Uint8Array(salt.length + iv.length + encrypted.byteLength);
459
- combined.set(salt, 0);
460
- combined.set(iv, salt.length);
461
- combined.set(new Uint8Array(encrypted), salt.length + iv.length);
462
- return btoa(String.fromCharCode(...combined));
485
+ try {
486
+ addresses.bitcoin = deriveBitcoinAddress(seed, network);
487
+ } catch (e) {
488
+ console.error("BTC derivation failed:", e);
463
489
  }
464
- /**
465
- * Decrypt a seed phrase with a password
466
- */
467
- static async decryptSeed(encryptedData, password) {
468
- const combined = new Uint8Array(
469
- atob(encryptedData).split("").map((c) => c.charCodeAt(0))
470
- );
471
- const salt = combined.slice(0, this.SALT_LENGTH);
472
- const iv = combined.slice(this.SALT_LENGTH, this.SALT_LENGTH + this.IV_LENGTH);
473
- const encrypted = combined.slice(this.SALT_LENGTH + this.IV_LENGTH);
474
- const key = await this.deriveKey(password, salt);
475
- const decrypted = await crypto.subtle.decrypt(
476
- { name: this.ALGORITHM, iv },
477
- key,
478
- encrypted
479
- );
480
- const decoder = new TextDecoder();
481
- return decoder.decode(decrypted);
490
+ try {
491
+ addresses.spark = deriveSparkAddress(seed, network);
492
+ } catch (e) {
493
+ console.error("Spark derivation failed:", e);
482
494
  }
483
- /**
484
- * Derive encryption key from password using PBKDF2
485
- */
486
- static async deriveKey(password, salt) {
487
- const encoder = new TextEncoder();
488
- const passwordData = encoder.encode(password);
489
- const keyMaterial = await crypto.subtle.importKey(
490
- "raw",
491
- passwordData,
492
- "PBKDF2",
493
- false,
494
- ["deriveKey"]
495
- );
496
- return crypto.subtle.deriveKey(
497
- {
498
- name: "PBKDF2",
499
- salt: salt.buffer.slice(salt.byteOffset, salt.byteOffset + salt.byteLength),
500
- iterations: this.PBKDF2_ITERATIONS,
501
- hash: "SHA-256"
502
- },
503
- keyMaterial,
504
- { name: this.ALGORITHM, length: this.KEY_LENGTH },
505
- false,
506
- ["encrypt", "decrypt"]
507
- );
495
+ try {
496
+ addresses.tron = deriveTronAddress(seed);
497
+ } catch (e) {
498
+ console.error("TRON derivation failed:", e);
508
499
  }
509
- /**
510
- * Validate a BIP-39 seed phrase (basic validation)
511
- */
512
- static validateSeedPhrase(seed) {
513
- const words = seed.trim().split(/\s+/);
514
- const validWordCounts = [12, 15, 18, 21, 24];
515
- return validWordCounts.includes(words.length);
500
+ const [solResult, tonResult] = await Promise.allSettled([
501
+ deriveSolanaAddress(seed),
502
+ deriveTonAddress(seed)
503
+ ]);
504
+ if (solResult.status === "fulfilled") {
505
+ addresses.solana = solResult.value;
506
+ } else {
507
+ console.error("SOL derivation failed:", solResult.reason);
516
508
  }
517
- /**
518
- * Generate a random encryption key (for backup purposes)
519
- */
520
- static generateBackupKey() {
521
- const bytes = crypto.getRandomValues(new Uint8Array(32));
522
- return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
509
+ if (tonResult.status === "fulfilled") {
510
+ addresses.ton = tonResult.value;
511
+ } else {
512
+ console.error("TON derivation failed:", tonResult.reason);
523
513
  }
524
- };
514
+ return addresses;
515
+ }
516
+ function isValidSeed(seed) {
517
+ return bip39.validateMnemonic(seed, english.wordlist);
518
+ }
519
+ function generateSeedPhrase() {
520
+ return bip39.generateMnemonic(english.wordlist);
521
+ }
525
522
 
526
- // src/storage/SecureStorage.ts
527
- var KeychainStorageAdapter = class {
528
- serviceName;
529
- constructor(serviceName = "com.zubari.wallet") {
530
- this.serviceName = serviceName;
523
+ // src/services/ZubariWdkService.ts
524
+ var DEFAULT_API_URL2 = "https://ckgwifsxka.us-east-2.awsapprunner.com";
525
+ function isBrowser() {
526
+ return typeof window !== "undefined" && typeof window.document !== "undefined";
527
+ }
528
+ var dynamicImport = new Function("specifier", "return import(specifier)");
529
+ async function canUseNativeWdk() {
530
+ if (isBrowser()) {
531
+ return false;
531
532
  }
532
- async setItem(key, value) {
533
- if (typeof global !== "undefined" && global.KeychainModule) {
534
- await global.KeychainModule.setItem(this.serviceName, key, value);
535
- } else {
536
- throw new Error("Keychain not available on this platform");
537
- }
533
+ try {
534
+ await dynamicImport("@tetherto/wdk");
535
+ return true;
536
+ } catch {
537
+ return false;
538
538
  }
539
- async getItem(key) {
540
- if (typeof global !== "undefined" && global.KeychainModule) {
541
- return global.KeychainModule.getItem(this.serviceName, key);
542
- }
543
- throw new Error("Keychain not available on this platform");
539
+ }
540
+ var ZubariWdkService = class {
541
+ config;
542
+ apiClient;
543
+ nativeWdkService = null;
544
+ initialized = false;
545
+ useNativeWdk = false;
546
+ constructor(config = {}) {
547
+ this.config = {
548
+ network: config.network || "testnet",
549
+ apiUrl: config.apiUrl || process.env.NEXT_PUBLIC_API_URL || DEFAULT_API_URL2,
550
+ forceApi: config.forceApi ?? false,
551
+ timeout: config.timeout || 3e4
552
+ };
553
+ this.apiClient = getWdkApiClient(this.config.apiUrl);
544
554
  }
545
- async removeItem(key) {
546
- if (typeof global !== "undefined" && global.KeychainModule) {
547
- await global.KeychainModule.removeItem(this.serviceName, key);
548
- } else {
549
- throw new Error("Keychain not available on this platform");
555
+ /**
556
+ * Initialize the service and determine the best strategy
557
+ */
558
+ async initialize() {
559
+ if (this.initialized) return;
560
+ if (isBrowser() || this.config.forceApi) {
561
+ this.useNativeWdk = false;
562
+ this.initialized = true;
563
+ return;
550
564
  }
551
- }
552
- async hasItem(key) {
553
- const value = await this.getItem(key);
554
- return value !== null;
555
- }
556
- async clear() {
557
- if (typeof global !== "undefined" && global.KeychainModule) {
558
- await global.KeychainModule.clear(this.serviceName);
559
- } else {
560
- throw new Error("Keychain not available on this platform");
565
+ if (await canUseNativeWdk()) {
566
+ try {
567
+ const WdkServiceModule = await dynamicImport("./WdkService");
568
+ const WdkService = WdkServiceModule.WdkService || WdkServiceModule.default;
569
+ this.nativeWdkService = new WdkService({
570
+ network: this.config.network
571
+ });
572
+ this.useNativeWdk = true;
573
+ } catch (error) {
574
+ console.warn("Failed to initialize native WDK, falling back to API:", error);
575
+ this.useNativeWdk = false;
576
+ }
561
577
  }
578
+ this.initialized = true;
562
579
  }
563
- };
564
- var KeystoreStorageAdapter = class {
565
- alias;
566
- constructor(alias = "zubari_wallet_keys") {
567
- this.alias = alias;
580
+ /**
581
+ * Get the current execution mode
582
+ */
583
+ getMode() {
584
+ if (this.useNativeWdk) return "native";
585
+ if (isBrowser()) return "api";
586
+ return "api";
568
587
  }
569
- async setItem(key, value) {
570
- if (typeof global !== "undefined" && global.KeystoreModule) {
571
- await global.KeystoreModule.setItem(this.alias, key, value);
572
- } else {
573
- throw new Error("Keystore not available on this platform");
574
- }
588
+ /**
589
+ * Check if running in browser
590
+ */
591
+ isBrowserEnvironment() {
592
+ return isBrowser();
575
593
  }
576
- async getItem(key) {
577
- if (typeof global !== "undefined" && global.KeystoreModule) {
578
- return global.KeystoreModule.getItem(this.alias, key);
594
+ /**
595
+ * Generate a new BIP-39 seed phrase (12 words)
596
+ */
597
+ async generateSeed() {
598
+ await this.initialize();
599
+ try {
600
+ const response = await this.apiClient.generateSeed();
601
+ if (response.success && response.seed) {
602
+ return response.seed;
603
+ }
604
+ } catch (error) {
605
+ console.warn("API seed generation failed:", error);
606
+ }
607
+ if (this.useNativeWdk && this.nativeWdkService) {
608
+ try {
609
+ const wdk = this.nativeWdkService;
610
+ return await wdk.generateSeedPhrase();
611
+ } catch (error) {
612
+ console.warn("Native WDK seed generation failed:", error);
613
+ }
579
614
  }
580
- throw new Error("Keystore not available on this platform");
615
+ return generateSeedPhrase();
581
616
  }
582
- async removeItem(key) {
583
- if (typeof global !== "undefined" && global.KeystoreModule) {
584
- await global.KeystoreModule.removeItem(this.alias, key);
585
- } else {
586
- throw new Error("Keystore not available on this platform");
617
+ /**
618
+ * Validate a BIP-39 seed phrase
619
+ */
620
+ async validateSeed(seed) {
621
+ await this.initialize();
622
+ try {
623
+ const response = await this.apiClient.validateSeed(seed);
624
+ if (response.success) {
625
+ return response.isValid ?? false;
626
+ }
627
+ } catch (error) {
628
+ console.warn("API seed validation failed:", error);
587
629
  }
588
- }
589
- async hasItem(key) {
590
- const value = await this.getItem(key);
591
- return value !== null;
592
- }
593
- async clear() {
594
- if (typeof global !== "undefined" && global.KeystoreModule) {
595
- await global.KeystoreModule.clear(this.alias);
596
- } else {
597
- throw new Error("Keystore not available on this platform");
630
+ if (this.useNativeWdk && this.nativeWdkService) {
631
+ try {
632
+ const wdk = this.nativeWdkService;
633
+ return await wdk.isValidSeed(seed);
634
+ } catch (error) {
635
+ console.warn("Native WDK seed validation failed:", error);
636
+ }
598
637
  }
599
- }
600
- };
601
- var WebEncryptedStorageAdapter = class {
602
- encryptionKey = null;
603
- storagePrefix;
604
- constructor(storagePrefix = "zubari_") {
605
- this.storagePrefix = storagePrefix;
638
+ return isValidSeed(seed);
606
639
  }
607
640
  /**
608
- * Initialize with a password-derived key
641
+ * Derive address for a specific chain using WDK API
642
+ *
643
+ * For Ethereum, falls back to local derivation if API fails.
644
+ * For other chains, WDK API is required - no placeholder fallback.
609
645
  */
610
- async initialize(password) {
611
- const encoder = new TextEncoder();
612
- const salt = this.getSalt();
613
- const keyMaterial = await crypto.subtle.importKey(
614
- "raw",
615
- encoder.encode(password),
616
- "PBKDF2",
617
- false,
618
- ["deriveKey"]
619
- );
620
- this.encryptionKey = await crypto.subtle.deriveKey(
621
- {
622
- name: "PBKDF2",
623
- salt: salt.buffer,
624
- iterations: 1e5,
625
- hash: "SHA-256"
626
- },
627
- keyMaterial,
628
- { name: "AES-GCM", length: 256 },
629
- false,
630
- ["encrypt", "decrypt"]
631
- );
632
- }
633
- getSalt() {
634
- const saltKey = `${this.storagePrefix}salt`;
635
- let saltHex = localStorage.getItem(saltKey);
636
- if (!saltHex) {
637
- const salt = crypto.getRandomValues(new Uint8Array(16));
638
- saltHex = Array.from(salt).map((b) => b.toString(16).padStart(2, "0")).join("");
639
- localStorage.setItem(saltKey, saltHex);
646
+ async deriveAddress(seed, chain) {
647
+ await this.initialize();
648
+ const path = this.getDerivationPath(chain);
649
+ try {
650
+ const response = await this.apiClient.deriveAddress(seed, chain, this.config.network);
651
+ if (response.success && response.address) {
652
+ return {
653
+ chain,
654
+ address: response.address,
655
+ path: response.path || path
656
+ };
657
+ }
658
+ } catch (error) {
659
+ console.warn(`API address derivation failed for ${chain}:`, error);
660
+ if (chain === "ethereum") {
661
+ return this.deriveBrowserAddress(seed, chain);
662
+ }
640
663
  }
641
- return new Uint8Array(
642
- saltHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
643
- );
644
- }
645
- async setItem(key, value) {
646
- if (!this.encryptionKey) {
647
- throw new Error("Storage not initialized. Call initialize() first.");
664
+ if (this.useNativeWdk && this.nativeWdkService) {
665
+ try {
666
+ const wdk = this.nativeWdkService;
667
+ await wdk.initialize(seed);
668
+ return await wdk.deriveAddress(chain);
669
+ } catch (error) {
670
+ console.warn(`Native WDK address derivation failed for ${chain}:`, error);
671
+ }
648
672
  }
649
- const encoder = new TextEncoder();
650
- const iv = crypto.getRandomValues(new Uint8Array(12));
651
- const encrypted = await crypto.subtle.encrypt(
652
- { name: "AES-GCM", iv },
653
- this.encryptionKey,
654
- encoder.encode(value)
673
+ if (chain === "ethereum") {
674
+ return this.deriveBrowserAddress(seed, chain);
675
+ }
676
+ throw new Error(
677
+ `WDK API required for ${chain} address derivation. Ensure the backend is running.`
655
678
  );
656
- const combined = new Uint8Array(iv.length + encrypted.byteLength);
657
- combined.set(iv);
658
- combined.set(new Uint8Array(encrypted), iv.length);
659
- const base64 = btoa(String.fromCharCode(...combined));
660
- localStorage.setItem(`${this.storagePrefix}${key}`, base64);
661
679
  }
662
- async getItem(key) {
663
- if (!this.encryptionKey) {
664
- throw new Error("Storage not initialized. Call initialize() first.");
665
- }
666
- const base64 = localStorage.getItem(`${this.storagePrefix}${key}`);
667
- if (!base64) return null;
680
+ /**
681
+ * Derive addresses for all supported chains using WDK API
682
+ *
683
+ * Uses the backend WDK API for real cryptographically valid addresses.
684
+ * No placeholder fallback - WDK API is required for multi-chain addresses.
685
+ */
686
+ async deriveAllAddresses(seed) {
687
+ await this.initialize();
668
688
  try {
669
- const combined = new Uint8Array(
670
- atob(base64).split("").map((c) => c.charCodeAt(0))
671
- );
672
- const iv = combined.slice(0, 12);
673
- const encrypted = combined.slice(12);
674
- const decrypted = await crypto.subtle.decrypt(
675
- { name: "AES-GCM", iv },
676
- this.encryptionKey,
677
- encrypted
678
- );
679
- const decoder = new TextDecoder();
680
- return decoder.decode(decrypted);
681
- } catch {
682
- return null;
683
- }
684
- }
685
- async removeItem(key) {
686
- localStorage.removeItem(`${this.storagePrefix}${key}`);
687
- }
688
- async hasItem(key) {
689
- return localStorage.getItem(`${this.storagePrefix}${key}`) !== null;
690
- }
691
- async clear() {
692
- const keysToRemove = [];
693
- for (let i = 0; i < localStorage.length; i++) {
694
- const key = localStorage.key(i);
695
- if (key?.startsWith(this.storagePrefix)) {
696
- keysToRemove.push(key);
689
+ const response = await this.apiClient.deriveAllAddresses(seed, this.config.network);
690
+ if (response.success && response.addresses) {
691
+ const extractAddress = (value) => {
692
+ if (!value) return null;
693
+ if (typeof value === "string") return value;
694
+ if (typeof value === "object" && value !== null && "address" in value) {
695
+ return value.address;
696
+ }
697
+ return null;
698
+ };
699
+ return {
700
+ ethereum: extractAddress(response.addresses.ethereum),
701
+ bitcoin: extractAddress(response.addresses.bitcoin),
702
+ ton: extractAddress(response.addresses.ton),
703
+ tron: extractAddress(response.addresses.tron),
704
+ solana: extractAddress(response.addresses.solana),
705
+ spark: extractAddress(response.addresses.spark)
706
+ };
697
707
  }
708
+ } catch (error) {
709
+ console.warn("API address derivation failed:", error);
698
710
  }
699
- keysToRemove.forEach((key) => localStorage.removeItem(key));
700
- }
701
- };
702
- var MemoryStorageAdapter = class {
703
- storage = /* @__PURE__ */ new Map();
704
- async setItem(key, value) {
705
- this.storage.set(key, value);
706
- }
707
- async getItem(key) {
708
- return this.storage.get(key) || null;
709
- }
710
- async removeItem(key) {
711
- this.storage.delete(key);
712
- }
713
- async hasItem(key) {
714
- return this.storage.has(key);
715
- }
716
- async clear() {
717
- this.storage.clear();
718
- }
719
- };
720
- function createSecureStorage() {
721
- if (typeof global !== "undefined" && global.nativeModuleProxy !== void 0) {
722
- const Platform = global.Platform;
723
- if (Platform?.OS === "ios") {
724
- return new KeychainStorageAdapter();
725
- } else if (Platform?.OS === "android") {
726
- return new KeystoreStorageAdapter();
711
+ if (this.useNativeWdk && this.nativeWdkService) {
712
+ try {
713
+ const wdk = this.nativeWdkService;
714
+ await wdk.initialize(seed);
715
+ return await wdk.deriveAllAddresses();
716
+ } catch (error) {
717
+ console.warn("Native WDK multi-chain derivation failed:", error);
718
+ }
727
719
  }
720
+ throw new Error(
721
+ "Tether WDK API required for multi-chain address derivation. Service temporarily unavailable."
722
+ );
728
723
  }
729
- if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
730
- return new WebEncryptedStorageAdapter();
731
- }
732
- return new MemoryStorageAdapter();
733
- }
734
-
735
- // src/services/WdkApiClient.ts
736
- var WdkApiClient = class {
737
- config;
738
- constructor(config) {
739
- this.config = {
740
- baseUrl: config.baseUrl,
741
- timeout: config.timeout || 3e4
742
- };
724
+ /**
725
+ * Get balances for all chains
726
+ */
727
+ async getAllBalances(seed) {
728
+ await this.initialize();
729
+ try {
730
+ const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/balances`, {
731
+ method: "POST",
732
+ headers: { "Content-Type": "application/json" },
733
+ body: JSON.stringify({ seed, network: this.config.network })
734
+ });
735
+ if (response.ok) {
736
+ const data = await response.json();
737
+ if (data.success) {
738
+ return data.balances;
739
+ }
740
+ }
741
+ } catch (error) {
742
+ console.warn("Failed to fetch balances:", error);
743
+ }
744
+ return {};
743
745
  }
744
746
  /**
745
- * Generate a new BIP-39 seed phrase using Tether WDK
747
+ * Get fee rates for a chain
746
748
  */
747
- async generateSeed() {
749
+ async getFeeRates(seed, chain) {
750
+ await this.initialize();
748
751
  try {
749
- const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/generate-seed`, {
752
+ const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/fee-rates`, {
750
753
  method: "POST",
751
- headers: {
752
- "Content-Type": "application/json"
753
- }
754
+ headers: { "Content-Type": "application/json" },
755
+ body: JSON.stringify({ seed, chain, network: this.config.network })
754
756
  });
755
- return await response.json();
757
+ if (response.ok) {
758
+ const data = await response.json();
759
+ if (data.success && data.feeRates) {
760
+ return data.feeRates;
761
+ }
762
+ }
756
763
  } catch (error) {
757
- return {
758
- success: false,
759
- error: error instanceof Error ? error.message : "Failed to generate seed"
760
- };
764
+ console.warn(`Failed to fetch fee rates for ${chain}:`, error);
761
765
  }
766
+ return { slow: "0", normal: "0", fast: "0" };
762
767
  }
763
768
  /**
764
- * Validate a BIP-39 seed phrase
769
+ * Estimate transaction fee
765
770
  */
766
- async validateSeed(seed) {
771
+ async estimateFee(seed, chain, to, amount) {
772
+ await this.initialize();
767
773
  try {
768
- const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/validate-seed`, {
774
+ const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/estimate-fee`, {
769
775
  method: "POST",
770
- headers: {
771
- "Content-Type": "application/json"
772
- },
773
- body: JSON.stringify({ seed })
776
+ headers: { "Content-Type": "application/json" },
777
+ body: JSON.stringify({ seed, chain, to, amount, network: this.config.network })
774
778
  });
775
- return await response.json();
779
+ if (response.ok) {
780
+ const data = await response.json();
781
+ if (data.success) {
782
+ return { fee: data.fee, symbol: data.symbol };
783
+ }
784
+ }
776
785
  } catch (error) {
777
- return {
778
- success: false,
779
- error: error instanceof Error ? error.message : "Failed to validate seed"
780
- };
786
+ console.warn(`Failed to estimate fee for ${chain}:`, error);
781
787
  }
788
+ return { fee: "0", symbol: this.getChainSymbol(chain) };
782
789
  }
783
790
  /**
784
- * Derive address for a specific chain using Tether WDK
791
+ * Send a transaction
785
792
  */
786
- async deriveAddress(seed, chain, network = "testnet") {
793
+ async sendTransaction(seed, chain, to, amount) {
794
+ await this.initialize();
787
795
  try {
788
- const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/derive-address`, {
796
+ const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/send`, {
789
797
  method: "POST",
790
- headers: {
791
- "Content-Type": "application/json"
792
- },
793
- body: JSON.stringify({ seed, chain, network })
798
+ headers: { "Content-Type": "application/json" },
799
+ body: JSON.stringify({ seed, chain, to, amount, network: this.config.network })
794
800
  });
795
- return await response.json();
801
+ if (response.ok) {
802
+ const data = await response.json();
803
+ let txHash = data.txHash || data.transactionHash || data.hash;
804
+ if (txHash && typeof txHash === "object" && "hash" in txHash) {
805
+ txHash = txHash.hash;
806
+ }
807
+ if (chain === "ethereum" && txHash && (typeof txHash !== "string" || !txHash.startsWith("0x") || txHash.length !== 66)) {
808
+ console.warn(`Invalid Ethereum tx hash format: ${txHash} (length: ${txHash?.length}, expected: 66)`);
809
+ }
810
+ return {
811
+ success: data.success,
812
+ txHash,
813
+ from: data.from,
814
+ to: data.to,
815
+ amount: data.amount,
816
+ chain: data.chain,
817
+ network: data.network
818
+ };
819
+ }
820
+ const errorData = await response.json().catch(() => ({}));
821
+ return {
822
+ success: false,
823
+ error: errorData.error || `HTTP ${response.status}`
824
+ };
796
825
  } catch (error) {
797
826
  return {
798
827
  success: false,
799
- error: error instanceof Error ? error.message : "Failed to derive address"
828
+ error: error instanceof Error ? error.message : "Transaction failed"
800
829
  };
801
830
  }
802
831
  }
803
832
  /**
804
- * Derive addresses for all chains using Tether WDK
833
+ * Get the network configuration
805
834
  */
806
- async deriveAllAddresses(seed, network = "testnet") {
835
+ getNetwork() {
836
+ return this.config.network;
837
+ }
838
+ /**
839
+ * Get API URL
840
+ */
841
+ getApiUrl() {
842
+ return this.config.apiUrl;
843
+ }
844
+ // ==========================================
845
+ // Private Helper Methods
846
+ // ==========================================
847
+ getDerivationPath(chain) {
848
+ const paths = {
849
+ bitcoin: this.config.network === "testnet" ? "m/84'/1'/0'/0/0" : "m/84'/0'/0'/0/0",
850
+ ethereum: "m/44'/60'/0'/0/0",
851
+ ton: "m/44'/607'/0'/0'/0'",
852
+ tron: "m/44'/195'/0'/0/0",
853
+ solana: "m/44'/501'/0'/0'",
854
+ spark: "m/44'/998'/0'/0/0"
855
+ };
856
+ return paths[chain];
857
+ }
858
+ getChainSymbol(chain) {
859
+ const symbols = {
860
+ ethereum: "ETH",
861
+ bitcoin: "BTC",
862
+ ton: "TON",
863
+ tron: "TRX",
864
+ solana: "SOL",
865
+ spark: "SAT"
866
+ };
867
+ return symbols[chain];
868
+ }
869
+ /**
870
+ * Derive address using browser-compatible libraries
871
+ */
872
+ async deriveBrowserAddress(seed, chain) {
873
+ const path = this.getDerivationPath(chain);
807
874
  try {
808
- const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/derive-all`, {
809
- method: "POST",
810
- headers: {
811
- "Content-Type": "application/json"
812
- },
813
- body: JSON.stringify({ seed, network })
814
- });
815
- return await response.json();
875
+ let address;
876
+ switch (chain) {
877
+ case "ethereum":
878
+ address = deriveEthereumAddress(seed);
879
+ break;
880
+ case "bitcoin":
881
+ address = deriveBitcoinAddress(seed, this.config.network);
882
+ break;
883
+ case "tron":
884
+ address = deriveTronAddress(seed);
885
+ break;
886
+ case "spark":
887
+ address = deriveSparkAddress(seed, this.config.network);
888
+ break;
889
+ case "solana":
890
+ address = await deriveSolanaAddress(seed);
891
+ break;
892
+ case "ton":
893
+ address = await deriveTonAddress(seed);
894
+ break;
895
+ default:
896
+ throw new Error(`Unsupported chain: ${chain}`);
897
+ }
898
+ return { chain, address, path };
816
899
  } catch (error) {
817
- return {
818
- success: false,
819
- error: error instanceof Error ? error.message : "Failed to derive addresses"
900
+ console.error(`Browser derivation failed for ${chain}:`, error);
901
+ throw error;
902
+ }
903
+ }
904
+ /**
905
+ * Derive all addresses using browser-compatible libraries
906
+ */
907
+ async deriveAllBrowserAddresses(seed) {
908
+ return deriveAllAddresses(seed, this.config.network);
909
+ }
910
+ };
911
+ var defaultService = null;
912
+ function getZubariWdkService(config) {
913
+ if (!defaultService || config && config.network !== defaultService.getNetwork()) {
914
+ defaultService = new ZubariWdkService(config);
915
+ }
916
+ return defaultService;
917
+ }
918
+
919
+ // src/wallet/ZubariWallet.ts
920
+ var ZubariWallet = class {
921
+ seed;
922
+ config;
923
+ accounts = /* @__PURE__ */ new Map();
924
+ wdkService;
925
+ initialized = false;
926
+ constructor(seed, config) {
927
+ this.seed = seed;
928
+ this.config = {
929
+ network: config.network || "mainnet",
930
+ enabledNetworks: config.enabledNetworks || ["ethereum"],
931
+ gasless: config.gasless ?? false,
932
+ paymasterUrl: config.paymasterUrl,
933
+ bundlerUrl: config.bundlerUrl,
934
+ rpcUrls: config.rpcUrls
935
+ };
936
+ this.wdkService = getZubariWdkService({
937
+ network: this.config.network === "testnet" ? "testnet" : "mainnet",
938
+ forceApi: true
939
+ // Use backend API for all operations
940
+ });
941
+ }
942
+ /**
943
+ * Initialize the wallet by deriving accounts for all enabled networks
944
+ */
945
+ async initialize() {
946
+ if (this.initialized) return;
947
+ for (const network of this.config.enabledNetworks) {
948
+ await this.deriveAccount(network);
949
+ }
950
+ this.initialized = true;
951
+ }
952
+ /**
953
+ * Derive account for a specific network using BIP-44 via WDK API
954
+ */
955
+ async deriveAccount(network, index = 0) {
956
+ const basePath = DERIVATION_PATHS[network];
957
+ const derivationPath = `${basePath}/${index}`;
958
+ const chainMap = {
959
+ ethereum: "ethereum",
960
+ bitcoin: "bitcoin",
961
+ ton: "ton",
962
+ tron: "tron",
963
+ solana: "solana",
964
+ spark: "spark"
965
+ };
966
+ const chain = chainMap[network];
967
+ if (!chain) {
968
+ throw new Error(`Unsupported network: ${network}`);
969
+ }
970
+ try {
971
+ const result = await this.wdkService.deriveAddress(this.seed, chain);
972
+ const account = {
973
+ network,
974
+ address: result.address,
975
+ publicKey: "",
976
+ // WDK doesn't expose public key directly
977
+ derivationPath: result.path || derivationPath
820
978
  };
979
+ this.accounts.set(network, account);
980
+ return account;
981
+ } catch (error) {
982
+ console.error(`Failed to derive account for ${network}:`, error);
983
+ throw new Error(`Failed to derive ${network} address: ${error instanceof Error ? error.message : "Unknown error"}`);
984
+ }
985
+ }
986
+ /**
987
+ * Get account for a specific network
988
+ */
989
+ async getAccount(network, index = 0) {
990
+ const existing = this.accounts.get(network);
991
+ if (existing && existing.derivationPath.endsWith(`/${index}`)) {
992
+ return existing;
993
+ }
994
+ return this.deriveAccount(network, index);
995
+ }
996
+ /**
997
+ * Get address for a specific network
998
+ */
999
+ async getAddress(network) {
1000
+ const account = await this.getAccount(network);
1001
+ return account.address;
1002
+ }
1003
+ /**
1004
+ * Get all addresses for enabled networks
1005
+ */
1006
+ async getAllAddresses() {
1007
+ const addresses = {};
1008
+ for (const network of this.config.enabledNetworks) {
1009
+ addresses[network] = await this.getAddress(network);
821
1010
  }
1011
+ return addresses;
822
1012
  }
823
1013
  /**
824
- * Send a transaction on a specific chain using Tether WDK
1014
+ * Get balance for a specific network via WDK API
825
1015
  */
826
- async sendTransaction(seed, chain, to, amount, network = "testnet") {
1016
+ async getBalance(network) {
1017
+ const networkConfig = getNetworkConfig(network, this.config.network === "testnet");
1018
+ const chainMap = {
1019
+ ethereum: "ethereum",
1020
+ bitcoin: "bitcoin",
1021
+ ton: "ton",
1022
+ tron: "tron",
1023
+ solana: "solana",
1024
+ spark: "spark"
1025
+ };
1026
+ const chain = chainMap[network];
1027
+ if (!chain) {
1028
+ throw new Error(`Unsupported network: ${network}`);
1029
+ }
827
1030
  try {
828
- const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/send`, {
829
- method: "POST",
830
- headers: {
831
- "Content-Type": "application/json"
1031
+ const balances = await this.wdkService.getAllBalances(this.seed);
1032
+ const chainBalance = balances[chain];
1033
+ if (chainBalance) {
1034
+ const balanceValue = BigInt(chainBalance.balance || "0");
1035
+ const decimals = networkConfig.nativeCurrency.decimals;
1036
+ const balanceFormatted = this.formatBalance(balanceValue, decimals);
1037
+ return {
1038
+ network,
1039
+ native: {
1040
+ symbol: chainBalance.symbol || networkConfig.nativeCurrency.symbol,
1041
+ balance: balanceValue,
1042
+ balanceFormatted,
1043
+ balanceUsd: 0
1044
+ // TODO: Integrate price feed
1045
+ },
1046
+ tokens: []
1047
+ };
1048
+ }
1049
+ return {
1050
+ network,
1051
+ native: {
1052
+ symbol: networkConfig.nativeCurrency.symbol,
1053
+ balance: BigInt(0),
1054
+ balanceFormatted: "0",
1055
+ balanceUsd: 0
832
1056
  },
833
- body: JSON.stringify({ seed, chain, to, amount, network })
834
- });
835
- return await response.json();
1057
+ tokens: []
1058
+ };
836
1059
  } catch (error) {
1060
+ console.error(`Failed to get balance for ${network}:`, error);
837
1061
  return {
838
- success: false,
839
- error: error instanceof Error ? error.message : "Failed to send transaction"
1062
+ network,
1063
+ native: {
1064
+ symbol: networkConfig.nativeCurrency.symbol,
1065
+ balance: BigInt(0),
1066
+ balanceFormatted: "0",
1067
+ balanceUsd: 0
1068
+ },
1069
+ tokens: []
840
1070
  };
841
1071
  }
842
1072
  }
843
1073
  /**
844
- * Get transaction history for an address on a specific chain
845
- * Fetches from blockchain explorers (Etherscan, mempool.space, etc.)
1074
+ * Format balance from wei/satoshi to human-readable string
846
1075
  */
847
- async getTransactionHistory(seed, chain, network = "testnet", limit = 10) {
848
- try {
849
- const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/history`, {
850
- method: "POST",
851
- headers: {
852
- "Content-Type": "application/json"
853
- },
854
- body: JSON.stringify({ seed, chain, network, limit })
855
- });
856
- return await response.json();
857
- } catch (error) {
858
- return {
859
- success: false,
860
- error: error instanceof Error ? error.message : "Failed to get transaction history"
861
- };
862
- }
1076
+ formatBalance(balance, decimals) {
1077
+ if (balance === BigInt(0)) return "0";
1078
+ const divisor = BigInt(10 ** decimals);
1079
+ const integerPart = balance / divisor;
1080
+ const fractionalPart = balance % divisor;
1081
+ const fractionalStr = fractionalPart.toString().padStart(decimals, "0").slice(0, 6);
1082
+ return `${integerPart}.${fractionalStr}`.replace(/\.?0+$/, "") || "0";
863
1083
  }
864
1084
  /**
865
- * Get transaction status by hash
866
- * Fetches from blockchain explorers to check confirmation status
1085
+ * Get balances for all enabled networks
867
1086
  */
868
- async getTransactionStatus(txHash, chain, network = "testnet") {
869
- try {
870
- const response = await fetch(`${this.config.baseUrl}/api/wallets/wdk/tx-status`, {
871
- method: "POST",
872
- headers: {
873
- "Content-Type": "application/json"
874
- },
875
- body: JSON.stringify({ txHash, chain, network })
876
- });
877
- return await response.json();
878
- } catch (error) {
879
- return {
880
- success: false,
881
- error: error instanceof Error ? error.message : "Failed to get transaction status"
882
- };
1087
+ async getAllBalances() {
1088
+ const balances = [];
1089
+ for (const network of this.config.enabledNetworks) {
1090
+ balances.push(await this.getBalance(network));
883
1091
  }
1092
+ return balances;
884
1093
  }
885
- };
886
- var DEFAULT_API_URL = process.env.NEXT_PUBLIC_API_URL || "https://ckgwifsxka.us-east-2.awsapprunner.com";
887
- var wdkApiClient = null;
888
- function getWdkApiClient(baseUrl) {
889
- if (!wdkApiClient || baseUrl && wdkApiClient["config"].baseUrl !== baseUrl) {
890
- wdkApiClient = new WdkApiClient({
891
- baseUrl: baseUrl || DEFAULT_API_URL
892
- });
893
- }
894
- return wdkApiClient;
895
- }
896
- var DERIVATION_PATHS2 = {
897
- ethereum: "m/44'/60'/0'/0/0",
898
- bitcoin_mainnet: "m/84'/0'/0'/0/0",
899
- bitcoin_testnet: "m/84'/1'/0'/0/0",
900
- ton: "m/44'/607'/0'/0'/0'",
901
- tron: "m/44'/195'/0'/0/0",
902
- solana: "m/44'/501'/0'/0'",
903
- spark: "m/44'/998'/0'/0/0"
904
- };
905
- function deriveEthereumAddress(seed) {
906
- const hdNode = ethers.HDNodeWallet.fromPhrase(seed, void 0, DERIVATION_PATHS2.ethereum);
907
- return hdNode.address;
908
- }
909
- function deriveBitcoinAddress(seed, network = "testnet") {
910
- try {
911
- const seedBytes = bip39.mnemonicToSeedSync(seed);
912
- const hdKey = bip32.HDKey.fromMasterSeed(seedBytes);
913
- const path = network === "testnet" ? DERIVATION_PATHS2.bitcoin_testnet : DERIVATION_PATHS2.bitcoin_mainnet;
914
- const child = hdKey.derive(path);
915
- if (!child.publicKey) {
916
- throw new Error("Failed to derive public key");
1094
+ /**
1095
+ * Get total portfolio value in USD
1096
+ */
1097
+ async getTotalPortfolioUsd() {
1098
+ const balances = await this.getAllBalances();
1099
+ let total = 0;
1100
+ for (const balance of balances) {
1101
+ total += balance.native.balanceUsd;
1102
+ for (const token of balance.tokens) {
1103
+ total += token.balanceUsd;
1104
+ }
917
1105
  }
918
- const pubKeyHash = ripemd160.ripemd160(sha256.sha256(child.publicKey));
919
- const witnessVersion = 0;
920
- const words = base.bech32.toWords(pubKeyHash);
921
- words.unshift(witnessVersion);
922
- const hrp = network === "testnet" ? "tb" : "bc";
923
- const address = base.bech32.encode(hrp, words);
924
- return address;
925
- } catch (error) {
926
- console.error("Bitcoin address derivation failed:", error);
927
- throw error;
928
- }
929
- }
930
- async function deriveSolanaAddress(seed) {
931
- try {
932
- const [ed25519, nacl, bs58Module] = await Promise.all([
933
- import('ed25519-hd-key'),
934
- import('tweetnacl'),
935
- import('bs58')
936
- ]);
937
- const bs58 = bs58Module.default || bs58Module;
938
- const seedBytes = bip39.mnemonicToSeedSync(seed);
939
- const derived = ed25519.derivePath(DERIVATION_PATHS2.solana, Buffer.from(seedBytes).toString("hex"));
940
- const keypair = nacl.sign.keyPair.fromSeed(new Uint8Array(derived.key));
941
- return bs58.encode(keypair.publicKey);
942
- } catch (error) {
943
- console.error("Solana address derivation failed:", error);
944
- throw error;
945
- }
946
- }
947
- async function deriveTonAddress(seed) {
948
- try {
949
- const [ed25519, nacl] = await Promise.all([
950
- import('ed25519-hd-key'),
951
- import('tweetnacl')
952
- ]);
953
- const seedBytes = bip39.mnemonicToSeedSync(seed);
954
- const derived = ed25519.derivePath(DERIVATION_PATHS2.ton, Buffer.from(seedBytes).toString("hex"));
955
- const keypair = nacl.sign.keyPair.fromSeed(new Uint8Array(derived.key));
956
- const publicKey = keypair.publicKey;
957
- const workchain = 0;
958
- const flags = 17;
959
- const hash = sha256.sha256(publicKey);
960
- const addressData = new Uint8Array(34);
961
- addressData[0] = flags;
962
- addressData[1] = workchain;
963
- addressData.set(hash, 2);
964
- const crc = crc16(addressData);
965
- const fullAddress = new Uint8Array(36);
966
- fullAddress.set(addressData);
967
- fullAddress[34] = crc >> 8 & 255;
968
- fullAddress[35] = crc & 255;
969
- const base64 = btoa(String.fromCharCode(...fullAddress)).replace(/\+/g, "-").replace(/\//g, "_");
970
- return base64;
971
- } catch (error) {
972
- console.error("TON address derivation failed:", error);
973
- throw error;
1106
+ return total;
974
1107
  }
975
- }
976
- function crc16(data) {
977
- let crc = 0;
978
- for (const byte of data) {
979
- crc ^= byte << 8;
980
- for (let i = 0; i < 8; i++) {
981
- crc = crc & 32768 ? crc << 1 ^ 4129 : crc << 1;
982
- crc &= 65535;
1108
+ /**
1109
+ * Send native currency on a specific network via WDK API
1110
+ */
1111
+ async send(network, params) {
1112
+ const { to, amount } = params;
1113
+ const chainMap = {
1114
+ ethereum: "ethereum",
1115
+ bitcoin: "bitcoin",
1116
+ ton: "ton",
1117
+ tron: "tron",
1118
+ solana: "solana",
1119
+ spark: "spark"
1120
+ };
1121
+ const chain = chainMap[network];
1122
+ if (!chain) {
1123
+ throw new Error(`Unsupported network: ${network}`);
983
1124
  }
984
- }
985
- return crc;
986
- }
987
- function deriveTronAddress(seed) {
988
- try {
989
- const hdNode = ethers.HDNodeWallet.fromPhrase(seed, void 0, DERIVATION_PATHS2.tron);
990
- const ethAddressHex = hdNode.address.slice(2).toLowerCase();
991
- const addressBytes = new Uint8Array(21);
992
- addressBytes[0] = 65;
993
- for (let i = 0; i < 20; i++) {
994
- addressBytes[i + 1] = parseInt(ethAddressHex.slice(i * 2, i * 2 + 2), 16);
1125
+ try {
1126
+ const result = await this.wdkService.sendTransaction(
1127
+ this.seed,
1128
+ chain,
1129
+ to,
1130
+ amount.toString()
1131
+ );
1132
+ if (result.success && result.txHash) {
1133
+ return {
1134
+ hash: result.txHash,
1135
+ network,
1136
+ status: "pending",
1137
+ metadata: {
1138
+ from: result.from,
1139
+ to: result.to,
1140
+ amount: result.amount
1141
+ }
1142
+ };
1143
+ }
1144
+ throw new Error(result.error || "Transaction failed");
1145
+ } catch (error) {
1146
+ console.error(`Failed to send on ${network}:`, error);
1147
+ throw new Error(`Transaction failed: ${error instanceof Error ? error.message : "Unknown error"}`);
995
1148
  }
996
- const tronBase58check = base.base58check(sha256.sha256);
997
- return tronBase58check.encode(addressBytes);
998
- } catch (error) {
999
- console.error("TRON address derivation failed:", error);
1000
- throw error;
1001
1149
  }
1002
- }
1003
- function deriveSparkAddress(seed, network = "testnet") {
1004
- try {
1005
- const seedBytes = bip39.mnemonicToSeedSync(seed);
1006
- const hdKey = bip32.HDKey.fromMasterSeed(seedBytes);
1007
- const child = hdKey.derive(DERIVATION_PATHS2.spark);
1008
- if (!child.publicKey) {
1009
- throw new Error("Failed to derive public key");
1150
+ /**
1151
+ * Send ERC-20 token on EVM networks via WDK API
1152
+ */
1153
+ async sendToken(network, token, to, amount) {
1154
+ const networkConfig = getNetworkConfig(network, this.config.network === "testnet");
1155
+ if (!networkConfig.isEvm) {
1156
+ throw new Error(`sendToken is only supported on EVM networks. ${network} is not an EVM chain.`);
1157
+ }
1158
+ try {
1159
+ const apiUrl = this.wdkService.getApiUrl();
1160
+ const response = await fetch(`${apiUrl}/api/wallets/wdk/send-token`, {
1161
+ method: "POST",
1162
+ headers: { "Content-Type": "application/json" },
1163
+ body: JSON.stringify({
1164
+ seed: this.seed,
1165
+ chain: "ethereum",
1166
+ token,
1167
+ to,
1168
+ amount: amount.toString(),
1169
+ network: this.config.network
1170
+ })
1171
+ });
1172
+ const result = await response.json();
1173
+ if (result.success && result.txHash) {
1174
+ return {
1175
+ hash: result.txHash,
1176
+ network,
1177
+ status: "pending",
1178
+ metadata: {
1179
+ token,
1180
+ from: result.from,
1181
+ to: result.to,
1182
+ amount: result.amount
1183
+ }
1184
+ };
1185
+ }
1186
+ throw new Error(result.error || "Token transfer failed");
1187
+ } catch (error) {
1188
+ console.error(`Failed to send token on ${network}:`, error);
1189
+ throw new Error(`Token transfer failed: ${error instanceof Error ? error.message : "Unknown error"}`);
1010
1190
  }
1011
- const pubKeyHash = ripemd160.ripemd160(sha256.sha256(child.publicKey));
1012
- const witnessVersion = 0;
1013
- const words = base.bech32.toWords(pubKeyHash);
1014
- words.unshift(witnessVersion);
1015
- const hrp = network === "testnet" ? "tsp" : "sp";
1016
- const address = base.bech32.encode(hrp, words);
1017
- return address;
1018
- } catch (error) {
1019
- console.error("Spark address derivation failed:", error);
1020
- throw error;
1021
- }
1022
- }
1023
- async function deriveAllAddresses(seed, network = "testnet") {
1024
- const addresses = {
1025
- ethereum: null,
1026
- bitcoin: null,
1027
- ton: null,
1028
- tron: null,
1029
- solana: null,
1030
- spark: null
1031
- };
1032
- try {
1033
- addresses.ethereum = deriveEthereumAddress(seed);
1034
- } catch (e) {
1035
- console.error("ETH derivation failed:", e);
1036
1191
  }
1037
- try {
1038
- addresses.bitcoin = deriveBitcoinAddress(seed, network);
1039
- } catch (e) {
1040
- console.error("BTC derivation failed:", e);
1192
+ /**
1193
+ * Send Bitcoin on-chain using WalletManagerBtc
1194
+ * @param to - Destination address (bc1q... for native segwit)
1195
+ * @param amount - Amount in satoshis
1196
+ */
1197
+ async sendBitcoin(to, amount) {
1198
+ if (!this.isValidBitcoinAddress(to)) {
1199
+ throw new Error("Invalid Bitcoin address. Expected bc1q... (native segwit) format.");
1200
+ }
1201
+ return this.send("bitcoin", { to, amount });
1041
1202
  }
1042
- try {
1043
- addresses.spark = deriveSparkAddress(seed, network);
1044
- } catch (e) {
1045
- console.error("Spark derivation failed:", e);
1203
+ /**
1204
+ * Validate Bitcoin address format
1205
+ */
1206
+ isValidBitcoinAddress(address) {
1207
+ if (address.startsWith("bc1q") || address.startsWith("tb1q")) {
1208
+ return address.length >= 42 && address.length <= 62;
1209
+ }
1210
+ if (address.startsWith("1") || address.startsWith("m") || address.startsWith("n")) {
1211
+ return address.length >= 25 && address.length <= 34;
1212
+ }
1213
+ if (address.startsWith("3") || address.startsWith("2")) {
1214
+ return address.length >= 25 && address.length <= 34;
1215
+ }
1216
+ return false;
1046
1217
  }
1047
- try {
1048
- addresses.tron = deriveTronAddress(seed);
1049
- } catch (e) {
1050
- console.error("TRON derivation failed:", e);
1218
+ /**
1219
+ * Get Bitcoin address (native segwit bc1q...)
1220
+ */
1221
+ async getBitcoinAddress() {
1222
+ const account = await this.getAccount("bitcoin");
1223
+ return account.address;
1051
1224
  }
1052
- const [solResult, tonResult] = await Promise.allSettled([
1053
- deriveSolanaAddress(seed),
1054
- deriveTonAddress(seed)
1055
- ]);
1056
- if (solResult.status === "fulfilled") {
1057
- addresses.solana = solResult.value;
1058
- } else {
1059
- console.error("SOL derivation failed:", solResult.reason);
1225
+ /**
1226
+ * Pay Lightning invoice via Spark network using WDK API
1227
+ * Uses WDK WalletManagerSpark (m/44'/998')
1228
+ * @param invoice - Lightning invoice string (lnbc...)
1229
+ */
1230
+ async sendLightning(invoice) {
1231
+ if (!this.isValidLightningInvoice(invoice)) {
1232
+ throw new Error("Invalid Lightning invoice format. Expected lnbc... or lntb...");
1233
+ }
1234
+ const invoiceDetails = await this.decodeLightningInvoice(invoice);
1235
+ try {
1236
+ const apiUrl = this.wdkService.getApiUrl();
1237
+ const response = await fetch(`${apiUrl}/api/wallets/wdk/lightning/pay`, {
1238
+ method: "POST",
1239
+ headers: { "Content-Type": "application/json" },
1240
+ body: JSON.stringify({
1241
+ seed: this.seed,
1242
+ invoice,
1243
+ network: this.config.network
1244
+ })
1245
+ });
1246
+ const result = await response.json();
1247
+ if (result.success && result.paymentHash) {
1248
+ return {
1249
+ hash: result.paymentHash,
1250
+ network: "spark",
1251
+ status: "pending",
1252
+ metadata: {
1253
+ invoice,
1254
+ amount: invoiceDetails.amount,
1255
+ destination: invoiceDetails.destination,
1256
+ preimage: result.preimage
1257
+ }
1258
+ };
1259
+ }
1260
+ throw new Error(result.error || "Lightning payment failed");
1261
+ } catch (error) {
1262
+ console.error("Failed to send Lightning payment:", error);
1263
+ throw new Error(`Lightning payment failed: ${error instanceof Error ? error.message : "Unknown error"}`);
1264
+ }
1060
1265
  }
1061
- if (tonResult.status === "fulfilled") {
1062
- addresses.ton = tonResult.value;
1063
- } else {
1064
- console.error("TON derivation failed:", tonResult.reason);
1266
+ /**
1267
+ * Create Lightning invoice via Spark using WDK API
1268
+ * @param amount - Amount in millisatoshis
1269
+ * @param memo - Optional payment memo
1270
+ * @returns Lightning invoice string (lnbc...)
1271
+ */
1272
+ async createLightningInvoice(amount, memo) {
1273
+ if (amount <= BigInt(0)) {
1274
+ throw new Error("Invoice amount must be greater than 0");
1275
+ }
1276
+ try {
1277
+ const apiUrl = this.wdkService.getApiUrl();
1278
+ const response = await fetch(`${apiUrl}/api/wallets/wdk/lightning/invoice`, {
1279
+ method: "POST",
1280
+ headers: { "Content-Type": "application/json" },
1281
+ body: JSON.stringify({
1282
+ seed: this.seed,
1283
+ amount: amount.toString(),
1284
+ memo: memo || "",
1285
+ network: this.config.network
1286
+ })
1287
+ });
1288
+ const result = await response.json();
1289
+ if (result.success && result.invoice) {
1290
+ return result.invoice;
1291
+ }
1292
+ throw new Error(result.error || "Failed to create invoice");
1293
+ } catch (error) {
1294
+ console.error("Failed to create Lightning invoice:", error);
1295
+ throw new Error(`Failed to create invoice: ${error instanceof Error ? error.message : "Unknown error"}`);
1296
+ }
1065
1297
  }
1066
- return addresses;
1067
- }
1068
- function isValidSeed(seed) {
1069
- return bip39.validateMnemonic(seed, english.wordlist);
1070
- }
1071
- function generateSeedPhrase() {
1072
- return bip39.generateMnemonic(english.wordlist);
1073
- }
1074
-
1075
- // src/services/ZubariWdkService.ts
1076
- var DEFAULT_API_URL2 = "https://ckgwifsxka.us-east-2.awsapprunner.com";
1077
- function isBrowser() {
1078
- return typeof window !== "undefined" && typeof window.document !== "undefined";
1079
- }
1080
- var dynamicImport = new Function("specifier", "return import(specifier)");
1081
- async function canUseNativeWdk() {
1082
- if (isBrowser()) {
1083
- return false;
1298
+ /**
1299
+ * Validate Lightning invoice format
1300
+ */
1301
+ isValidLightningInvoice(invoice) {
1302
+ const lowerInvoice = invoice.toLowerCase();
1303
+ return lowerInvoice.startsWith("lnbc") || // Mainnet
1304
+ lowerInvoice.startsWith("lntb") || // Testnet
1305
+ lowerInvoice.startsWith("lnbcrt");
1084
1306
  }
1085
- try {
1086
- await dynamicImport("@tetherto/wdk");
1087
- return true;
1088
- } catch {
1089
- return false;
1307
+ /**
1308
+ * Decode Lightning invoice using backend API
1309
+ * Parses BOLT11 invoice to extract payment details
1310
+ */
1311
+ async decodeLightningInvoice(invoice) {
1312
+ try {
1313
+ const apiUrl = this.wdkService.getApiUrl();
1314
+ const response = await fetch(`${apiUrl}/api/wallets/wdk/lightning/decode`, {
1315
+ method: "POST",
1316
+ headers: { "Content-Type": "application/json" },
1317
+ body: JSON.stringify({ invoice })
1318
+ });
1319
+ const result = await response.json();
1320
+ if (result.success) {
1321
+ return {
1322
+ amount: BigInt(result.amount || 0),
1323
+ destination: result.destination || "",
1324
+ expiry: result.expiry || 3600,
1325
+ description: result.description
1326
+ };
1327
+ }
1328
+ return this.parseInvoiceBasic(invoice);
1329
+ } catch (error) {
1330
+ console.warn("Failed to decode invoice via API, using basic parsing:", error);
1331
+ return this.parseInvoiceBasic(invoice);
1332
+ }
1090
1333
  }
1091
- }
1092
- var ZubariWdkService = class {
1093
- config;
1094
- apiClient;
1095
- nativeWdkService = null;
1096
- initialized = false;
1097
- useNativeWdk = false;
1098
- constructor(config = {}) {
1099
- this.config = {
1100
- network: config.network || "testnet",
1101
- apiUrl: config.apiUrl || process.env.NEXT_PUBLIC_API_URL || DEFAULT_API_URL2,
1102
- forceApi: config.forceApi ?? false,
1103
- timeout: config.timeout || 3e4
1334
+ /**
1335
+ * Basic Lightning invoice parsing (fallback)
1336
+ * Extracts amount from invoice prefix when API is unavailable
1337
+ */
1338
+ parseInvoiceBasic(invoice) {
1339
+ const lowerInvoice = invoice.toLowerCase();
1340
+ const amountMatch = lowerInvoice.match(/^ln(?:bc|tb|bcrt)(\d+)([munp])?/);
1341
+ let amount = BigInt(0);
1342
+ if (amountMatch && amountMatch[1]) {
1343
+ const value = BigInt(amountMatch[1]);
1344
+ const multiplier = amountMatch[2];
1345
+ switch (multiplier) {
1346
+ case "m":
1347
+ amount = value * BigInt(1e8);
1348
+ break;
1349
+ // milli-BTC
1350
+ case "u":
1351
+ amount = value * BigInt(1e5);
1352
+ break;
1353
+ // micro-BTC
1354
+ case "n":
1355
+ amount = value * BigInt(100);
1356
+ break;
1357
+ // nano-BTC
1358
+ case "p":
1359
+ amount = value / BigInt(10);
1360
+ break;
1361
+ // pico-BTC
1362
+ default:
1363
+ amount = value * BigInt(1e11);
1364
+ break;
1365
+ }
1366
+ }
1367
+ return {
1368
+ amount,
1369
+ destination: "",
1370
+ expiry: 3600
1104
1371
  };
1105
- this.apiClient = getWdkApiClient(this.config.apiUrl);
1106
1372
  }
1107
1373
  /**
1108
- * Initialize the service and determine the best strategy
1374
+ * Get Lightning (Spark) balance via WDK API
1109
1375
  */
1110
- async initialize() {
1111
- if (this.initialized) return;
1112
- if (isBrowser() || this.config.forceApi) {
1113
- this.useNativeWdk = false;
1114
- this.initialized = true;
1115
- return;
1116
- }
1117
- if (await canUseNativeWdk()) {
1118
- try {
1119
- const WdkServiceModule = await dynamicImport("./WdkService");
1120
- const WdkService = WdkServiceModule.WdkService || WdkServiceModule.default;
1121
- this.nativeWdkService = new WdkService({
1376
+ async getLightningBalance() {
1377
+ try {
1378
+ const apiUrl = this.wdkService.getApiUrl();
1379
+ const response = await fetch(`${apiUrl}/api/wallets/wdk/lightning/balance`, {
1380
+ method: "POST",
1381
+ headers: { "Content-Type": "application/json" },
1382
+ body: JSON.stringify({
1383
+ seed: this.seed,
1122
1384
  network: this.config.network
1123
- });
1124
- this.useNativeWdk = true;
1125
- } catch (error) {
1126
- console.warn("Failed to initialize native WDK, falling back to API:", error);
1127
- this.useNativeWdk = false;
1385
+ })
1386
+ });
1387
+ const result = await response.json();
1388
+ if (result.success) {
1389
+ return {
1390
+ available: BigInt(result.available || 0),
1391
+ pending: BigInt(result.pending || 0)
1392
+ };
1128
1393
  }
1394
+ return { available: BigInt(0), pending: BigInt(0) };
1395
+ } catch (error) {
1396
+ console.error("Failed to get Lightning balance:", error);
1397
+ return { available: BigInt(0), pending: BigInt(0) };
1129
1398
  }
1130
- this.initialized = true;
1131
1399
  }
1132
1400
  /**
1133
- * Get the current execution mode
1401
+ * Get configuration
1402
+ */
1403
+ getConfig() {
1404
+ return { ...this.config };
1405
+ }
1406
+ /**
1407
+ * Check if wallet is initialized
1408
+ */
1409
+ isInitialized() {
1410
+ return this.initialized;
1411
+ }
1412
+ /**
1413
+ * Get contract addresses for current network
1414
+ */
1415
+ getContractAddresses() {
1416
+ return getContractAddresses(this.config.network);
1417
+ }
1418
+ };
1419
+
1420
+ // src/security/KeyManager.ts
1421
+ var KeyManager = class {
1422
+ static ALGORITHM = "AES-GCM";
1423
+ static KEY_LENGTH = 256;
1424
+ static IV_LENGTH = 12;
1425
+ static SALT_LENGTH = 16;
1426
+ static PBKDF2_ITERATIONS = 1e5;
1427
+ /**
1428
+ * Encrypt a seed phrase with a password
1429
+ */
1430
+ static async encryptSeed(seed, password) {
1431
+ const encoder = new TextEncoder();
1432
+ const seedData = encoder.encode(seed);
1433
+ const salt = crypto.getRandomValues(new Uint8Array(this.SALT_LENGTH));
1434
+ const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));
1435
+ const key = await this.deriveKey(password, salt);
1436
+ const encrypted = await crypto.subtle.encrypt(
1437
+ { name: this.ALGORITHM, iv },
1438
+ key,
1439
+ seedData
1440
+ );
1441
+ const combined = new Uint8Array(salt.length + iv.length + encrypted.byteLength);
1442
+ combined.set(salt, 0);
1443
+ combined.set(iv, salt.length);
1444
+ combined.set(new Uint8Array(encrypted), salt.length + iv.length);
1445
+ return btoa(String.fromCharCode(...combined));
1446
+ }
1447
+ /**
1448
+ * Decrypt a seed phrase with a password
1449
+ */
1450
+ static async decryptSeed(encryptedData, password) {
1451
+ const combined = new Uint8Array(
1452
+ atob(encryptedData).split("").map((c) => c.charCodeAt(0))
1453
+ );
1454
+ const salt = combined.slice(0, this.SALT_LENGTH);
1455
+ const iv = combined.slice(this.SALT_LENGTH, this.SALT_LENGTH + this.IV_LENGTH);
1456
+ const encrypted = combined.slice(this.SALT_LENGTH + this.IV_LENGTH);
1457
+ const key = await this.deriveKey(password, salt);
1458
+ const decrypted = await crypto.subtle.decrypt(
1459
+ { name: this.ALGORITHM, iv },
1460
+ key,
1461
+ encrypted
1462
+ );
1463
+ const decoder = new TextDecoder();
1464
+ return decoder.decode(decrypted);
1465
+ }
1466
+ /**
1467
+ * Derive encryption key from password using PBKDF2
1134
1468
  */
1135
- getMode() {
1136
- if (this.useNativeWdk) return "native";
1137
- if (isBrowser()) return "api";
1138
- return "api";
1469
+ static async deriveKey(password, salt) {
1470
+ const encoder = new TextEncoder();
1471
+ const passwordData = encoder.encode(password);
1472
+ const keyMaterial = await crypto.subtle.importKey(
1473
+ "raw",
1474
+ passwordData,
1475
+ "PBKDF2",
1476
+ false,
1477
+ ["deriveKey"]
1478
+ );
1479
+ return crypto.subtle.deriveKey(
1480
+ {
1481
+ name: "PBKDF2",
1482
+ salt: salt.buffer.slice(salt.byteOffset, salt.byteOffset + salt.byteLength),
1483
+ iterations: this.PBKDF2_ITERATIONS,
1484
+ hash: "SHA-256"
1485
+ },
1486
+ keyMaterial,
1487
+ { name: this.ALGORITHM, length: this.KEY_LENGTH },
1488
+ false,
1489
+ ["encrypt", "decrypt"]
1490
+ );
1139
1491
  }
1140
1492
  /**
1141
- * Check if running in browser
1493
+ * Validate a BIP-39 seed phrase (basic validation)
1142
1494
  */
1143
- isBrowserEnvironment() {
1144
- return isBrowser();
1495
+ static validateSeedPhrase(seed) {
1496
+ const words = seed.trim().split(/\s+/);
1497
+ const validWordCounts = [12, 15, 18, 21, 24];
1498
+ return validWordCounts.includes(words.length);
1145
1499
  }
1146
1500
  /**
1147
- * Generate a new BIP-39 seed phrase (12 words)
1501
+ * Generate a random encryption key (for backup purposes)
1148
1502
  */
1149
- async generateSeed() {
1150
- await this.initialize();
1151
- try {
1152
- const response = await this.apiClient.generateSeed();
1153
- if (response.success && response.seed) {
1154
- return response.seed;
1155
- }
1156
- } catch (error) {
1157
- console.warn("API seed generation failed:", error);
1503
+ static generateBackupKey() {
1504
+ const bytes = crypto.getRandomValues(new Uint8Array(32));
1505
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1506
+ }
1507
+ };
1508
+
1509
+ // src/storage/SecureStorage.ts
1510
+ var KeychainStorageAdapter = class {
1511
+ serviceName;
1512
+ constructor(serviceName = "com.zubari.wallet") {
1513
+ this.serviceName = serviceName;
1514
+ }
1515
+ async setItem(key, value) {
1516
+ if (typeof global !== "undefined" && global.KeychainModule) {
1517
+ await global.KeychainModule.setItem(this.serviceName, key, value);
1518
+ } else {
1519
+ throw new Error("Keychain not available on this platform");
1158
1520
  }
1159
- if (this.useNativeWdk && this.nativeWdkService) {
1160
- try {
1161
- const wdk = this.nativeWdkService;
1162
- return await wdk.generateSeedPhrase();
1163
- } catch (error) {
1164
- console.warn("Native WDK seed generation failed:", error);
1165
- }
1521
+ }
1522
+ async getItem(key) {
1523
+ if (typeof global !== "undefined" && global.KeychainModule) {
1524
+ return global.KeychainModule.getItem(this.serviceName, key);
1166
1525
  }
1167
- return generateSeedPhrase();
1526
+ throw new Error("Keychain not available on this platform");
1168
1527
  }
1169
- /**
1170
- * Validate a BIP-39 seed phrase
1171
- */
1172
- async validateSeed(seed) {
1173
- await this.initialize();
1174
- try {
1175
- const response = await this.apiClient.validateSeed(seed);
1176
- if (response.success) {
1177
- return response.isValid ?? false;
1178
- }
1179
- } catch (error) {
1180
- console.warn("API seed validation failed:", error);
1528
+ async removeItem(key) {
1529
+ if (typeof global !== "undefined" && global.KeychainModule) {
1530
+ await global.KeychainModule.removeItem(this.serviceName, key);
1531
+ } else {
1532
+ throw new Error("Keychain not available on this platform");
1181
1533
  }
1182
- if (this.useNativeWdk && this.nativeWdkService) {
1183
- try {
1184
- const wdk = this.nativeWdkService;
1185
- return await wdk.isValidSeed(seed);
1186
- } catch (error) {
1187
- console.warn("Native WDK seed validation failed:", error);
1188
- }
1534
+ }
1535
+ async hasItem(key) {
1536
+ const value = await this.getItem(key);
1537
+ return value !== null;
1538
+ }
1539
+ async clear() {
1540
+ if (typeof global !== "undefined" && global.KeychainModule) {
1541
+ await global.KeychainModule.clear(this.serviceName);
1542
+ } else {
1543
+ throw new Error("Keychain not available on this platform");
1189
1544
  }
1190
- return isValidSeed(seed);
1191
1545
  }
1192
- /**
1193
- * Derive address for a specific chain using WDK API
1194
- *
1195
- * For Ethereum, falls back to local derivation if API fails.
1196
- * For other chains, WDK API is required - no placeholder fallback.
1197
- */
1198
- async deriveAddress(seed, chain) {
1199
- await this.initialize();
1200
- const path = this.getDerivationPath(chain);
1201
- try {
1202
- const response = await this.apiClient.deriveAddress(seed, chain, this.config.network);
1203
- if (response.success && response.address) {
1204
- return {
1205
- chain,
1206
- address: response.address,
1207
- path: response.path || path
1208
- };
1209
- }
1210
- } catch (error) {
1211
- console.warn(`API address derivation failed for ${chain}:`, error);
1212
- if (chain === "ethereum") {
1213
- return this.deriveBrowserAddress(seed, chain);
1214
- }
1546
+ };
1547
+ var KeystoreStorageAdapter = class {
1548
+ alias;
1549
+ constructor(alias = "zubari_wallet_keys") {
1550
+ this.alias = alias;
1551
+ }
1552
+ async setItem(key, value) {
1553
+ if (typeof global !== "undefined" && global.KeystoreModule) {
1554
+ await global.KeystoreModule.setItem(this.alias, key, value);
1555
+ } else {
1556
+ throw new Error("Keystore not available on this platform");
1215
1557
  }
1216
- if (this.useNativeWdk && this.nativeWdkService) {
1217
- try {
1218
- const wdk = this.nativeWdkService;
1219
- await wdk.initialize(seed);
1220
- return await wdk.deriveAddress(chain);
1221
- } catch (error) {
1222
- console.warn(`Native WDK address derivation failed for ${chain}:`, error);
1223
- }
1558
+ }
1559
+ async getItem(key) {
1560
+ if (typeof global !== "undefined" && global.KeystoreModule) {
1561
+ return global.KeystoreModule.getItem(this.alias, key);
1224
1562
  }
1225
- if (chain === "ethereum") {
1226
- return this.deriveBrowserAddress(seed, chain);
1563
+ throw new Error("Keystore not available on this platform");
1564
+ }
1565
+ async removeItem(key) {
1566
+ if (typeof global !== "undefined" && global.KeystoreModule) {
1567
+ await global.KeystoreModule.removeItem(this.alias, key);
1568
+ } else {
1569
+ throw new Error("Keystore not available on this platform");
1570
+ }
1571
+ }
1572
+ async hasItem(key) {
1573
+ const value = await this.getItem(key);
1574
+ return value !== null;
1575
+ }
1576
+ async clear() {
1577
+ if (typeof global !== "undefined" && global.KeystoreModule) {
1578
+ await global.KeystoreModule.clear(this.alias);
1579
+ } else {
1580
+ throw new Error("Keystore not available on this platform");
1227
1581
  }
1228
- throw new Error(
1229
- `WDK API required for ${chain} address derivation. Ensure the backend is running.`
1230
- );
1582
+ }
1583
+ };
1584
+ var WebEncryptedStorageAdapter = class {
1585
+ encryptionKey = null;
1586
+ storagePrefix;
1587
+ constructor(storagePrefix = "zubari_") {
1588
+ this.storagePrefix = storagePrefix;
1231
1589
  }
1232
1590
  /**
1233
- * Derive addresses for all supported chains using WDK API
1234
- *
1235
- * Uses the backend WDK API for real cryptographically valid addresses.
1236
- * No placeholder fallback - WDK API is required for multi-chain addresses.
1591
+ * Initialize with a password-derived key
1237
1592
  */
1238
- async deriveAllAddresses(seed) {
1239
- await this.initialize();
1240
- try {
1241
- const response = await this.apiClient.deriveAllAddresses(seed, this.config.network);
1242
- if (response.success && response.addresses) {
1243
- const extractAddress = (value) => {
1244
- if (!value) return null;
1245
- if (typeof value === "string") return value;
1246
- if (typeof value === "object" && value !== null && "address" in value) {
1247
- return value.address;
1248
- }
1249
- return null;
1250
- };
1251
- return {
1252
- ethereum: extractAddress(response.addresses.ethereum),
1253
- bitcoin: extractAddress(response.addresses.bitcoin),
1254
- ton: extractAddress(response.addresses.ton),
1255
- tron: extractAddress(response.addresses.tron),
1256
- solana: extractAddress(response.addresses.solana),
1257
- spark: extractAddress(response.addresses.spark)
1258
- };
1259
- }
1260
- } catch (error) {
1261
- console.warn("API address derivation failed:", error);
1262
- }
1263
- if (this.useNativeWdk && this.nativeWdkService) {
1264
- try {
1265
- const wdk = this.nativeWdkService;
1266
- await wdk.initialize(seed);
1267
- return await wdk.deriveAllAddresses();
1268
- } catch (error) {
1269
- console.warn("Native WDK multi-chain derivation failed:", error);
1270
- }
1593
+ async initialize(password) {
1594
+ const encoder = new TextEncoder();
1595
+ const salt = this.getSalt();
1596
+ const keyMaterial = await crypto.subtle.importKey(
1597
+ "raw",
1598
+ encoder.encode(password),
1599
+ "PBKDF2",
1600
+ false,
1601
+ ["deriveKey"]
1602
+ );
1603
+ this.encryptionKey = await crypto.subtle.deriveKey(
1604
+ {
1605
+ name: "PBKDF2",
1606
+ salt: salt.buffer,
1607
+ iterations: 1e5,
1608
+ hash: "SHA-256"
1609
+ },
1610
+ keyMaterial,
1611
+ { name: "AES-GCM", length: 256 },
1612
+ false,
1613
+ ["encrypt", "decrypt"]
1614
+ );
1615
+ }
1616
+ getSalt() {
1617
+ const saltKey = `${this.storagePrefix}salt`;
1618
+ let saltHex = localStorage.getItem(saltKey);
1619
+ if (!saltHex) {
1620
+ const salt = crypto.getRandomValues(new Uint8Array(16));
1621
+ saltHex = Array.from(salt).map((b) => b.toString(16).padStart(2, "0")).join("");
1622
+ localStorage.setItem(saltKey, saltHex);
1271
1623
  }
1272
- throw new Error(
1273
- "Tether WDK API required for multi-chain address derivation. Service temporarily unavailable."
1624
+ return new Uint8Array(
1625
+ saltHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
1274
1626
  );
1275
1627
  }
1276
- /**
1277
- * Get balances for all chains
1278
- */
1279
- async getAllBalances(seed) {
1280
- await this.initialize();
1281
- try {
1282
- const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/balances`, {
1283
- method: "POST",
1284
- headers: { "Content-Type": "application/json" },
1285
- body: JSON.stringify({ seed, network: this.config.network })
1286
- });
1287
- if (response.ok) {
1288
- const data = await response.json();
1289
- if (data.success) {
1290
- return data.balances;
1291
- }
1292
- }
1293
- } catch (error) {
1294
- console.warn("Failed to fetch balances:", error);
1628
+ async setItem(key, value) {
1629
+ if (!this.encryptionKey) {
1630
+ throw new Error("Storage not initialized. Call initialize() first.");
1295
1631
  }
1296
- return {};
1632
+ const encoder = new TextEncoder();
1633
+ const iv = crypto.getRandomValues(new Uint8Array(12));
1634
+ const encrypted = await crypto.subtle.encrypt(
1635
+ { name: "AES-GCM", iv },
1636
+ this.encryptionKey,
1637
+ encoder.encode(value)
1638
+ );
1639
+ const combined = new Uint8Array(iv.length + encrypted.byteLength);
1640
+ combined.set(iv);
1641
+ combined.set(new Uint8Array(encrypted), iv.length);
1642
+ const base64 = btoa(String.fromCharCode(...combined));
1643
+ localStorage.setItem(`${this.storagePrefix}${key}`, base64);
1297
1644
  }
1298
- /**
1299
- * Get fee rates for a chain
1300
- */
1301
- async getFeeRates(seed, chain) {
1302
- await this.initialize();
1303
- try {
1304
- const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/fee-rates`, {
1305
- method: "POST",
1306
- headers: { "Content-Type": "application/json" },
1307
- body: JSON.stringify({ seed, chain, network: this.config.network })
1308
- });
1309
- if (response.ok) {
1310
- const data = await response.json();
1311
- if (data.success && data.feeRates) {
1312
- return data.feeRates;
1313
- }
1314
- }
1315
- } catch (error) {
1316
- console.warn(`Failed to fetch fee rates for ${chain}:`, error);
1645
+ async getItem(key) {
1646
+ if (!this.encryptionKey) {
1647
+ throw new Error("Storage not initialized. Call initialize() first.");
1317
1648
  }
1318
- return { slow: "0", normal: "0", fast: "0" };
1319
- }
1320
- /**
1321
- * Estimate transaction fee
1322
- */
1323
- async estimateFee(seed, chain, to, amount) {
1324
- await this.initialize();
1649
+ const base64 = localStorage.getItem(`${this.storagePrefix}${key}`);
1650
+ if (!base64) return null;
1325
1651
  try {
1326
- const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/estimate-fee`, {
1327
- method: "POST",
1328
- headers: { "Content-Type": "application/json" },
1329
- body: JSON.stringify({ seed, chain, to, amount, network: this.config.network })
1330
- });
1331
- if (response.ok) {
1332
- const data = await response.json();
1333
- if (data.success) {
1334
- return { fee: data.fee, symbol: data.symbol };
1335
- }
1336
- }
1337
- } catch (error) {
1338
- console.warn(`Failed to estimate fee for ${chain}:`, error);
1652
+ const combined = new Uint8Array(
1653
+ atob(base64).split("").map((c) => c.charCodeAt(0))
1654
+ );
1655
+ const iv = combined.slice(0, 12);
1656
+ const encrypted = combined.slice(12);
1657
+ const decrypted = await crypto.subtle.decrypt(
1658
+ { name: "AES-GCM", iv },
1659
+ this.encryptionKey,
1660
+ encrypted
1661
+ );
1662
+ const decoder = new TextDecoder();
1663
+ return decoder.decode(decrypted);
1664
+ } catch {
1665
+ return null;
1339
1666
  }
1340
- return { fee: "0", symbol: this.getChainSymbol(chain) };
1341
1667
  }
1342
- /**
1343
- * Send a transaction
1344
- */
1345
- async sendTransaction(seed, chain, to, amount) {
1346
- await this.initialize();
1347
- try {
1348
- const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/send`, {
1349
- method: "POST",
1350
- headers: { "Content-Type": "application/json" },
1351
- body: JSON.stringify({ seed, chain, to, amount, network: this.config.network })
1352
- });
1353
- if (response.ok) {
1354
- const data = await response.json();
1355
- let txHash = data.txHash || data.transactionHash || data.hash;
1356
- if (txHash && typeof txHash === "object" && "hash" in txHash) {
1357
- txHash = txHash.hash;
1358
- }
1359
- if (chain === "ethereum" && txHash && (typeof txHash !== "string" || !txHash.startsWith("0x") || txHash.length !== 66)) {
1360
- console.warn(`Invalid Ethereum tx hash format: ${txHash} (length: ${txHash?.length}, expected: 66)`);
1361
- }
1362
- return {
1363
- success: data.success,
1364
- txHash,
1365
- from: data.from,
1366
- to: data.to,
1367
- amount: data.amount,
1368
- chain: data.chain,
1369
- network: data.network
1370
- };
1668
+ async removeItem(key) {
1669
+ localStorage.removeItem(`${this.storagePrefix}${key}`);
1670
+ }
1671
+ async hasItem(key) {
1672
+ return localStorage.getItem(`${this.storagePrefix}${key}`) !== null;
1673
+ }
1674
+ async clear() {
1675
+ const keysToRemove = [];
1676
+ for (let i = 0; i < localStorage.length; i++) {
1677
+ const key = localStorage.key(i);
1678
+ if (key?.startsWith(this.storagePrefix)) {
1679
+ keysToRemove.push(key);
1371
1680
  }
1372
- const errorData = await response.json().catch(() => ({}));
1373
- return {
1374
- success: false,
1375
- error: errorData.error || `HTTP ${response.status}`
1376
- };
1377
- } catch (error) {
1378
- return {
1379
- success: false,
1380
- error: error instanceof Error ? error.message : "Transaction failed"
1381
- };
1382
1681
  }
1682
+ keysToRemove.forEach((key) => localStorage.removeItem(key));
1383
1683
  }
1384
- /**
1385
- * Get the network configuration
1386
- */
1387
- getNetwork() {
1388
- return this.config.network;
1389
- }
1390
- /**
1391
- * Get API URL
1392
- */
1393
- getApiUrl() {
1394
- return this.config.apiUrl;
1684
+ };
1685
+ var MemoryStorageAdapter = class {
1686
+ storage = /* @__PURE__ */ new Map();
1687
+ async setItem(key, value) {
1688
+ this.storage.set(key, value);
1395
1689
  }
1396
- // ==========================================
1397
- // Private Helper Methods
1398
- // ==========================================
1399
- getDerivationPath(chain) {
1400
- const paths = {
1401
- bitcoin: this.config.network === "testnet" ? "m/84'/1'/0'/0/0" : "m/84'/0'/0'/0/0",
1402
- ethereum: "m/44'/60'/0'/0/0",
1403
- ton: "m/44'/607'/0'/0'/0'",
1404
- tron: "m/44'/195'/0'/0/0",
1405
- solana: "m/44'/501'/0'/0'",
1406
- spark: "m/44'/998'/0'/0/0"
1407
- };
1408
- return paths[chain];
1690
+ async getItem(key) {
1691
+ return this.storage.get(key) || null;
1409
1692
  }
1410
- getChainSymbol(chain) {
1411
- const symbols = {
1412
- ethereum: "ETH",
1413
- bitcoin: "BTC",
1414
- ton: "TON",
1415
- tron: "TRX",
1416
- solana: "SOL",
1417
- spark: "SAT"
1418
- };
1419
- return symbols[chain];
1693
+ async removeItem(key) {
1694
+ this.storage.delete(key);
1420
1695
  }
1421
- /**
1422
- * Derive address using browser-compatible libraries
1423
- */
1424
- async deriveBrowserAddress(seed, chain) {
1425
- const path = this.getDerivationPath(chain);
1426
- try {
1427
- let address;
1428
- switch (chain) {
1429
- case "ethereum":
1430
- address = deriveEthereumAddress(seed);
1431
- break;
1432
- case "bitcoin":
1433
- address = deriveBitcoinAddress(seed, this.config.network);
1434
- break;
1435
- case "tron":
1436
- address = deriveTronAddress(seed);
1437
- break;
1438
- case "spark":
1439
- address = deriveSparkAddress(seed, this.config.network);
1440
- break;
1441
- case "solana":
1442
- address = await deriveSolanaAddress(seed);
1443
- break;
1444
- case "ton":
1445
- address = await deriveTonAddress(seed);
1446
- break;
1447
- default:
1448
- throw new Error(`Unsupported chain: ${chain}`);
1449
- }
1450
- return { chain, address, path };
1451
- } catch (error) {
1452
- console.error(`Browser derivation failed for ${chain}:`, error);
1453
- throw error;
1454
- }
1696
+ async hasItem(key) {
1697
+ return this.storage.has(key);
1455
1698
  }
1456
- /**
1457
- * Derive all addresses using browser-compatible libraries
1458
- */
1459
- async deriveAllBrowserAddresses(seed) {
1460
- return deriveAllAddresses(seed, this.config.network);
1699
+ async clear() {
1700
+ this.storage.clear();
1461
1701
  }
1462
1702
  };
1463
- var defaultService = null;
1464
- function getZubariWdkService(config) {
1465
- if (!defaultService || config && config.network !== defaultService.getNetwork()) {
1466
- defaultService = new ZubariWdkService(config);
1703
+ function createSecureStorage() {
1704
+ if (typeof global !== "undefined" && global.nativeModuleProxy !== void 0) {
1705
+ const Platform = global.Platform;
1706
+ if (Platform?.OS === "ios") {
1707
+ return new KeychainStorageAdapter();
1708
+ } else if (Platform?.OS === "android") {
1709
+ return new KeystoreStorageAdapter();
1710
+ }
1467
1711
  }
1468
- return defaultService;
1712
+ if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
1713
+ return new WebEncryptedStorageAdapter();
1714
+ }
1715
+ return new MemoryStorageAdapter();
1469
1716
  }
1470
1717
 
1471
1718
  // src/wallet/WalletManager.ts