@silentswap/react 0.1.50 → 0.1.52

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.
@@ -12,6 +12,7 @@ const CHAIN_WHITELIST_GLOBAL = new Set([
12
12
  'eip155:1',
13
13
  'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
14
14
  'bip122:000000000019d6689c085ae165831e93',
15
+ 'tron:0x2b6653dc',
15
16
  'eip155:56',
16
17
  'eip155:8453',
17
18
  'eip155:42161',
@@ -11,7 +11,7 @@ export type BalancesContextType = {
11
11
  totalUsdValue: number;
12
12
  errors: Record<string, string>;
13
13
  refetch: () => Promise<void>;
14
- refetchChains: (chainIds: (number | 'solana' | 'bitcoin')[]) => Promise<void>;
14
+ refetchChains: (chainIds: (number | 'solana' | 'bitcoin' | 'tron')[]) => Promise<void>;
15
15
  };
16
16
  /**
17
17
  * Provider for user balances across all supported chains
@@ -23,6 +23,8 @@ export declare const BalancesProvider: React.FC<{
23
23
  solanaRpcUrl?: string;
24
24
  bitcoinAddress?: string;
25
25
  bitcoinRpcUrl?: string;
26
+ tronAddress?: string;
27
+ tronRpcUrl?: string;
26
28
  }>;
27
29
  /**
28
30
  * Hook to access user balances
@@ -8,7 +8,7 @@ import { getAssociatedTokenAddress } from '@solana/spl-token';
8
8
  import { PublicKey } from '@solana/web3.js';
9
9
  import { useAssetsContext } from './AssetsContext.js';
10
10
  import { usePrices } from '../hooks/usePrices.js';
11
- import { isSolanaAsset, parseSolanaCaip19, isSolanaNativeToken, isSplToken, isEvmNativeToken, isBitcoinAsset, isBitcoinMainnetAsset, SB58_CHAIN_ID_SOLANA_MAINNET, A_VIEM_CHAINS, BITCOIN_CHAIN_ID, H_RPCS, } from '@silentswap/sdk';
11
+ import { isSolanaAsset, parseSolanaCaip19, isSolanaNativeToken, isSplToken, isEvmNativeToken, isBitcoinAsset, isBitcoinMainnetAsset, SB58_CHAIN_ID_SOLANA_MAINNET, A_VIEM_CHAINS, BITCOIN_CHAIN_ID, TRON_CHAIN_ID, H_RPCS, } from '@silentswap/sdk';
12
12
  const BalancesContext = createContext(undefined);
13
13
  // Utility: Create viem client with custom RPC endpoints where available
14
14
  // Uses H_RPCS from @silentswap/sdk (rpc.ts) as the single source of truth
@@ -75,6 +75,79 @@ const separateBitcoinAssets = (bitcoinAssets) => {
75
75
  }
76
76
  return { a_assets_native };
77
77
  };
78
+ const TRON_BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
79
+ function decodeBase58(input) {
80
+ let value = 0n;
81
+ for (const char of input) {
82
+ const idx = TRON_BASE58_ALPHABET.indexOf(char);
83
+ if (idx < 0)
84
+ throw new Error('Invalid base58 character');
85
+ value = value * 58n + BigInt(idx);
86
+ }
87
+ const bytes = [];
88
+ while (value > 0n) {
89
+ bytes.push(Number(value % 256n));
90
+ value /= 256n;
91
+ }
92
+ bytes.reverse();
93
+ let leadingZeroes = 0;
94
+ while (leadingZeroes < input.length && input[leadingZeroes] === '1') {
95
+ leadingZeroes++;
96
+ }
97
+ const out = new Uint8Array(leadingZeroes + bytes.length);
98
+ for (let i = 0; i < bytes.length; i++)
99
+ out[leadingZeroes + i] = bytes[i];
100
+ return out;
101
+ }
102
+ function tronAddressToHex20(address) {
103
+ const trimmed = address.trim();
104
+ const lower = trimmed.toLowerCase();
105
+ if (lower.startsWith('0x') && lower.length === 42) {
106
+ return lower;
107
+ }
108
+ if (lower.startsWith('0x41') && lower.length === 44) {
109
+ return `0x${lower.slice(4)}`;
110
+ }
111
+ if (lower.startsWith('41') && lower.length === 42) {
112
+ return `0x${lower.slice(2)}`;
113
+ }
114
+ if (!trimmed.startsWith('T'))
115
+ return null;
116
+ try {
117
+ const decoded = decodeBase58(trimmed);
118
+ // Tron base58check payload: 21 bytes (0x41 + 20-byte address) + 4 checksum bytes
119
+ if (decoded.length < 25)
120
+ return null;
121
+ const payload = decoded.slice(0, decoded.length - 4);
122
+ if (payload.length !== 21 || payload[0] !== 0x41)
123
+ return null;
124
+ const hex = Array.from(payload.slice(1))
125
+ .map((b) => b.toString(16).padStart(2, '0'))
126
+ .join('');
127
+ return `0x${hex}`;
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ function encodeTrc20BalanceOf(ownerHex20) {
134
+ const arg = ownerHex20.slice(2).padStart(64, '0');
135
+ return `0x70a08231${arg}`;
136
+ }
137
+ // Utility: Separate Tron assets into native and TRC20 tokens
138
+ const separateTronAssets = (tronAssets) => {
139
+ const a_assets_native = [];
140
+ const a_assets_tokens = [];
141
+ for (const asset of tronAssets) {
142
+ if (asset.caip19.includes('/slip44:195')) {
143
+ a_assets_native.push(asset);
144
+ }
145
+ else if (asset.caip19.includes('/trc20:') || asset.caip19.includes('/token:')) {
146
+ a_assets_tokens.push(asset);
147
+ }
148
+ }
149
+ return { a_assets_native, a_assets_tokens };
150
+ };
78
151
  // Utility: Fetch ERC-20 balance for a single token (fallback when multicall not available)
79
152
  const fetchSingleErc20Balance = async (y_client, s0x_account, k_asset, getUnitValueUsd) => {
80
153
  try {
@@ -433,10 +506,150 @@ assets, getUnitValueUsd) => {
433
506
  return { balances: {}, error: errorMsg };
434
507
  }
435
508
  };
509
+ // Utility: Fetch Tron native balances from account payload
510
+ const fetchTronNativeBalances = async (a_assets_native, balanceSun, getUnitValueUsd) => {
511
+ const updatedBalances = {};
512
+ if (a_assets_native.length === 0)
513
+ return updatedBalances;
514
+ await Promise.all(a_assets_native.map(async (asset) => {
515
+ let usdValue = 0;
516
+ try {
517
+ usdValue = await getUnitValueUsd(asset, balanceSun);
518
+ }
519
+ catch {
520
+ // ignore pricing errors, keep balance
521
+ }
522
+ updatedBalances[asset.caip19] = {
523
+ asset,
524
+ balance: balanceSun,
525
+ usdValue,
526
+ };
527
+ }));
528
+ return updatedBalances;
529
+ };
530
+ // Utility: Fetch Tron TRC20 balances from account payload
531
+ const fetchTronTokenBalances = async (a_assets_tokens, trc20Rows, tronAddress, tronRpcUrl, getUnitValueUsd) => {
532
+ const updatedBalances = {};
533
+ if (a_assets_tokens.length === 0)
534
+ return updatedBalances;
535
+ const tokenAmounts = new Map();
536
+ for (const row of trc20Rows) {
537
+ for (const [tokenAddress, amountRaw] of Object.entries(row || {})) {
538
+ const key = tokenAddress.toLowerCase();
539
+ const amount = BigInt(amountRaw || '0');
540
+ tokenAmounts.set(key, (tokenAmounts.get(key) || 0n) + amount);
541
+ const tokenHex = tronAddressToHex20(tokenAddress);
542
+ if (tokenHex) {
543
+ const keyHex = tokenHex.toLowerCase();
544
+ tokenAmounts.set(keyHex, (tokenAmounts.get(keyHex) || 0n) + amount);
545
+ }
546
+ }
547
+ }
548
+ const ownerHex20 = tronAddressToHex20(tronAddress);
549
+ const resolvedRpcUrl = tronRpcUrl || H_RPCS[TRON_CHAIN_ID];
550
+ await Promise.all(a_assets_tokens.map(async (asset) => {
551
+ const tokenAddress = asset.caip19.split('/trc20:')[1] ||
552
+ asset.caip19.split('/token:')[1] ||
553
+ '';
554
+ if (!tokenAddress)
555
+ return;
556
+ const tokenAddressLower = tokenAddress.toLowerCase();
557
+ const tokenHex = tronAddressToHex20(tokenAddress);
558
+ const fromRows = tokenAmounts.get(tokenAddressLower) ||
559
+ (tokenHex ? tokenAmounts.get(tokenHex.toLowerCase()) : undefined) ||
560
+ 0n;
561
+ let balance = fromRows;
562
+ // Fallback to Tron RPC eth_call when TronGrid account.trc20 is missing/empty.
563
+ if (balance <= 0n && ownerHex20 && tokenHex && resolvedRpcUrl) {
564
+ try {
565
+ const response = await fetch(resolvedRpcUrl, {
566
+ method: 'POST',
567
+ headers: { 'Content-Type': 'application/json' },
568
+ body: JSON.stringify({
569
+ jsonrpc: '2.0',
570
+ id: 1,
571
+ method: 'eth_call',
572
+ params: [
573
+ {
574
+ to: tokenHex,
575
+ data: encodeTrc20BalanceOf(ownerHex20),
576
+ },
577
+ 'latest',
578
+ ],
579
+ }),
580
+ });
581
+ if (response.ok) {
582
+ const data = await response.json();
583
+ if (typeof data?.result === 'string' && data.result.startsWith('0x')) {
584
+ balance = BigInt(data.result);
585
+ }
586
+ }
587
+ }
588
+ catch {
589
+ // ignore rpc errors and keep best-known balance
590
+ }
591
+ }
592
+ if (balance <= 0n)
593
+ return;
594
+ let usdValue = 0;
595
+ try {
596
+ usdValue = await getUnitValueUsd(asset, balance);
597
+ }
598
+ catch {
599
+ // ignore pricing errors, keep balance
600
+ }
601
+ updatedBalances[asset.caip19] = {
602
+ asset,
603
+ balance,
604
+ usdValue,
605
+ };
606
+ }));
607
+ return updatedBalances;
608
+ };
609
+ // Utility: Fetch all Tron balances (native TRX + TRC20) using TronGrid
610
+ const fetchTronBalances = async (tronAddress, tronRpcUrl, // kept for future RPC usage
611
+ assets, getUnitValueUsd) => {
612
+ try {
613
+ const tronAssets = Object.values(assets).filter((asset) => asset.caip19.startsWith('tron:'));
614
+ const { a_assets_native, a_assets_tokens } = separateTronAssets(tronAssets);
615
+ const response = await fetch(`https://api.trongrid.io/v1/accounts/${tronAddress}`);
616
+ if (!response.ok) {
617
+ throw new Error(`Failed to fetch Tron balance: ${response.status} ${response.statusText}`);
618
+ }
619
+ const data = await response.json();
620
+ const account = Array.isArray(data?.data) ? data.data[0] : undefined;
621
+ const balanceSun = BigInt(account?.balance ?? 0);
622
+ const trc20Rows = Array.isArray(account?.trc20) ? account.trc20 : [];
623
+ const [nativeResult, tokenResult] = await Promise.allSettled([
624
+ fetchTronNativeBalances(a_assets_native, balanceSun, getUnitValueUsd),
625
+ fetchTronTokenBalances(a_assets_tokens, trc20Rows, tronAddress, tronRpcUrl, getUnitValueUsd),
626
+ ]);
627
+ const nativeBalances = nativeResult.status === 'fulfilled' ? nativeResult.value : {};
628
+ const tokenBalances = tokenResult.status === 'fulfilled' ? tokenResult.value : {};
629
+ const errors = [];
630
+ if (nativeResult.status === 'rejected') {
631
+ errors.push(`Native: ${nativeResult.reason?.message || 'Unknown error'}`);
632
+ }
633
+ if (tokenResult.status === 'rejected') {
634
+ errors.push(`Tokens: ${tokenResult.reason?.message || 'Unknown error'}`);
635
+ }
636
+ return {
637
+ balances: { ...nativeBalances, ...tokenBalances },
638
+ error: errors.length > 0 ? errors.join('; ') : undefined,
639
+ };
640
+ }
641
+ catch (error) {
642
+ const errorMsg = `Failed to fetch Tron balances: ${error instanceof Error ? error.message : String(error)}`;
643
+ if (process.env.NODE_ENV === 'development') {
644
+ console.error(errorMsg, error);
645
+ }
646
+ return { balances: {}, error: errorMsg };
647
+ }
648
+ };
436
649
  /**
437
650
  * Provider for user balances across all supported chains
438
651
  */
439
- export const BalancesProvider = ({ children, evmAddress, solAddress, solanaRpcUrl, bitcoinAddress, bitcoinRpcUrl }) => {
652
+ export const BalancesProvider = ({ children, evmAddress, solAddress, solanaRpcUrl, bitcoinAddress, bitcoinRpcUrl, tronAddress, tronRpcUrl }) => {
440
653
  const { assets, loading: assetsLoading } = useAssetsContext();
441
654
  const { getUnitValueUsd } = usePrices();
442
655
  const [balances, setBalances] = useState({});
@@ -476,6 +689,13 @@ export const BalancesProvider = ({ children, evmAddress, solAddress, solanaRpcUr
476
689
  promise: fetchBitcoinBalances(bitcoinAddress, bitcoinRpcUrl, assets, getUnitValueUsd),
477
690
  });
478
691
  }
692
+ // Process Tron balances if Tron address is available
693
+ if (tronAddress) {
694
+ balanceTasks.push({
695
+ chainId: 'tron',
696
+ promise: fetchTronBalances(tronAddress, tronRpcUrl, assets, getUnitValueUsd),
697
+ });
698
+ }
479
699
  // Use Promise.allSettled to handle partial failures gracefully
480
700
  // This ensures one chain failure doesn't prevent others from updating
481
701
  const results = await Promise.allSettled(balanceTasks.map((task) => task.promise));
@@ -512,7 +732,7 @@ export const BalancesProvider = ({ children, evmAddress, solAddress, solanaRpcUr
512
732
  finally {
513
733
  setLoading(false);
514
734
  }
515
- }, [evmAddress, solAddress, bitcoinAddress, bitcoinRpcUrl, solanaRpcUrl, assets, assetsLoading, getUnitValueUsd]);
735
+ }, [evmAddress, solAddress, bitcoinAddress, bitcoinRpcUrl, tronAddress, tronRpcUrl, solanaRpcUrl, assets, assetsLoading, getUnitValueUsd]);
516
736
  // Refetch balances for specific chains only
517
737
  const refetchChains = useCallback(async (chainIds) => {
518
738
  if (assetsLoading || chainIds.length === 0)
@@ -524,6 +744,7 @@ export const BalancesProvider = ({ children, evmAddress, solAddress, solanaRpcUr
524
744
  // Separate EVM and Solana chain IDs
525
745
  const evmChainIds = chainIds.filter((id) => typeof id === 'number');
526
746
  const shouldRefetchSolana = chainIds.includes('solana');
747
+ const shouldRefetchTron = chainIds.includes('tron');
527
748
  // Process EVM balances for specific chains
528
749
  if (evmAddress && evmChainIds.length > 0) {
529
750
  const s0x_account = getAddress(evmAddress);
@@ -542,6 +763,13 @@ export const BalancesProvider = ({ children, evmAddress, solAddress, solanaRpcUr
542
763
  promise: fetchSolanaBalances(solAddress, solanaRpcUrl, assets, getUnitValueUsd),
543
764
  });
544
765
  }
766
+ // Process Tron balances if requested
767
+ if (tronAddress && shouldRefetchTron) {
768
+ balanceTasks.push({
769
+ chainId: 'tron',
770
+ promise: fetchTronBalances(tronAddress, tronRpcUrl, assets, getUnitValueUsd),
771
+ });
772
+ }
545
773
  // Process Bitcoin balances if requested
546
774
  const shouldRefetchBitcoin = chainIds.includes(BITCOIN_CHAIN_ID);
547
775
  console.log('[BalancesContext] shouldRefetchBitcoin', shouldRefetchBitcoin);
@@ -597,17 +825,17 @@ export const BalancesProvider = ({ children, evmAddress, solAddress, solanaRpcUr
597
825
  finally {
598
826
  setLoading(false);
599
827
  }
600
- }, [evmAddress, solAddress, solanaRpcUrl, bitcoinAddress, bitcoinRpcUrl, assets, assetsLoading, getUnitValueUsd]);
828
+ }, [evmAddress, solAddress, solanaRpcUrl, bitcoinAddress, bitcoinRpcUrl, tronAddress, tronRpcUrl, assets, assetsLoading, getUnitValueUsd]);
601
829
  // Calculate total USD value
602
830
  const totalUsdValue = useMemo(() => {
603
831
  return Object.values(balances).reduce((total, balance) => total + balance.usdValue, 0);
604
832
  }, [balances]);
605
- // Auto-update balances when address, solAddress, bitcoinAddress, or assets change
833
+ // Auto-update balances when address, solAddress, bitcoinAddress, tronAddress, or assets change
606
834
  useEffect(() => {
607
- if ((evmAddress || solAddress || bitcoinAddress) && !assetsLoading) {
835
+ if ((evmAddress || solAddress || bitcoinAddress || tronAddress) && !assetsLoading) {
608
836
  updateBalances();
609
837
  }
610
- }, [evmAddress, solAddress, solanaRpcUrl, bitcoinAddress, bitcoinRpcUrl, assetsLoading, updateBalances]);
838
+ }, [evmAddress, solAddress, solanaRpcUrl, bitcoinAddress, bitcoinRpcUrl, tronAddress, tronRpcUrl, assetsLoading, updateBalances]);
611
839
  const value = useMemo(() => ({
612
840
  balances,
613
841
  loading: loading || assetsLoading,
@@ -7,6 +7,7 @@ import { type ExecuteSwapParams, type SwapResult } from '../hooks/silent/useSile
7
7
  import { type OutputStatus } from '../hooks/silent/useOrderTracking.js';
8
8
  import { type SolanaWalletConnector, type SolanaConnection } from '../hooks/silent/solana-transaction.js';
9
9
  import { type BitcoinWalletConnector, type BitcoinConnection } from '../hooks/silent/bitcoin-transaction.js';
10
+ import { type TronWalletConnector, type TronConnection } from '../hooks/silent/tron-transaction.js';
10
11
  import { type SilentSwapWallet } from '../hooks/silent/useWallet.js';
11
12
  export interface SilentSwapStatusData {
12
13
  platformHealth: PlatformHealthStatus | undefined;
@@ -67,7 +68,7 @@ export interface SilentSwapContextType {
67
68
  solanaRpcUrl?: string;
68
69
  }
69
70
  export declare function useSilentSwap(): SilentSwapContextType;
70
- export declare function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, requestWalletConnect, forceBridgeProvider, onAuthStatus, onWalletStatus, }: {
71
+ export declare function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, tronConnector, tronConnection, environment, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, tronAddress, bitcoinRpcUrl, tronRpcUrl, proId, requestWalletConnect, forceBridgeProvider, onAuthStatus, onWalletStatus, }: {
71
72
  children: React.ReactNode;
72
73
  client: SilentSwapClient;
73
74
  evmAddress?: string;
@@ -79,11 +80,15 @@ export declare function SilentSwapProvider({ children, client, evmAddress, solAd
79
80
  solanaConnection?: SolanaConnection;
80
81
  bitcoinConnector?: BitcoinWalletConnector;
81
82
  bitcoinConnection?: BitcoinConnection;
83
+ tronConnector?: TronWalletConnector;
84
+ tronConnection?: TronConnection;
82
85
  environment?: ENVIRONMENT;
83
86
  baseUrl?: string;
84
87
  solanaRpcUrl?: string;
85
88
  bitcoinAddress?: string;
89
+ tronAddress?: string;
86
90
  bitcoinRpcUrl?: string;
91
+ tronRpcUrl?: string;
87
92
  /** Pro user ID for volume tracking (from ?pro= URL or localStorage silentswap:pro) */
88
93
  proId?: string;
89
94
  /** Optional: request wallet connection when walletClient is missing (e.g. user switched account on mobile). */
@@ -81,7 +81,7 @@ export function useSilentSwap() {
81
81
  }
82
82
  return context;
83
83
  }
84
- function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bitcoinAddress, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, config, solanaRpcUrl, connector, isConnected = false, walletClient, proId, requestWalletConnect, forceBridgeProvider, onAuthStatus, onWalletStatus, }) {
84
+ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bitcoinAddress, tronAddress, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, tronConnector, tronConnection, environment, config, solanaRpcUrl, connector, isConnected = false, walletClient, proId, requestWalletConnect, forceBridgeProvider, onAuthStatus, onWalletStatus, }) {
85
85
  // Authentication hook - only for EVM
86
86
  const { auth, isLoading: authLoading, authenticate, authExhausted } = useAuth({
87
87
  client,
@@ -111,7 +111,7 @@ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bit
111
111
  const setDestinations = useSwap((state) => state.setDestinations);
112
112
  const updateDestinationAmount = useSwap((state) => state.updateDestinationAmount);
113
113
  const splits = useSwap((state) => state.splits);
114
- const transactionAddress = useTransactionAddress(tokenIn, evmAddress, solAddress, bitcoinAddress);
114
+ const transactionAddress = useTransactionAddress(tokenIn, evmAddress, solAddress, bitcoinAddress, tronAddress);
115
115
  const effectiveQuoteAddress = transactionAddress;
116
116
  const { getPrice } = usePrices();
117
117
  const { serviceFeeRate, overheadUsd, platformHealth: statusPlatformHealth, minimumDepositUusdc, maximumDepositUusdc, outputLimit, identity, volume, liquidity, storage, isLoading: statusLoading, } = useStatus({ baseUrl: config.baseUrl ?? '', environment: config.environment, proId });
@@ -155,6 +155,9 @@ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bit
155
155
  solanaConnection,
156
156
  bitcoinConnector,
157
157
  bitcoinConnection,
158
+ tronConnector,
159
+ tronConnection,
160
+ tronAddress,
158
161
  getPrice,
159
162
  proId,
160
163
  forceBridgeProvider,
@@ -267,7 +270,7 @@ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bit
267
270
  solanaRpcUrl,
268
271
  }, children: children });
269
272
  }
270
- export function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment = ENVIRONMENT.STAGING, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, requestWalletConnect, forceBridgeProvider, onAuthStatus, onWalletStatus, }) {
273
+ export function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, tronConnector, tronConnection, environment = ENVIRONMENT.STAGING, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, tronAddress, bitcoinRpcUrl, tronRpcUrl, proId, requestWalletConnect, forceBridgeProvider, onAuthStatus, onWalletStatus, }) {
271
274
  const config = useMemo(() => {
272
275
  const computedBaseUrl = baseUrl ?? ENVIRONMENT_CONFIGS[environment].baseUrl;
273
276
  return {
@@ -275,5 +278,5 @@ export function SilentSwapProvider({ children, client, evmAddress, solAddress, c
275
278
  baseUrl: computedBaseUrl,
276
279
  };
277
280
  }, [environment, baseUrl]);
278
- return (_jsx(AssetsProvider, { children: _jsx(PricesProvider, { children: _jsx(BalancesProvider, { evmAddress: evmAddress, solAddress: solAddress, solanaRpcUrl: solanaRpcUrl, bitcoinAddress: bitcoinAddress, bitcoinRpcUrl: bitcoinRpcUrl, children: _jsx(OrdersProvider, { baseUrl: config.baseUrl, children: _jsx(SilentSwapInnerProvider, { client: client, connector: connector, isConnected: isConnected, evmAddress: evmAddress, solAddress: solAddress, bitcoinAddress: bitcoinAddress, solanaConnector: solanaConnector, solanaConnection: solanaConnection, bitcoinConnector: bitcoinConnector, bitcoinConnection: bitcoinConnection, environment: environment, config: config, solanaRpcUrl: solanaRpcUrl, walletClient: walletClient, proId: proId, requestWalletConnect: requestWalletConnect, forceBridgeProvider: forceBridgeProvider, onAuthStatus: onAuthStatus, onWalletStatus: onWalletStatus, children: children }) }) }) }) }));
281
+ return (_jsx(AssetsProvider, { children: _jsx(PricesProvider, { children: _jsx(BalancesProvider, { evmAddress: evmAddress, solAddress: solAddress, solanaRpcUrl: solanaRpcUrl, bitcoinAddress: bitcoinAddress, bitcoinRpcUrl: bitcoinRpcUrl, tronAddress: tronAddress, tronRpcUrl: tronRpcUrl, children: _jsx(OrdersProvider, { baseUrl: config.baseUrl, children: _jsx(SilentSwapInnerProvider, { client: client, connector: connector, isConnected: isConnected, evmAddress: evmAddress, solAddress: solAddress, bitcoinAddress: bitcoinAddress, tronAddress: tronAddress, solanaConnector: solanaConnector, solanaConnection: solanaConnection, bitcoinConnector: bitcoinConnector, bitcoinConnection: bitcoinConnection, tronConnector: tronConnector, tronConnection: tronConnection, environment: environment, config: config, solanaRpcUrl: solanaRpcUrl, walletClient: walletClient, proId: proId, requestWalletConnect: requestWalletConnect, forceBridgeProvider: forceBridgeProvider, onAuthStatus: onAuthStatus, onWalletStatus: onWalletStatus, children: children }) }) }) }) }));
279
282
  }
@@ -31,13 +31,43 @@ export type TronTransactionExecutor = (tx: BridgeTransaction) => Promise<string>
31
31
  */
32
32
  export declare function createTronTransactionExecutor(connector: TronWalletConnector, _connection?: TronConnection): TronTransactionExecutor;
33
33
  /**
34
- * Helper to convert relay.link Tron step data to BridgeTransaction.
35
- * Relay may return EVM-style fields (chainId, to, value, data) for Tron.
34
+ * Shape of a relay.link step item `data` for Tron.
35
+ * Relay returns a nested TriggerSmartContract payload (NOT the EVM-style
36
+ * flat { chainId, to, value, data } shape). Example:
37
+ *
38
+ * {
39
+ * type: 'TriggerSmartContract',
40
+ * parameter: {
41
+ * owner_address: '414527...', // 21-byte Tron hex (no 0x prefix)
42
+ * contract_address: '41a614...', // 21-byte Tron hex (no 0x prefix)
43
+ * call_value: 0,
44
+ * data: '095ea7b3...' // calldata hex (no 0x prefix)
45
+ * }
46
+ * }
36
47
  */
37
- export declare function convertRelayTronStepToTransaction(stepData: {
48
+ export interface RelayTronStepData {
49
+ type?: string;
50
+ parameter?: {
51
+ owner_address?: string;
52
+ contract_address?: string;
53
+ call_value?: number | string;
54
+ data?: string;
55
+ };
38
56
  chainId?: number;
39
57
  to?: string;
40
58
  value?: string;
41
59
  data?: string;
42
60
  gas?: string;
43
- }, chainId?: number): BridgeTransaction;
61
+ }
62
+ /**
63
+ * Returns true if the given relay step item data looks like a Tron step.
64
+ * Used to discriminate Tron steps from Solana / Bitcoin / EVM in a mixed
65
+ * relay response.
66
+ */
67
+ export declare function isRelayTronStepData(itemData: unknown): itemData is RelayTronStepData;
68
+ /**
69
+ * Helper to convert relay.link Tron step data to BridgeTransaction.
70
+ * Handles both the nested TriggerSmartContract shape (current relay API)
71
+ * and a legacy EVM-style flat shape.
72
+ */
73
+ export declare function convertRelayTronStepToTransaction(stepData: RelayTronStepData, chainId?: number): BridgeTransaction;
@@ -22,11 +22,55 @@ export function createTronTransactionExecutor(connector, _connection) {
22
22
  return txHash;
23
23
  };
24
24
  }
25
+ /**
26
+ * Returns true if the given relay step item data looks like a Tron step.
27
+ * Used to discriminate Tron steps from Solana / Bitcoin / EVM in a mixed
28
+ * relay response.
29
+ */
30
+ export function isRelayTronStepData(itemData) {
31
+ if (!itemData || typeof itemData !== 'object')
32
+ return false;
33
+ const d = itemData;
34
+ // Nested TriggerSmartContract shape (what relay actually returns today).
35
+ if (d.type === 'TriggerSmartContract' && d.parameter && typeof d.parameter === 'object') {
36
+ return true;
37
+ }
38
+ // Legacy flat shape fallback.
39
+ if (typeof d.chainId === 'number' && d.chainId === N_RELAY_CHAIN_ID_TRON) {
40
+ return true;
41
+ }
42
+ return false;
43
+ }
25
44
  /**
26
45
  * Helper to convert relay.link Tron step data to BridgeTransaction.
27
- * Relay may return EVM-style fields (chainId, to, value, data) for Tron.
46
+ * Handles both the nested TriggerSmartContract shape (current relay API)
47
+ * and a legacy EVM-style flat shape.
28
48
  */
29
49
  export function convertRelayTronStepToTransaction(stepData, chainId = N_RELAY_CHAIN_ID_TRON) {
50
+ // Preferred: nested TriggerSmartContract shape from relay.
51
+ if (stepData.parameter && typeof stepData.parameter === 'object') {
52
+ const { contract_address, data, call_value } = stepData.parameter;
53
+ if (!contract_address) {
54
+ throw new Error('Tron transaction step is missing parameter.contract_address');
55
+ }
56
+ if (data === undefined) {
57
+ throw new Error('Tron transaction step is missing parameter.data');
58
+ }
59
+ // Normalize the calldata to include a 0x prefix (downstream connectors
60
+ // strip/expect this consistently).
61
+ const normalizedData = data.startsWith('0x') ? data : `0x${data}`;
62
+ const normalizedValue = call_value === undefined || call_value === null ? '0' : String(call_value);
63
+ return {
64
+ chainId,
65
+ // contract_address is Tron 21-byte hex (e.g. "41a614..."). We pass
66
+ // it through unchanged — the Tron connector is responsible for
67
+ // normalizing to base58 / whichever format tronWeb expects.
68
+ to: contract_address,
69
+ value: normalizedValue,
70
+ data: normalizedData,
71
+ };
72
+ }
73
+ // Legacy flat shape fallback.
30
74
  if (!stepData.to || stepData.data === undefined) {
31
75
  throw new Error('Tron transaction step must have to and data fields');
32
76
  }
@@ -164,6 +164,20 @@ export function useAuth({ client, address, walletClient, connector, domain = typ
164
164
  throw new Error('WalletConnect session is stale, skipping sign-in attempt');
165
165
  }
166
166
  }
167
+ // Liveness check — verify the wallet actually responds to a lightweight
168
+ // JSON-RPC call before attempting the heavier signMessage. For WC connections
169
+ // the ping above tests relay connectivity, but a real RPC round-trip catches
170
+ // cases where the relay is alive but the peer wallet is unresponsive.
171
+ const LIVENESS_TIMEOUT_MS = 5_000;
172
+ try {
173
+ await Promise.race([
174
+ walletClient.request({ method: 'eth_chainId' }),
175
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Wallet liveness check timed out')), LIVENESS_TIMEOUT_MS)),
176
+ ]);
177
+ }
178
+ catch (livenessErr) {
179
+ throw new Error(`Wallet not responding to requests: ${livenessErr.message}`);
180
+ }
167
181
  // Create SIWE message
168
182
  const siweMessage = createSignInMessageCallback(normalizedAddress, nonceValue, domain);
169
183
  // Notify consumer that a wallet signature is about to be requested
@@ -3,6 +3,7 @@ import type { Connector } from 'wagmi';
3
3
  import type { DepositParams } from '@silentswap/sdk';
4
4
  import type { SolanaWalletConnector, SolanaConnection } from './solana-transaction.js';
5
5
  import type { BitcoinWalletConnector, BitcoinConnection } from './bitcoin-transaction.js';
6
+ import type { TronWalletConnector, TronConnection } from './tron-transaction.js';
6
7
  /** Delay after approve tx so RPC/nodes confirm before next tx (avoids MetaMask RPC errors) */
7
8
  export declare const APPROVE_POST_DELAY_MS = 7000;
8
9
  /**
@@ -34,12 +35,21 @@ export interface BridgeExecutionResult {
34
35
  * @param solanaRpcUrl - Optional Solana RPC URL
35
36
  * @param bitcoinConnector - Bitcoin wallet connector (required for Bitcoin swaps)
36
37
  * @param bitcoinConnection - Bitcoin connection (optional, for consistency)
38
+ * @param tronConnector - Tron wallet connector (required for Tron swaps)
39
+ * @param tronConnection - Tron connection (optional, for consistency)
37
40
  * @param setCurrentStep - Callback to set current step message
38
41
  * @param onStatus - Optional status update callback
39
42
  * @returns Functions for executing bridge transactions
40
43
  */
41
- export declare function useBridgeExecution(walletClient: WalletClient | undefined, connector: Connector | undefined, solanaConnector: SolanaWalletConnector | undefined, solanaConnection: SolanaConnection | undefined, solanaRpcUrl: string | undefined, setCurrentStep: (step: string) => void, depositorAddress: Hex, onStatus?: (status: string) => void, bitcoinConnector?: BitcoinWalletConnector, bitcoinConnection?: BitcoinConnection): {
42
- executeSolanaBridge: (sourceAsset: string, sourceAmount: string, usdcAmount: string | undefined, solanaSenderAddress: string, evmSignerAddress: `0x${string}`, depositParams?: DepositParams<`${bigint}`>) => Promise<BridgeExecutionResult>;
44
+ export declare function useBridgeExecution(walletClient: WalletClient | undefined, connector: Connector | undefined, solanaConnector: SolanaWalletConnector | undefined, solanaConnection: SolanaConnection | undefined, solanaRpcUrl: string | undefined, setCurrentStep: (step: string) => void, depositorAddress: Hex, onStatus?: (status: string) => void, bitcoinConnector?: BitcoinWalletConnector, bitcoinConnection?: BitcoinConnection, tronConnector?: TronWalletConnector, tronConnection?: TronConnection): {
45
+ executeSolanaBridge: (sourceAsset: string, sourceAmount: string, usdcAmount: string | undefined, solanaSenderAddress: string, evmSignerAddress: `0x${string}`, options?: {
46
+ depositParams?: DepositParams<`${bigint}`>;
47
+ provider?: "relay" | "debridge";
48
+ }) => Promise<BridgeExecutionResult>;
43
49
  executeBitcoinBridge: (sourceAsset: string, sourceAmount: string, usdcAmount: string | undefined, bitcoinSenderAddress: string, evmSignerAddress: `0x${string}`, depositParams?: DepositParams<`${bigint}`>) => Promise<BridgeExecutionResult>;
50
+ executeTronBridge: (sourceAsset: string, sourceAmount: string, usdcAmount: string | undefined, tronSenderAddress: string, evmSignerAddress: `0x${string}`, options?: {
51
+ depositParams?: DepositParams<`${bigint}`>;
52
+ provider?: "relay" | "debridge";
53
+ }) => Promise<BridgeExecutionResult>;
44
54
  executeEvmBridge: (sourceChainId: number, sourceTokenAddress: string, sourceAmount: string, usdcAmount: string | undefined, depositParams: DepositParams<`${bigint}`>, evmSignerAddress: `0x${string}`, evmSenderAddress?: `0x${string}`, provider?: "relay" | "debridge", allowanceTarget?: string) => Promise<BridgeExecutionResult>;
45
55
  };