@zubari/sdk 0.2.7 → 0.3.0

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