@silentswap/react 0.1.49 → 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.
@@ -1,10 +1,9 @@
1
1
  import { useCallback } from 'react';
2
2
  import { encodeFunctionData, erc20Abi, getAddress } from 'viem';
3
- import { ensureChain, waitForTransactionConfirmation, createPublicClientWithRpc, NI_CHAIN_ID_AVALANCHE, S0X_ADDR_USDC_AVALANCHE, X_MAX_IMPACT_PERCENT, getRelayStatus, getDebridgeStatus, fetchDebridgeOrder, fetchRelayQuote, createPhonyDepositCalldata, isSolanaAsset, isBitcoinAsset, parseSolanaCaip19, isSolanaNativeToken, isSplToken, N_RELAY_CHAIN_ID_SOLANA, N_RELAY_CHAIN_ID_BITCOIN, SB58_ADDR_SOL_PROGRAM_SYSTEM, getChainById, SBTC_ADDR_BITCOIN_NATIVE, } from '@silentswap/sdk';
3
+ import { ensureChain, waitForTransactionConfirmation, createPublicClientWithRpc, NI_CHAIN_ID_AVALANCHE, S0X_ADDR_USDC_AVALANCHE, S0X_ADDR_EVM_ZERO, UINT256_MAX, X_MAX_IMPACT_PERCENT, getRelayStatus, getDebridgeStatus, fetchDebridgeOrder, fetchRelayQuote, createPhonyDepositCalldata, isSolanaAsset, isBitcoinAsset, isTronAsset, parseSolanaCaip19, isSolanaNativeToken, isSplToken, N_RELAY_CHAIN_ID_SOLANA, N_RELAY_CHAIN_ID_BITCOIN, N_RELAY_CHAIN_ID_TRON, N_DEBRIDGE_CHAIN_ID_SOLANA, N_DEBRIDGE_CHAIN_ID_TRON, SB58_ADDR_SOL_PROGRAM_SYSTEM, getChainById, SBTC_ADDR_BITCOIN_NATIVE, S0X_ADDR_TRON_NATIVE, } from '@silentswap/sdk';
4
4
  import { createSolanaTransactionExecutor, convertRelaySolanaStepToTransaction } from './solana-transaction.js';
5
5
  import { createBitcoinTransactionExecutor, convertRelayBitcoinStepToTransaction } from './bitcoin-transaction.js';
6
- const S0X_ADDR_EVM_ZERO = '0x0000000000000000000000000000000000000000';
7
- const XG_UINT256_MAX = (1n << 256n) - 1n;
6
+ import { createTronTransactionExecutor, convertRelayTronStepToTransaction, isRelayTronStepData } from './tron-transaction.js';
8
7
  /** Delay after approve tx so RPC/nodes confirm before next tx (avoids MetaMask RPC errors) */
9
8
  export const APPROVE_POST_DELAY_MS = 7000;
10
9
  /**
@@ -160,6 +159,15 @@ function getRelayOriginAssetFromCaip19(caip19) {
160
159
  originCurrency: SBTC_ADDR_BITCOIN_NATIVE, // Bitcoin native token (BTC) - relay.link requires this specific address
161
160
  };
162
161
  }
162
+ if (isTronAsset(caip19)) {
163
+ // Tron CAIP-19: native = tron:chainRef/slip44:195, TRC-20 = tron:chainRef/trc20:ADDRESS
164
+ const trc20Match = /^tron:[^/]+\/(?:trc20|token):([A-Za-z0-9]+)$/i.exec(caip19);
165
+ const originCurrency = trc20Match ? trc20Match[1] : S0X_ADDR_TRON_NATIVE;
166
+ return {
167
+ originChainId: N_RELAY_CHAIN_ID_TRON,
168
+ originCurrency,
169
+ };
170
+ }
163
171
  throw new Error(`Unsupported asset type: ${caip19}`);
164
172
  }
165
173
  // Depositor ABI for encoding deposit calldata
@@ -211,11 +219,13 @@ const DEPOSITOR_ABI = [
211
219
  * @param solanaRpcUrl - Optional Solana RPC URL
212
220
  * @param bitcoinConnector - Bitcoin wallet connector (required for Bitcoin swaps)
213
221
  * @param bitcoinConnection - Bitcoin connection (optional, for consistency)
222
+ * @param tronConnector - Tron wallet connector (required for Tron swaps)
223
+ * @param tronConnection - Tron connection (optional, for consistency)
214
224
  * @param setCurrentStep - Callback to set current step message
215
225
  * @param onStatus - Optional status update callback
216
226
  * @returns Functions for executing bridge transactions
217
227
  */
218
- export function useBridgeExecution(walletClient, connector, solanaConnector, solanaConnection, solanaRpcUrl, setCurrentStep, depositorAddress, onStatus, bitcoinConnector, bitcoinConnection) {
228
+ export function useBridgeExecution(walletClient, connector, solanaConnector, solanaConnection, solanaRpcUrl, setCurrentStep, depositorAddress, onStatus, bitcoinConnector, bitcoinConnection, tronConnector, tronConnection) {
219
229
  /**
220
230
  * Execute Solana bridge transaction
221
231
  *
@@ -234,11 +244,13 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
234
244
  * @param evmSignerAddress - EVM signer address for deposit operations
235
245
  * @returns Promise resolving to bridge execution result
236
246
  */
237
- const executeSolanaBridge = useCallback(async (sourceAsset, sourceAmount, usdcAmount, solanaSenderAddress, evmSignerAddress, depositParams) => {
247
+ const executeSolanaBridge = useCallback(async (sourceAsset, sourceAmount, usdcAmount, solanaSenderAddress, evmSignerAddress, options) => {
238
248
  try {
239
249
  if (!solanaConnector || !solanaConnection) {
240
250
  throw new Error('Solana connector and connection are required for Solana swaps');
241
251
  }
252
+ const depositParams = options?.depositParams;
253
+ const provider = options?.provider;
242
254
  // Get relay origin asset parameters
243
255
  const { originChainId, originCurrency } = getRelayOriginAssetFromCaip19(sourceAsset);
244
256
  console.log('[SilentSwap:SolanaBridge] Start', {
@@ -264,7 +276,7 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
264
276
  const approveUsdcCalldata = encodeFunctionData({
265
277
  abi: erc20Abi,
266
278
  functionName: 'approve',
267
- args: [depositorAddress, XG_UINT256_MAX],
279
+ args: [depositorAddress, UINT256_MAX],
268
280
  });
269
281
  // Encode depositProxy2 calldata from depositParams (matches Svelte line 914-925)
270
282
  // CRITICAL: The signer must match EXACTLY (including checksum) the one used in the SilentSwap quote request
@@ -297,61 +309,71 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
297
309
  console.warn('DepositParams not provided, using phony calldata for bridge quote');
298
310
  depositCalldataForExecution = createPhonyDepositCalldata(evmSignerAddress);
299
311
  }
300
- const relayQuoteRequest = {
301
- user: solanaSenderAddress,
302
- originChainId,
303
- originCurrency,
304
- destinationChainId: NI_CHAIN_ID_AVALANCHE,
305
- destinationCurrency: S0X_ADDR_USDC_AVALANCHE,
306
- amount: bridgeUsdcAmount,
307
- recipient: depositorAddress,
308
- tradeType: 'EXACT_OUTPUT',
309
- txsGasLimit: 600_000,
310
- txs: [
311
- {
312
- to: S0X_ADDR_USDC_AVALANCHE,
313
- value: '0',
314
- data: approveUsdcCalldata,
315
- },
316
- {
317
- to: depositorAddress,
318
- value: '0',
319
- data: depositCalldataForExecution,
320
- },
321
- ],
322
- };
323
- console.log('[SilentSwap:SolanaBridge] Relay quote request (EXACT_OUTPUT)', {
324
- user: relayQuoteRequest.user,
325
- originChainId: relayQuoteRequest.originChainId,
326
- originCurrency: relayQuoteRequest.originCurrency,
327
- destinationChainId: relayQuoteRequest.destinationChainId,
328
- amount: relayQuoteRequest.amount,
329
- recipient: relayQuoteRequest.recipient,
330
- tradeType: relayQuoteRequest.tradeType,
331
- txsCount: relayQuoteRequest.txs.length,
332
- depositCalldataLength: depositCalldataForExecution.length,
333
- });
334
- const relayQuote = await fetchRelayQuote(relayQuoteRequest);
335
- console.log('[SilentSwap:SolanaBridge] Relay quote response', {
336
- currencyIn: relayQuote.details?.currencyIn ? {
337
- amount: relayQuote.details.currencyIn.amount,
338
- amountUsd: relayQuote.details.currencyIn.amountUsd,
339
- } : 'N/A',
340
- currencyOut: relayQuote.details?.currencyOut ? {
341
- amount: relayQuote.details.currencyOut.amount,
342
- amountUsd: relayQuote.details.currencyOut.amountUsd,
343
- } : 'N/A',
344
- totalImpact: relayQuote.details?.totalImpact,
345
- stepsCount: relayQuote.steps?.length,
346
- stepIds: relayQuote.steps?.map((s) => s.id),
347
- });
348
- // Check price impact
349
- const impactPercent = Number(relayQuote.details.totalImpact.percent);
350
- if (impactPercent > X_MAX_IMPACT_PERCENT) {
351
- throw new Error(`Price impact across bridge too high: ${impactPercent.toFixed(2)}%`);
312
+ const selectedProvider = provider === 'debridge' ? 'debridge' : 'relay';
313
+ let rawResponse;
314
+ if (selectedProvider === 'debridge') {
315
+ const debridgeQuote = await fetchDebridgeOrder({
316
+ srcChainId: N_DEBRIDGE_CHAIN_ID_SOLANA,
317
+ srcChainTokenIn: originCurrency,
318
+ srcChainTokenInAmount: 'auto',
319
+ dstChainId: NI_CHAIN_ID_AVALANCHE,
320
+ dstChainTokenOut: S0X_ADDR_USDC_AVALANCHE,
321
+ dstChainTokenOutAmount: `${BigInt(bridgeUsdcAmount)}`,
322
+ dstChainTokenOutRecipient: depositorAddress,
323
+ prependOperatingExpenses: true,
324
+ additionalTakerRewardBps: 0,
325
+ account: solanaSenderAddress,
326
+ srcChainOrderAuthorityAddress: solanaSenderAddress,
327
+ srcChainRefundAddress: solanaSenderAddress,
328
+ dstChainOrderAuthorityAddress: depositorAddress,
329
+ dlnHook: JSON.stringify({
330
+ type: 'evm_transaction_call',
331
+ data: {
332
+ to: depositorAddress,
333
+ calldata: depositCalldataForExecution,
334
+ gas: 500_000,
335
+ },
336
+ }),
337
+ });
338
+ const usdIn = debridgeQuote.estimation?.srcChainTokenIn?.approximateUsdValue || 0;
339
+ const usdOut = debridgeQuote.estimation?.dstChainTokenOut?.approximateUsdValue || 0;
340
+ const impactPercent = usdIn > 0 ? 100 * (1 - usdOut / usdIn) : 0;
341
+ if (impactPercent > X_MAX_IMPACT_PERCENT) {
342
+ throw new Error(`Price impact across bridge too high: ${impactPercent.toFixed(2)}%`);
343
+ }
344
+ rawResponse = debridgeQuote;
345
+ }
346
+ else {
347
+ const relayQuoteRequest = {
348
+ user: solanaSenderAddress,
349
+ originChainId,
350
+ originCurrency,
351
+ destinationChainId: NI_CHAIN_ID_AVALANCHE,
352
+ destinationCurrency: S0X_ADDR_USDC_AVALANCHE,
353
+ amount: bridgeUsdcAmount,
354
+ recipient: depositorAddress,
355
+ tradeType: 'EXACT_OUTPUT',
356
+ txsGasLimit: 600_000,
357
+ txs: [
358
+ {
359
+ to: S0X_ADDR_USDC_AVALANCHE,
360
+ value: '0',
361
+ data: approveUsdcCalldata,
362
+ },
363
+ {
364
+ to: depositorAddress,
365
+ value: '0',
366
+ data: depositCalldataForExecution,
367
+ },
368
+ ],
369
+ };
370
+ const relayQuote = await fetchRelayQuote(relayQuoteRequest);
371
+ const impactPercent = Number(relayQuote.details.totalImpact.percent);
372
+ if (impactPercent > X_MAX_IMPACT_PERCENT) {
373
+ throw new Error(`Price impact across bridge too high: ${impactPercent.toFixed(2)}%`);
374
+ }
375
+ rawResponse = relayQuote;
352
376
  }
353
- const rawResponse = relayQuote;
354
- const selectedProvider = 'relay';
355
377
  // Create Solana transaction executor
356
378
  const solanaExecutor = createSolanaTransactionExecutor(solanaConnector, solanaConnection);
357
379
  setCurrentStep('Executing bridge transaction');
@@ -424,8 +446,21 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
424
446
  };
425
447
  }
426
448
  else if (selectedProvider === 'debridge') {
427
- // DeBridge Solana execution not yet fully implemented
428
- throw new Error('DeBridge Solana transaction execution not yet fully implemented');
449
+ const debridgeQuote = rawResponse;
450
+ if (!debridgeQuote.tx?.data) {
451
+ throw new Error('No Solana transaction data in deBridge response');
452
+ }
453
+ setCurrentStep('Requesting bridge...');
454
+ onStatus?.('Requesting bridge...');
455
+ await solanaExecutor({
456
+ chainId: N_DEBRIDGE_CHAIN_ID_SOLANA,
457
+ data: debridgeQuote.tx.data,
458
+ });
459
+ const debridgeOrderId = debridgeQuote?.order?.orderId;
460
+ if (!debridgeOrderId) {
461
+ throw new Error('Missing deBridge order ID');
462
+ }
463
+ return await monitorDebridgeBridgeStatus(debridgeOrderId, setCurrentStep, onStatus);
429
464
  }
430
465
  else {
431
466
  throw new Error(`Unsupported bridge provider: ${selectedProvider}`);
@@ -485,7 +520,7 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
485
520
  const approveUsdcCalldata = encodeFunctionData({
486
521
  abi: erc20Abi,
487
522
  functionName: 'approve',
488
- args: [depositorAddress, XG_UINT256_MAX],
523
+ args: [depositorAddress, UINT256_MAX],
489
524
  });
490
525
  // Encode depositProxy2 calldata from depositParams
491
526
  let depositCalldataForExecution;
@@ -609,6 +644,195 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
609
644
  throw error;
610
645
  }
611
646
  }, [bitcoinConnector, bitcoinConnection, setCurrentStep, onStatus, depositorAddress]);
647
+ /**
648
+ * Execute Tron bridge transaction
649
+ *
650
+ * Handles bridge execution for Tron source assets:
651
+ * 1. Solves for optimal USDC amount (if not provided)
652
+ * 2. Gets quotes from relay.link or deBridge
653
+ * 3. Executes Tron transactions (EVM-style to/value/data)
654
+ * 4. Monitors bridge status
655
+ */
656
+ const executeTronBridge = useCallback(async (sourceAsset, sourceAmount, usdcAmount, tronSenderAddress, evmSignerAddress, options) => {
657
+ try {
658
+ if (!tronConnector) {
659
+ throw new Error('Tron connector required for Tron swaps');
660
+ }
661
+ const depositParams = options?.depositParams;
662
+ const provider = options?.provider;
663
+ const { originChainId, originCurrency } = getRelayOriginAssetFromCaip19(sourceAsset);
664
+ if (!usdcAmount) {
665
+ throw new Error('USDC amount is required for Tron bridge execution. ' +
666
+ 'It should be provided from the initial solveOptimalUsdcAmount call in handleGetQuote.');
667
+ }
668
+ const bridgeUsdcAmount = usdcAmount;
669
+ setCurrentStep('Fetching bridge quote');
670
+ onStatus?.('Fetching bridge quote');
671
+ const approveUsdcCalldata = encodeFunctionData({
672
+ abi: erc20Abi,
673
+ functionName: 'approve',
674
+ args: [depositorAddress, UINT256_MAX],
675
+ });
676
+ let depositCalldataForExecution;
677
+ if (depositParams) {
678
+ const checksummedSigner = getAddress(evmSignerAddress);
679
+ const finalDepositParams = {
680
+ ...depositParams,
681
+ signer: checksummedSigner,
682
+ approvalExpiration: typeof depositParams.approvalExpiration === 'bigint'
683
+ ? depositParams.approvalExpiration
684
+ : BigInt(String(depositParams.approvalExpiration)),
685
+ duration: typeof depositParams.duration === 'bigint'
686
+ ? depositParams.duration
687
+ : BigInt(String(depositParams.duration)),
688
+ };
689
+ depositCalldataForExecution = encodeFunctionData({
690
+ abi: DEPOSITOR_ABI,
691
+ functionName: 'depositProxy2',
692
+ args: [finalDepositParams],
693
+ });
694
+ }
695
+ else {
696
+ depositCalldataForExecution = createPhonyDepositCalldata(evmSignerAddress);
697
+ }
698
+ const selectedProvider = provider;
699
+ let rawResponse;
700
+ if (selectedProvider === 'debridge') {
701
+ const debridgeQuote = await fetchDebridgeOrder({
702
+ srcChainId: N_DEBRIDGE_CHAIN_ID_TRON,
703
+ srcChainTokenIn: originCurrency,
704
+ srcChainTokenInAmount: 'auto',
705
+ dstChainId: NI_CHAIN_ID_AVALANCHE,
706
+ dstChainTokenOut: S0X_ADDR_USDC_AVALANCHE,
707
+ dstChainTokenOutAmount: `${BigInt(bridgeUsdcAmount)}`,
708
+ dstChainTokenOutRecipient: depositorAddress,
709
+ prependOperatingExpenses: true,
710
+ additionalTakerRewardBps: 0,
711
+ account: tronSenderAddress,
712
+ srcChainOrderAuthorityAddress: tronSenderAddress,
713
+ srcChainRefundAddress: tronSenderAddress,
714
+ dstChainOrderAuthorityAddress: depositorAddress,
715
+ enableEstimate: false,
716
+ dlnHook: JSON.stringify({
717
+ type: 'evm_transaction_call',
718
+ data: {
719
+ to: depositorAddress,
720
+ calldata: depositCalldataForExecution,
721
+ gas: 500_000,
722
+ },
723
+ }),
724
+ });
725
+ const usdIn = debridgeQuote.estimation?.srcChainTokenIn?.approximateUsdValue || 0;
726
+ const usdOut = debridgeQuote.estimation?.dstChainTokenOut?.approximateUsdValue || 0;
727
+ const impactPercent = usdIn > 0 ? 100 * (1 - usdOut / usdIn) : 0;
728
+ if (impactPercent > X_MAX_IMPACT_PERCENT) {
729
+ throw new Error(`Price impact across bridge too high: ${impactPercent.toFixed(2)}%`);
730
+ }
731
+ rawResponse = debridgeQuote;
732
+ }
733
+ else {
734
+ const relayQuote = await fetchRelayQuote({
735
+ user: tronSenderAddress,
736
+ originChainId,
737
+ originCurrency,
738
+ destinationChainId: NI_CHAIN_ID_AVALANCHE,
739
+ destinationCurrency: S0X_ADDR_USDC_AVALANCHE,
740
+ amount: bridgeUsdcAmount,
741
+ recipient: depositorAddress,
742
+ tradeType: 'EXACT_OUTPUT',
743
+ txsGasLimit: 600_000,
744
+ txs: [
745
+ { to: S0X_ADDR_USDC_AVALANCHE, value: '0', data: approveUsdcCalldata },
746
+ { to: depositorAddress, value: '0', data: depositCalldataForExecution },
747
+ ],
748
+ });
749
+ const impactPercent = Number(relayQuote.details.totalImpact.percent);
750
+ if (impactPercent > X_MAX_IMPACT_PERCENT) {
751
+ throw new Error(`Price impact across bridge too high: ${impactPercent.toFixed(2)}%`);
752
+ }
753
+ rawResponse = relayQuote;
754
+ }
755
+ const tronExecutor = createTronTransactionExecutor(tronConnector, tronConnection);
756
+ setCurrentStep('Executing bridge transaction');
757
+ onStatus?.('Executing bridge transaction');
758
+ if (selectedProvider === 'relay') {
759
+ const relayQuoteTyped = rawResponse;
760
+ if (!relayQuoteTyped.steps) {
761
+ throw new Error('No steps in relay quote response');
762
+ }
763
+ for (const step of relayQuoteTyped.steps) {
764
+ if (step.kind !== 'transaction') {
765
+ throw new Error(`Unsupported relay step kind: ${step.kind}`);
766
+ }
767
+ if (step.items.length > 1) {
768
+ throw new Error('Multiple items in transaction step not implemented');
769
+ }
770
+ const item = step.items[0];
771
+ const itemData = item.data;
772
+ // Relay returns Tron steps as nested TriggerSmartContract payloads
773
+ // (no top-level chainId), so we discriminate by shape. Non-Tron
774
+ // steps in a Tron bridge response are unexpected — throw instead
775
+ // of silently skipping so failures surface loudly.
776
+ if (!isRelayTronStepData(itemData)) {
777
+ throw new Error(`Unexpected relay step data in Tron bridge flow (step id: ${step.id}). ` +
778
+ `Expected TriggerSmartContract payload with 'parameter' field.`);
779
+ }
780
+ const label = step.id === 'approve'
781
+ ? 'Requesting approval...'
782
+ : step.id === 'deposit'
783
+ ? 'Requesting deposit...'
784
+ : 'Requesting bridge...';
785
+ setCurrentStep(label);
786
+ onStatus?.(label);
787
+ const tronTx = convertRelayTronStepToTransaction(itemData, N_RELAY_CHAIN_ID_TRON);
788
+ await tronExecutor(tronTx);
789
+ }
790
+ const requestId = relayQuoteTyped.steps.find((s) => s.requestId)?.requestId;
791
+ if (!requestId) {
792
+ throw new Error('Missing relay.link request ID');
793
+ }
794
+ const depositTxHash = await monitorRelayBridgeStatus(requestId, setCurrentStep, onStatus);
795
+ return {
796
+ depositTxHash: depositTxHash,
797
+ provider: 'relay',
798
+ };
799
+ }
800
+ else {
801
+ // DeBridge execution for Tron
802
+ const debridgeQuote = rawResponse;
803
+ const debridgeTx = debridgeQuote.tx;
804
+ if (!debridgeTx) {
805
+ throw new Error('No transaction in DeBridge order response');
806
+ }
807
+ setCurrentStep('Requesting bridge...');
808
+ onStatus?.('Requesting bridge...');
809
+ const tronTx = convertRelayTronStepToTransaction({
810
+ to: debridgeTx.to,
811
+ value: debridgeTx.value || '0',
812
+ data: debridgeTx.data,
813
+ chainId: N_RELAY_CHAIN_ID_TRON,
814
+ }, N_RELAY_CHAIN_ID_TRON);
815
+ await tronExecutor(tronTx);
816
+ const orderId = debridgeQuote.orderId;
817
+ if (!orderId) {
818
+ throw new Error('Missing DeBridge order ID');
819
+ }
820
+ return await monitorDebridgeBridgeStatus(orderId, setCurrentStep, onStatus);
821
+ }
822
+ }
823
+ catch (error) {
824
+ console.error('Tron bridge execution failed:', {
825
+ sourceAsset,
826
+ sourceAmount,
827
+ usdcAmount,
828
+ tronSenderAddress,
829
+ evmSignerAddress,
830
+ error: error instanceof Error ? error.message : String(error),
831
+ stack: error instanceof Error ? error.stack : undefined,
832
+ });
833
+ throw error;
834
+ }
835
+ }, [tronConnector, tronConnection, setCurrentStep, onStatus, depositorAddress]);
612
836
  /**
613
837
  * Execute EVM bridge transaction
614
838
  *
@@ -655,7 +879,7 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
655
879
  const approveUsdcCalldata = encodeFunctionData({
656
880
  abi: erc20Abi,
657
881
  functionName: 'approve',
658
- args: [depositorAddress, XG_UINT256_MAX],
882
+ args: [depositorAddress, UINT256_MAX],
659
883
  });
660
884
  // Match Svelte implementation exactly: spread g_params, then override signer, approvalExpiration, and duration
661
885
  // CRITICAL: The signer must match EXACTLY (including checksum) the one used in the SilentSwap quote request
@@ -751,6 +975,7 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
751
975
  return {
752
976
  executeSolanaBridge,
753
977
  executeBitcoinBridge,
978
+ executeTronBridge,
754
979
  executeEvmBridge,
755
980
  };
756
981
  }
@@ -952,7 +1177,7 @@ walletClient, connector, setCurrentStep, onStatus) {
952
1177
  address: sourceTokenAddress,
953
1178
  abi: erc20Abi,
954
1179
  functionName: 'approve',
955
- args: [targetAllowance, XG_UINT256_MAX],
1180
+ args: [targetAllowance, UINT256_MAX],
956
1181
  account: walletClient.account,
957
1182
  chain: null,
958
1183
  });
@@ -993,60 +1218,12 @@ walletClient, connector, setCurrentStep, onStatus) {
993
1218
  throw new Error(`DeBridge transaction failed: ${receipt.transactionHash}`);
994
1219
  }
995
1220
  console.log('DeBridge transaction confirmed:', { hash: receipt.transactionHash });
996
- setCurrentStep('Waiting for bridge');
997
- onStatus?.('Waiting for bridge');
998
1221
  // Monitor deBridge status
999
1222
  const debridgeOrderId = debridgeResponse.orderId;
1000
1223
  if (!debridgeOrderId) {
1001
1224
  throw new Error('Missing deBridge order ID');
1002
1225
  }
1003
- // Poll for deBridge status with exponential backoff
1004
- let depositTxHash;
1005
- const startTime = Date.now();
1006
- const timeout = 10 * 60 * 1000; // 10 minutes (bridges can take several minutes)
1007
- let pollInterval = 2_000; // Start with 2 seconds
1008
- const maxPollInterval = 10_000; // Max 10 seconds between polls
1009
- let attemptCount = 0;
1010
- while (Date.now() - startTime < timeout) {
1011
- attemptCount++;
1012
- try {
1013
- const status = await getDebridgeStatus(debridgeOrderId);
1014
- if (status.status === 'success') {
1015
- depositTxHash = status.txHashes?.[0] || '0x';
1016
- break;
1017
- }
1018
- else if (status.status === 'failed' || status.status === 'refund') {
1019
- throw new Error(`DeBridge transaction failed: ${status.details || 'Unknown error'}`);
1020
- }
1021
- }
1022
- catch (error) {
1023
- // If error is thrown from status check, re-throw it
1024
- if (error instanceof Error && error.message.includes('DeBridge transaction failed')) {
1025
- throw error;
1026
- }
1027
- // Otherwise log and continue polling
1028
- console.error('Failed to fetch deBridge status:', {
1029
- debridgeOrderId,
1030
- error: error instanceof Error ? error.message : String(error),
1031
- });
1032
- }
1033
- // Update status message with elapsed time
1034
- const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
1035
- setCurrentStep(`Waiting for bridge (${elapsedSeconds}s)...`);
1036
- onStatus?.(`Waiting for bridge (${elapsedSeconds}s)...`);
1037
- // Exponential backoff: increase interval after every 3 attempts
1038
- if (attemptCount % 3 === 0) {
1039
- pollInterval = Math.min(pollInterval * 1.5, maxPollInterval);
1040
- }
1041
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
1042
- }
1043
- if (!depositTxHash) {
1044
- throw new Error('DeBridge transaction timed out after 10 minutes');
1045
- }
1046
- return {
1047
- depositTxHash: depositTxHash,
1048
- provider: 'debridge',
1049
- };
1226
+ return await monitorDebridgeBridgeStatus(debridgeOrderId, setCurrentStep, onStatus);
1050
1227
  }
1051
1228
  /**
1052
1229
  * Monitor relay.link bridge status until deposit arrives
@@ -1129,3 +1306,53 @@ async function monitorRelayBridgeStatus(requestId, setCurrentStep, onStatus) {
1129
1306
  }
1130
1307
  return depositTxHash;
1131
1308
  }
1309
+ /**
1310
+ * Monitor deBridge bridge status until deposit arrives
1311
+ * With exponential backoff and timeout
1312
+ */
1313
+ async function monitorDebridgeBridgeStatus(debridgeOrderId, setCurrentStep, onStatus) {
1314
+ setCurrentStep('Waiting for bridge');
1315
+ onStatus?.('Waiting for bridge');
1316
+ let depositTxHash;
1317
+ const startTime = Date.now();
1318
+ const timeout = 10 * 60 * 1000;
1319
+ let pollInterval = 2_000;
1320
+ const maxPollInterval = 10_000;
1321
+ let attemptCount = 0;
1322
+ while (Date.now() - startTime < timeout) {
1323
+ attemptCount++;
1324
+ try {
1325
+ const status = await getDebridgeStatus(debridgeOrderId);
1326
+ if (status.status === 'success') {
1327
+ depositTxHash = status.txHashes?.[0] || '0x';
1328
+ break;
1329
+ }
1330
+ if (status.status === 'failed' || status.status === 'refund') {
1331
+ throw new Error(`DeBridge transaction failed: ${status.details || 'Unknown error'}`);
1332
+ }
1333
+ }
1334
+ catch (error) {
1335
+ if (error instanceof Error && error.message.includes('DeBridge transaction failed')) {
1336
+ throw error;
1337
+ }
1338
+ console.error('Failed to fetch deBridge status:', {
1339
+ debridgeOrderId,
1340
+ error: error instanceof Error ? error.message : String(error),
1341
+ });
1342
+ }
1343
+ const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
1344
+ setCurrentStep(`Waiting for bridge (${elapsedSeconds}s)...`);
1345
+ onStatus?.(`Waiting for bridge (${elapsedSeconds}s)...`);
1346
+ if (attemptCount % 3 === 0) {
1347
+ pollInterval = Math.min(pollInterval * 1.5, maxPollInterval);
1348
+ }
1349
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1350
+ }
1351
+ if (!depositTxHash) {
1352
+ throw new Error('DeBridge transaction timed out after 10 minutes');
1353
+ }
1354
+ return {
1355
+ depositTxHash: depositTxHash,
1356
+ provider: 'debridge',
1357
+ };
1358
+ }
@@ -32,6 +32,8 @@ export interface UseQuoteCalculationOptions {
32
32
  setDestinations: (updater: Destination[] | ((prev: Destination[]) => Destination[])) => void;
33
33
  /** Pro user ID for volume tracking (sent with quote request) */
34
34
  proId?: string;
35
+ /** User's Tron address (TVM base58). Used as recipient fallback for Tron destinations. */
36
+ tronAddress?: string;
35
37
  /** Optional: force a single bridge provider for testing. */
36
38
  forceBridgeProvider?: 'relay' | 'debridge';
37
39
  }
@@ -53,4 +55,4 @@ export interface UseQuoteCalculationReturn {
53
55
  * - Quote request construction and fetching
54
56
  * - Destination amount updates
55
57
  */
56
- export declare function useQuoteCalculation({ address, evmAddress, wallet, depositorAddress, getQuote, getPrice, setDestinations, proId, forceBridgeProvider, }: UseQuoteCalculationOptions): UseQuoteCalculationReturn;
58
+ export declare function useQuoteCalculation({ address, evmAddress, wallet, depositorAddress, getQuote, getPrice, setDestinations, proId, tronAddress, forceBridgeProvider, }: UseQuoteCalculationOptions): UseQuoteCalculationReturn;