@zubari/sdk 0.4.5 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/{TransactionService-Djonkbp4.d.ts → TransactionService-BtWUjKt_.d.ts} +14 -2
  2. package/dist/{TransactionService-1Jt8ZRqO.d.mts → TransactionService-Lr_WS6iR.d.mts} +14 -2
  3. package/dist/{WalletManager-DfvFJ-mk.d.ts → WalletManager-DQQwVkoa.d.ts} +4 -0
  4. package/dist/{WalletManager-j0tgNIKi.d.mts → WalletManager-Sbpx4E1-.d.mts} +4 -0
  5. package/dist/{contracts-BahTuxZj.d.mts → contracts-B842YprC.d.mts} +2 -2
  6. package/dist/{contracts-D7rVmNJy.d.ts → contracts-s_CDIruh.d.ts} +2 -2
  7. package/dist/index.d.mts +3 -3
  8. package/dist/index.d.ts +3 -3
  9. package/dist/index.js +305 -23
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +306 -24
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/protocols/index.js +2 -2
  14. package/dist/protocols/index.js.map +1 -1
  15. package/dist/protocols/index.mjs +2 -2
  16. package/dist/protocols/index.mjs.map +1 -1
  17. package/dist/react/index.d.mts +2 -2
  18. package/dist/react/index.d.ts +2 -2
  19. package/dist/react/index.js +303 -21
  20. package/dist/react/index.js.map +1 -1
  21. package/dist/react/index.mjs +304 -22
  22. package/dist/react/index.mjs.map +1 -1
  23. package/dist/services/index.d.mts +1 -1
  24. package/dist/services/index.d.ts +1 -1
  25. package/dist/services/index.js +132 -8
  26. package/dist/services/index.js.map +1 -1
  27. package/dist/services/index.mjs +132 -8
  28. package/dist/services/index.mjs.map +1 -1
  29. package/dist/wallet/index.d.mts +2 -2
  30. package/dist/wallet/index.d.ts +2 -2
  31. package/dist/wallet/index.js +303 -21
  32. package/dist/wallet/index.js.map +1 -1
  33. package/dist/wallet/index.mjs +304 -22
  34. package/dist/wallet/index.mjs.map +1 -1
  35. package/package.json +1 -1
@@ -5,7 +5,7 @@ import { HDKey } from '@scure/bip32';
5
5
  import { bech32, base58check } from '@scure/base';
6
6
  import { sha256 } from '@noble/hashes/sha256';
7
7
  import { ripemd160 } from '@noble/hashes/ripemd160';
8
- import { createPublicClient, http, formatEther } from 'viem';
8
+ import { createPublicClient, http, formatEther, getAddress } from 'viem';
9
9
  import { mainnet, sepolia } from 'viem/chains';
10
10
 
11
11
  // src/config/networks.ts
@@ -113,6 +113,38 @@ var TESTNET_NETWORKS = {
113
113
  name: "Solana Devnet",
114
114
  rpcUrl: "https://api.devnet.solana.com",
115
115
  explorerUrl: "https://solscan.io?cluster=devnet"
116
+ },
117
+ tron: {
118
+ name: "TRON Nile Testnet",
119
+ chainId: 3448148188,
120
+ rpcUrl: "https://nile.trongrid.io",
121
+ explorerUrl: "https://nile.tronscan.org"
122
+ },
123
+ ton: {
124
+ name: "TON Testnet",
125
+ rpcUrl: "https://testnet.toncenter.com/api/v2",
126
+ explorerUrl: "https://testnet.tonscan.org"
127
+ }
128
+ };
129
+ var USDT_ADDRESSES = {
130
+ ethereum: {
131
+ mainnet: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
132
+ testnet: "0x7169D38820dfd117C3FA1f22a697dBA58d90BA06"
133
+ // Sepolia (Test Tether USD)
134
+ },
135
+ tron: {
136
+ mainnet: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
137
+ testnet: "TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf"
138
+ // Nile testnet
139
+ },
140
+ solana: {
141
+ mainnet: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
142
+ testnet: ""
143
+ // No official USDT on devnet
144
+ },
145
+ ton: {
146
+ mainnet: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs",
147
+ testnet: "kQD0GKBM8ZbryVk2aESmzfU6b9b_8era_IkvBSELujFZPsyy"
116
148
  }
117
149
  };
118
150
  var DERIVATION_PATHS = {
@@ -534,6 +566,60 @@ function generateSeedPhrase() {
534
566
 
535
567
  // src/services/ZubariWdkService.ts
536
568
  var DEFAULT_API_URL2 = "https://ckgwifsxka.us-east-2.awsapprunner.com";
569
+ var CHAIN_ERROR_MESSAGES = {
570
+ ethereum: {
571
+ "insufficient funds": "INSUFFICIENT_FUNDS",
572
+ "nonce too low": "NONCE_TOO_LOW",
573
+ "gas too low": "GAS_TOO_LOW",
574
+ "replacement transaction underpriced": "GAS_TOO_LOW",
575
+ "transaction underpriced": "GAS_TOO_LOW",
576
+ "invalid address": "INVALID_ADDRESS"
577
+ },
578
+ bitcoin: {
579
+ "insufficient funds": "INSUFFICIENT_FUNDS",
580
+ "dust": "DUST_AMOUNT",
581
+ "mempool": "MEMPOOL_FULL",
582
+ "invalid address": "INVALID_ADDRESS"
583
+ },
584
+ solana: {
585
+ "insufficient funds": "INSUFFICIENT_FUNDS",
586
+ "invalid account": "INVALID_ADDRESS",
587
+ "blockhash not found": "NETWORK_ERROR"
588
+ },
589
+ ton: {
590
+ "insufficient funds": "INSUFFICIENT_FUNDS",
591
+ "invalid address": "INVALID_ADDRESS"
592
+ },
593
+ tron: {
594
+ "insufficient funds": "INSUFFICIENT_FUNDS",
595
+ "invalid address": "INVALID_ADDRESS",
596
+ "bandwidth": "GAS_TOO_LOW"
597
+ },
598
+ spark: {
599
+ "insufficient funds": "INSUFFICIENT_FUNDS",
600
+ "invoice expired": "TIMEOUT",
601
+ "no route": "NETWORK_ERROR"
602
+ }
603
+ };
604
+ function parseChainError(chain, errorMessage) {
605
+ const errorLower = errorMessage.toLowerCase();
606
+ const chainErrors = CHAIN_ERROR_MESSAGES[chain];
607
+ for (const [pattern, code] of Object.entries(chainErrors)) {
608
+ if (errorLower.includes(pattern)) {
609
+ return code;
610
+ }
611
+ }
612
+ if (errorLower.includes("timeout") || errorLower.includes("timed out")) {
613
+ return "TIMEOUT";
614
+ }
615
+ if (errorLower.includes("network") || errorLower.includes("connection")) {
616
+ return "NETWORK_ERROR";
617
+ }
618
+ if (errorLower.includes("rejected") || errorLower.includes("denied")) {
619
+ return "REJECTED";
620
+ }
621
+ return "UNKNOWN";
622
+ }
537
623
  function isBrowser() {
538
624
  return typeof window !== "undefined" && typeof window.document !== "undefined";
539
625
  }
@@ -808,47 +894,117 @@ var ZubariWdkService = class {
808
894
  return { fee: "0", symbol: this.getChainSymbol(chain) };
809
895
  }
810
896
  /**
811
- * Send a transaction
897
+ * Send a transaction on any supported chain
898
+ *
899
+ * @param seed - BIP-39 seed phrase
900
+ * @param chain - Target blockchain (ethereum, bitcoin, solana, ton, tron, spark)
901
+ * @param to - Recipient address
902
+ * @param amount - Amount to send (in native units: ETH, BTC, SOL, etc.)
903
+ * @returns Transaction result with hash on success, or error details on failure
812
904
  */
813
905
  async sendTransaction(seed, chain, to, amount) {
814
906
  await this.initialize();
907
+ const startTime = Date.now();
908
+ console.log(`[ZubariWdkService] Sending ${chain} transaction`, {
909
+ to: `${to.slice(0, 10)}...${to.slice(-6)}`,
910
+ amount,
911
+ network: this.config.network
912
+ });
815
913
  try {
816
914
  const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/send`, {
817
915
  method: "POST",
818
916
  headers: { "Content-Type": "application/json" },
819
917
  body: JSON.stringify({ seed, chain, to, amount, network: this.config.network })
820
918
  });
919
+ const elapsed = Date.now() - startTime;
821
920
  if (response.ok) {
822
921
  const data = await response.json();
823
922
  let txHash = data.txHash || data.transactionHash || data.hash;
824
923
  if (txHash && typeof txHash === "object" && "hash" in txHash) {
825
924
  txHash = txHash.hash;
826
925
  }
827
- if (chain === "ethereum" && txHash && (typeof txHash !== "string" || !txHash.startsWith("0x") || txHash.length !== 66)) {
828
- console.warn(`Invalid Ethereum tx hash format: ${txHash} (length: ${txHash?.length}, expected: 66)`);
926
+ if (txHash) {
927
+ const isValid = this.validateTxHash(chain, txHash);
928
+ if (!isValid) {
929
+ console.warn(`[ZubariWdkService] Invalid ${chain} tx hash format:`, txHash);
930
+ }
931
+ }
932
+ console.log(`[ZubariWdkService] ${chain} transaction ${data.success ? "SUCCESS" : "FAILED"}`, {
933
+ txHash: txHash ? `${txHash.slice(0, 16)}...` : "N/A",
934
+ elapsed: `${elapsed}ms`
935
+ });
936
+ if (!data.success) {
937
+ const errorCode2 = parseChainError(chain, data.error || "");
938
+ return {
939
+ success: false,
940
+ error: data.error,
941
+ errorCode: errorCode2,
942
+ chain
943
+ };
829
944
  }
830
945
  return {
831
- success: data.success,
946
+ success: true,
832
947
  txHash,
833
948
  from: data.from,
834
949
  to: data.to,
835
950
  amount: data.amount,
836
- chain: data.chain,
837
- network: data.network
951
+ chain: data.chain || chain,
952
+ network: data.network || this.config.network
838
953
  };
839
954
  }
840
955
  const errorData = await response.json().catch(() => ({}));
956
+ const errorMessage = errorData.error || `HTTP ${response.status}`;
957
+ const errorCode = parseChainError(chain, errorMessage);
958
+ console.error(`[ZubariWdkService] ${chain} transaction FAILED`, {
959
+ status: response.status,
960
+ error: errorMessage,
961
+ errorCode,
962
+ elapsed: `${elapsed}ms`
963
+ });
841
964
  return {
842
965
  success: false,
843
- error: errorData.error || `HTTP ${response.status}`
966
+ error: errorMessage,
967
+ errorCode,
968
+ chain
844
969
  };
845
970
  } catch (error) {
971
+ const elapsed = Date.now() - startTime;
972
+ const errorMessage = error instanceof Error ? error.message : "Transaction failed";
973
+ const errorCode = parseChainError(chain, errorMessage);
974
+ console.error(`[ZubariWdkService] ${chain} transaction ERROR`, {
975
+ error: errorMessage,
976
+ errorCode,
977
+ elapsed: `${elapsed}ms`
978
+ });
846
979
  return {
847
980
  success: false,
848
- error: error instanceof Error ? error.message : "Transaction failed"
981
+ error: errorMessage,
982
+ errorCode,
983
+ chain
849
984
  };
850
985
  }
851
986
  }
987
+ /**
988
+ * Validate transaction hash format for a specific chain
989
+ */
990
+ validateTxHash(chain, txHash) {
991
+ switch (chain) {
992
+ case "ethereum":
993
+ return /^0x[a-fA-F0-9]{64}$/.test(txHash);
994
+ case "bitcoin":
995
+ return /^[a-fA-F0-9]{64}$/.test(txHash);
996
+ case "solana":
997
+ return /^[1-9A-HJ-NP-Za-km-z]{80,90}$/.test(txHash);
998
+ case "ton":
999
+ return txHash.length >= 40;
1000
+ case "tron":
1001
+ return /^[a-fA-F0-9]{64}$/.test(txHash);
1002
+ case "spark":
1003
+ return txHash.length >= 32;
1004
+ default:
1005
+ return true;
1006
+ }
1007
+ }
852
1008
  /**
853
1009
  * Get the network configuration
854
1010
  */
@@ -1835,6 +1991,19 @@ async function getPriceForChain(chain) {
1835
1991
  const prices = await fetchPrices();
1836
1992
  return prices[chain] || 0;
1837
1993
  }
1994
+ function tonFriendlyToRaw(addr) {
1995
+ if (addr.includes(":")) return addr;
1996
+ try {
1997
+ const b64 = addr.replace(/-/g, "+").replace(/_/g, "/");
1998
+ const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
1999
+ if (bytes.length !== 36) return addr;
2000
+ const workchain = bytes[1] === 255 ? -1 : bytes[1];
2001
+ const hash = Array.from(bytes.slice(2, 34)).map((b) => b.toString(16).padStart(2, "0")).join("");
2002
+ return `${workchain}:${hash}`;
2003
+ } catch {
2004
+ return addr;
2005
+ }
2006
+ }
1838
2007
  var STORAGE_KEYS = {
1839
2008
  ENCRYPTED_SEED: "encrypted_seed",
1840
2009
  ACTIVE_WALLET: "active_wallet",
@@ -2339,26 +2508,53 @@ var WalletManager = class _WalletManager {
2339
2508
  }
2340
2509
  const networkConfig = this.getChainConfig(chain);
2341
2510
  let balance = "0";
2511
+ const tokenBalances = {};
2342
2512
  if (chain === "ethereum") {
2343
2513
  const viemChain = this.config.network === "mainnet" ? mainnet : sepolia;
2344
- console.log(`[WalletManager] Fetching ${chain} balance for ${address} using RPC: ${this.config.rpcUrl}`);
2514
+ const isTestnet = this.config.network !== "mainnet";
2345
2515
  const client = createPublicClient({
2346
2516
  chain: viemChain,
2347
2517
  transport: http(this.config.rpcUrl, {
2348
2518
  timeout: 15e3,
2349
- // 15 second timeout
2350
2519
  retryCount: 2,
2351
2520
  retryDelay: 1e3
2352
2521
  })
2353
2522
  });
2354
- try {
2355
- const rawBalance = await client.getBalance({
2356
- address
2357
- });
2358
- balance = formatEther(rawBalance);
2359
- console.log(`[WalletManager] ${chain} balance fetched: ${balance} (raw: ${rawBalance})`);
2360
- } catch (error) {
2361
- console.error(`[WalletManager] Failed to fetch ${chain} balance from ${this.config.rpcUrl}:`, error);
2523
+ const usdtAddr = USDT_ADDRESSES.ethereum?.[isTestnet ? "testnet" : "mainnet"];
2524
+ const erc20BalanceOfAbi = [{
2525
+ type: "function",
2526
+ name: "balanceOf",
2527
+ stateMutability: "view",
2528
+ inputs: [{ name: "account", type: "address" }],
2529
+ outputs: [{ name: "", type: "uint256" }]
2530
+ }];
2531
+ const checksumAddr = getAddress(address);
2532
+ const [ethResult, usdtResult] = await Promise.allSettled([
2533
+ client.getBalance({ address: checksumAddr }),
2534
+ usdtAddr ? client.readContract({
2535
+ address: getAddress(usdtAddr),
2536
+ abi: erc20BalanceOfAbi,
2537
+ functionName: "balanceOf",
2538
+ args: [checksumAddr]
2539
+ }) : Promise.resolve(null)
2540
+ ]);
2541
+ if (ethResult.status === "fulfilled") {
2542
+ balance = formatEther(ethResult.value);
2543
+ } else {
2544
+ console.error(`[WalletManager] Failed to fetch ETH balance:`, ethResult.reason);
2545
+ }
2546
+ if (usdtResult.status === "fulfilled" && usdtResult.value != null) {
2547
+ try {
2548
+ const rawUsdt = BigInt(usdtResult.value);
2549
+ const usdtAmount = Number(rawUsdt) / 1e6;
2550
+ if (usdtAmount > 0) {
2551
+ tokenBalances.USDT = { balance: usdtAmount.toFixed(6), balanceUsd: usdtAmount };
2552
+ }
2553
+ } catch (err) {
2554
+ console.warn("[WalletManager] Failed to parse ETH USDT balance:", err);
2555
+ }
2556
+ } else if (usdtResult.status === "rejected") {
2557
+ console.warn("[WalletManager] Failed to fetch ETH USDT balance:", usdtResult.reason);
2362
2558
  }
2363
2559
  } else if (chain === "bitcoin") {
2364
2560
  const isMainnet = this.config.network === "mainnet" || address.startsWith("bc1") || address.startsWith("1") || address.startsWith("3");
@@ -2414,8 +2610,41 @@ var WalletManager = class _WalletManager {
2414
2610
  } catch (error) {
2415
2611
  console.warn(`Failed to fetch ${chain} balance:`, error);
2416
2612
  }
2613
+ const isTestnet = this.config.network !== "mainnet";
2614
+ const usdtMint = USDT_ADDRESSES.solana?.[isTestnet ? "testnet" : "mainnet"];
2615
+ if (usdtMint) {
2616
+ try {
2617
+ const tokenResponse = await fetch(rpcUrl, {
2618
+ method: "POST",
2619
+ headers: { "Content-Type": "application/json" },
2620
+ body: JSON.stringify({
2621
+ jsonrpc: "2.0",
2622
+ id: 2,
2623
+ method: "getTokenAccountsByOwner",
2624
+ params: [
2625
+ address,
2626
+ { mint: usdtMint },
2627
+ { encoding: "jsonParsed" }
2628
+ ]
2629
+ })
2630
+ });
2631
+ if (tokenResponse.ok) {
2632
+ const tokenData = await tokenResponse.json();
2633
+ const accounts = tokenData.result?.value;
2634
+ if (accounts && accounts.length > 0) {
2635
+ const uiAmount = accounts[0].account?.data?.parsed?.info?.tokenAmount?.uiAmount;
2636
+ if (uiAmount && uiAmount > 0) {
2637
+ tokenBalances.USDT = { balance: uiAmount.toFixed(6), balanceUsd: uiAmount };
2638
+ }
2639
+ }
2640
+ }
2641
+ } catch (error) {
2642
+ console.warn("Failed to fetch Solana USDT balance:", error);
2643
+ }
2644
+ }
2417
2645
  } else if (chain === "tron") {
2418
- const baseUrl = this.config.network === "mainnet" ? "https://api.trongrid.io" : "https://api.shasta.trongrid.io";
2646
+ const tronConfig = getNetworkConfig("tron", this.config.network !== "mainnet");
2647
+ const baseUrl = tronConfig.rpcUrl;
2419
2648
  try {
2420
2649
  const response = await fetch(`${baseUrl}/v1/accounts/${address}`, {
2421
2650
  headers: { "Accept": "application/json" }
@@ -2425,12 +2654,28 @@ var WalletManager = class _WalletManager {
2425
2654
  if (data.data?.[0]?.balance !== void 0) {
2426
2655
  balance = (data.data[0].balance / 1e6).toFixed(6);
2427
2656
  }
2657
+ const isTestnet = this.config.network !== "mainnet";
2658
+ const usdtAddr = USDT_ADDRESSES.tron?.[isTestnet ? "testnet" : "mainnet"];
2659
+ if (usdtAddr && data.data?.[0]?.trc20) {
2660
+ const trc20List = data.data[0].trc20;
2661
+ for (const tokenObj of trc20List) {
2662
+ if (tokenObj[usdtAddr]) {
2663
+ const rawUsdtBalance = BigInt(tokenObj[usdtAddr]);
2664
+ const usdtAmount = Number(rawUsdtBalance) / 1e6;
2665
+ if (usdtAmount > 0) {
2666
+ tokenBalances.USDT = { balance: usdtAmount.toFixed(6), balanceUsd: usdtAmount };
2667
+ }
2668
+ break;
2669
+ }
2670
+ }
2671
+ }
2428
2672
  }
2429
2673
  } catch (error) {
2430
2674
  console.warn(`Failed to fetch ${chain} balance:`, error);
2431
2675
  }
2432
2676
  } else if (chain === "ton") {
2433
- const baseUrl = this.config.network === "mainnet" ? "https://toncenter.com/api/v2" : "https://testnet.toncenter.com/api/v2";
2677
+ const isTestnet = this.config.network !== "mainnet";
2678
+ const baseUrl = isTestnet ? "https://testnet.toncenter.com/api/v2" : "https://toncenter.com/api/v2";
2434
2679
  try {
2435
2680
  const response = await fetch(`${baseUrl}/getAddressBalance?address=${address}`, {
2436
2681
  headers: { "Accept": "application/json" }
@@ -2446,6 +2691,42 @@ var WalletManager = class _WalletManager {
2446
2691
  } catch (error) {
2447
2692
  console.warn(`Failed to fetch ${chain} balance:`, error);
2448
2693
  }
2694
+ const usdtJetton = USDT_ADDRESSES.ton?.[isTestnet ? "testnet" : "mainnet"];
2695
+ if (usdtJetton) {
2696
+ const tonapiBaseUrl = isTestnet ? "https://testnet.tonapi.io/v2" : "https://tonapi.io/v2";
2697
+ try {
2698
+ const rawAddr = tonFriendlyToRaw(address);
2699
+ const jettonResponse = await fetch(
2700
+ `${tonapiBaseUrl}/accounts/${encodeURIComponent(rawAddr)}/jettons?currencies=usd`,
2701
+ { headers: { "Accept": "application/json" } }
2702
+ );
2703
+ if (jettonResponse.ok) {
2704
+ const jettonData = await jettonResponse.json();
2705
+ const balances = jettonData.balances;
2706
+ if (balances && balances.length > 0) {
2707
+ for (const jb of balances) {
2708
+ const jettonAddr = jb.jetton?.address;
2709
+ if (jettonAddr) {
2710
+ const usdtRaw = tonFriendlyToRaw(usdtJetton);
2711
+ if (jettonAddr.toLowerCase() === usdtRaw.toLowerCase()) {
2712
+ const rawBalance = jb.balance;
2713
+ if (rawBalance) {
2714
+ const decimals = jb.jetton?.decimals || 6;
2715
+ const usdtAmount = Number(BigInt(rawBalance)) / Math.pow(10, decimals);
2716
+ if (usdtAmount > 0) {
2717
+ tokenBalances.USDT = { balance: usdtAmount.toFixed(6), balanceUsd: usdtAmount };
2718
+ }
2719
+ }
2720
+ break;
2721
+ }
2722
+ }
2723
+ }
2724
+ }
2725
+ }
2726
+ } catch (error) {
2727
+ console.warn("Failed to fetch TON USDT jetton balance:", error);
2728
+ }
2729
+ }
2449
2730
  } else if (chain === "spark") {
2450
2731
  try {
2451
2732
  const response = await fetch(`${this.config.apiUrl}/api/wallets/wdk/balance`, {
@@ -2477,7 +2758,8 @@ var WalletManager = class _WalletManager {
2477
2758
  balance,
2478
2759
  balanceUsd,
2479
2760
  address,
2480
- decimals: networkConfig.nativeCurrency.decimals
2761
+ decimals: networkConfig.nativeCurrency.decimals,
2762
+ ...Object.keys(tokenBalances).length > 0 ? { tokenBalances } : {}
2481
2763
  };
2482
2764
  }
2483
2765
  /**