@silentswap/react 0.1.50 → 0.1.51
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.
- package/dist/contexts/AssetsContext.js +1 -0
- package/dist/contexts/BalancesContext.d.ts +3 -1
- package/dist/contexts/BalancesContext.js +235 -7
- package/dist/contexts/SilentSwapContext.d.ts +6 -1
- package/dist/contexts/SilentSwapContext.js +7 -4
- package/dist/hooks/silent/tron-transaction.d.ts +34 -4
- package/dist/hooks/silent/tron-transaction.js +45 -1
- package/dist/hooks/silent/useAuth.js +14 -0
- package/dist/hooks/silent/useBridgeExecution.d.ts +12 -2
- package/dist/hooks/silent/useBridgeExecution.js +341 -114
- package/dist/hooks/silent/useQuoteCalculation.d.ts +3 -1
- package/dist/hooks/silent/useQuoteCalculation.js +72 -5
- package/dist/hooks/silent/useSilentQuote.d.ts +8 -1
- package/dist/hooks/silent/useSilentQuote.js +77 -7
- package/dist/hooks/useContacts.d.ts +2 -2
- package/dist/hooks/useOrderEstimates.d.ts +3 -1
- package/dist/hooks/useOrderEstimates.js +23 -11
- package/dist/hooks/useQuote.js +53 -15
- package/dist/hooks/useTransaction.d.ts +6 -1
- package/dist/hooks/useTransaction.js +89 -8
- package/dist/hooks/useTransactionAddress.d.ts +3 -2
- package/dist/hooks/useTransactionAddress.js +10 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +3 -3
|
@@ -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
|
-
*
|
|
35
|
-
* Relay
|
|
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
|
|
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
|
-
}
|
|
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
|
-
*
|
|
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}`,
|
|
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
|
};
|