@silentswap/react 0.0.91 → 0.0.92

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.
@@ -65,7 +65,7 @@ export interface SilentSwapContextType {
65
65
  solanaRpcUrl?: string;
66
66
  }
67
67
  export declare function useSilentSwap(): SilentSwapContextType;
68
- export declare function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, requestWalletConnect, }: {
68
+ export declare function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, requestWalletConnect, forceBridgeProvider, }: {
69
69
  children: React.ReactNode;
70
70
  client: SilentSwapClient;
71
71
  evmAddress?: string;
@@ -86,4 +86,6 @@ export declare function SilentSwapProvider({ children, client, evmAddress, solAd
86
86
  proId?: string;
87
87
  /** Optional: request wallet connection when walletClient is missing (e.g. user switched account on mobile). */
88
88
  requestWalletConnect?: () => Promise<void>;
89
+ /** Optional: force a single bridge provider for testing. */
90
+ forceBridgeProvider?: 'relay' | 'debridge';
89
91
  }): import("react/jsx-runtime").JSX.Element;
@@ -80,7 +80,7 @@ export function useSilentSwap() {
80
80
  }
81
81
  return context;
82
82
  }
83
- function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bitcoinAddress, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, config, solanaRpcUrl, connector, isConnected = false, walletClient, proId, requestWalletConnect, }) {
83
+ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bitcoinAddress, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, config, solanaRpcUrl, connector, isConnected = false, walletClient, proId, requestWalletConnect, forceBridgeProvider, }) {
84
84
  // Authentication hook - only for EVM
85
85
  const { auth, isLoading: authLoading } = useAuth({
86
86
  client,
@@ -149,6 +149,7 @@ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bit
149
149
  bitcoinConnection,
150
150
  getPrice,
151
151
  proId,
152
+ forceBridgeProvider,
152
153
  });
153
154
  const effectiveDepositAmountUsd = useMemo(() => {
154
155
  return depositAmountUsdFromEstimates > 0 ? depositAmountUsdFromEstimates : depositAmountUsdc;
@@ -257,7 +258,7 @@ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bit
257
258
  solanaRpcUrl,
258
259
  }, children: children });
259
260
  }
260
- export function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment = ENVIRONMENT.STAGING, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, requestWalletConnect, }) {
261
+ export function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment = ENVIRONMENT.STAGING, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, requestWalletConnect, forceBridgeProvider, }) {
261
262
  const config = useMemo(() => {
262
263
  const computedBaseUrl = baseUrl ?? ENVIRONMENT_CONFIGS[environment].baseUrl;
263
264
  console.log('[SilentSwapProvider] Creating config:', { environment, baseUrl, computedBaseUrl });
@@ -266,5 +267,5 @@ export function SilentSwapProvider({ children, client, evmAddress, solAddress, c
266
267
  baseUrl: computedBaseUrl,
267
268
  };
268
269
  }, [environment, baseUrl]);
269
- 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, children: children }) }) }) }) }));
270
+ 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, children: children }) }) }) }) }));
270
271
  }
@@ -1,4 +1,4 @@
1
- import { address, appendTransactionMessageInstructions, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, compileTransaction, getBase64EncodedWireTransaction, getTransactionDecoder, getCompiledTransactionMessageDecoder, decompileTransactionMessage, pipe, AccountRole, } from '@solana/kit';
1
+ import { address, appendTransactionMessageInstructions, createTransactionMessage, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, compileTransaction, getBase64EncodedWireTransaction, pipe, AccountRole, } from '@solana/kit';
2
2
  import { createSolanaRpc } from '@solana/rpc';
3
3
  import { VersionedTransaction } from '@solana/web3.js';
4
4
  /**
@@ -25,30 +25,54 @@ export function createSolanaTransactionExecutor(connector, connection) {
25
25
  });
26
26
  // Get RPC client from connection or create a default one
27
27
  const rpc = connection.rpc || createSolanaRpc('https://api.mainnet-beta.solana.com');
28
+ const rpcEndpoint = connection.connection?.rpcEndpoint ||
29
+ connection.connection?._rpcEndpoint ||
30
+ 'https://api.mainnet-beta.solana.com';
31
+ console.log('[SolanaTx] Execution context:', {
32
+ chainId: tx.chainId,
33
+ hasAdapterConnection: !!connection.connection,
34
+ rpcEndpoint,
35
+ });
28
36
  let transactionMessage;
37
+ let versionedTransaction;
29
38
  let blockhash;
30
39
  let lastValidBlockHeight;
40
+ const sendSignedViaRpc = async (txToSignAndSend) => {
41
+ console.log('Requesting signature...');
42
+ const signedTransaction = await connector.signTransaction(txToSignAndSend);
43
+ console.log('Transaction signed');
44
+ const signedTransactionBytes = signedTransaction.serialize();
45
+ const signedTransactionBase64 = Buffer.from(signedTransactionBytes).toString('base64');
46
+ console.log('Sending transaction via RPC...');
47
+ const sig = await rpc
48
+ .sendTransaction(signedTransactionBase64, {
49
+ encoding: 'base64',
50
+ skipPreflight: true,
51
+ preflightCommitment: 'confirmed',
52
+ })
53
+ .send();
54
+ console.log('Transaction sent via RPC:', sig);
55
+ return sig;
56
+ };
31
57
  // Check if transaction has serialized data (from deBridge)
32
58
  // deBridge returns Solana transactions as serialized hex strings in tx.data
33
59
  if (tx.data && !tx.instructions && !tx.to) {
34
- // Deserialize transaction from hex data
35
- // Remove 0x prefix if present
60
+ // deBridge returns a serialized v0 transaction (often with Address Lookup Tables).
61
+ // Do NOT decompile here: decompilation requires ALT contents and fails for unknown tables.
36
62
  const hexData = tx.data.replace(/^0x/, '');
37
63
  const buffer = Buffer.from(hexData, 'hex');
38
- // Decode transaction using @solana/kit
39
- const decoded = getTransactionDecoder().decode(new Uint8Array(buffer));
40
- // Extract blockhash from decoded transaction
41
- const compiledMessage = getCompiledTransactionMessageDecoder().decode(decoded.messageBytes);
42
- transactionMessage = decompileTransactionMessage(compiledMessage);
43
- // Get blockhash from the decoded transaction's lifetime token
44
- // In @solana/kit v5, lifetime is stored as lifetimeToken (blockhash string)
45
- if ('lifetimeToken' in compiledMessage) {
46
- blockhash = compiledMessage.lifetimeToken;
47
- // For deserialized transactions, we don't have lastValidBlockHeight
48
- // We'll need to fetch it from RPC
49
- const blockhashResponse = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();
50
- lastValidBlockHeight = blockhashResponse.value.lastValidBlockHeight;
51
- }
64
+ versionedTransaction = VersionedTransaction.deserialize(buffer);
65
+ blockhash = versionedTransaction.message.recentBlockhash;
66
+ console.log('[SolanaTx] Using serialized deBridge tx:', {
67
+ dataBytes: buffer.length,
68
+ recentBlockhash: blockhash,
69
+ signaturesCount: versionedTransaction.signatures.length,
70
+ staticAccounts: versionedTransaction.message.staticAccountKeys.length,
71
+ addressTableLookups: versionedTransaction.message.addressTableLookups.length,
72
+ });
73
+ // Fetch latest lastValidBlockHeight for confirmation flow.
74
+ const blockhashResponse = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();
75
+ lastValidBlockHeight = blockhashResponse.value.lastValidBlockHeight;
52
76
  }
53
77
  else if (tx.instructions) {
54
78
  // Transaction has instructions (from relay.link)
@@ -152,56 +176,95 @@ export function createSolanaTransactionExecutor(connector, connection) {
152
176
  else {
153
177
  throw new Error('Solana transaction must have either instructions or serialized data');
154
178
  }
155
- // Compile transaction message to get wire format
156
- console.log('Compiling transaction...');
157
- const compiledTransaction = compileTransaction(transactionMessage);
158
- // Convert to VersionedTransaction for wallet adapter signing
159
- const base64WireTransaction = getBase64EncodedWireTransaction(compiledTransaction);
160
- const transactionBytes = Buffer.from(base64WireTransaction, 'base64');
161
- console.log('Transaction bytes length:', transactionBytes.length);
162
- // Create VersionedTransaction from wire bytes
163
- const versionedTransaction = VersionedTransaction.deserialize(transactionBytes);
164
- console.log('Requesting signature...');
165
- // Sign transaction with wallet adapter
166
- const signedTransaction = await connector.signTransaction(versionedTransaction);
167
- console.log('Transaction signed');
168
- // Send transaction using RPC directly (matching reference implementation pattern)
169
- // Convert signed transaction to base64 for RPC
170
- const signedTransactionBytes = signedTransaction.serialize();
171
- const signedTransactionBase64 = Buffer.from(signedTransactionBytes).toString('base64'); // Type assertion for Base64EncodedWireTransaction
172
- console.log('Sending transaction via RPC...');
173
- // Use RPC sendTransaction with base64 encoding (matching reference implementation)
174
- const signatureResponse = await rpc
175
- .sendTransaction(signedTransactionBase64, {
176
- encoding: 'base64',
177
- skipPreflight: true,
178
- preflightCommitment: 'confirmed',
179
- })
180
- .send();
181
- const signatureString = signatureResponse;
182
- console.log('Transaction sent:', signatureString);
183
- // Wait for confirmation
184
- // Use the adapter's confirmTransaction method
185
- // Only confirm if we have blockhash and lastValidBlockHeight
186
- let result;
187
- if (blockhash && lastValidBlockHeight) {
188
- console.log('Waiting for confirmation...');
189
- result = await connection.confirmTransaction({
190
- signature: signatureString,
191
- blockhash,
192
- lastValidBlockHeight,
193
- }, 'confirmed');
194
- console.log('Transaction confirmed');
179
+ // Build VersionedTransaction from relay instructions path.
180
+ if (!versionedTransaction) {
181
+ if (!transactionMessage) {
182
+ throw new Error('Failed to build Solana transaction message');
183
+ }
184
+ console.log('Compiling transaction...');
185
+ const compiledTransaction = compileTransaction(transactionMessage);
186
+ const base64WireTransaction = getBase64EncodedWireTransaction(compiledTransaction);
187
+ const transactionBytes = Buffer.from(base64WireTransaction, 'base64');
188
+ console.log('Transaction bytes length:', transactionBytes.length);
189
+ versionedTransaction = VersionedTransaction.deserialize(transactionBytes);
190
+ console.log('[SolanaTx] Built relay v0 tx:', {
191
+ recentBlockhash: versionedTransaction.message.recentBlockhash,
192
+ signaturesCount: versionedTransaction.signatures.length,
193
+ staticAccounts: versionedTransaction.message.staticAccountKeys.length,
194
+ addressTableLookups: versionedTransaction.message.addressTableLookups.length,
195
+ });
196
+ }
197
+ let signatureString;
198
+ // Prefer wallet-adapter broadcast path (critical for wallets like MetaMask Solana).
199
+ if (connection.connection) {
200
+ try {
201
+ console.log('Sending transaction via wallet adapter...');
202
+ signatureString = await connector.sendTransaction(versionedTransaction, connection.connection);
203
+ console.log('Transaction sent via wallet adapter:', signatureString);
204
+ // MetaMask Solana can sometimes return a signature but not propagate/broadcast.
205
+ // If the signature remains unknown briefly, re-broadcast via signed raw transaction.
206
+ let seenByRpc = false;
207
+ for (let i = 0; i < 3; i++) {
208
+ const statusResp = await rpc
209
+ .getSignatureStatuses([signatureString], { searchTransactionHistory: true })
210
+ .send();
211
+ const status = statusResp.value?.[0];
212
+ console.log('[SolanaTx] Post-adapter visibility check:', { attempt: i + 1, status: status ?? null });
213
+ if (status) {
214
+ seenByRpc = true;
215
+ break;
216
+ }
217
+ await new Promise((resolve) => setTimeout(resolve, 1000));
218
+ }
219
+ if (!seenByRpc) {
220
+ console.warn('[SolanaTx] Adapter returned signature not found on RPC. Re-broadcasting via signed RPC path.');
221
+ signatureString = await sendSignedViaRpc(versionedTransaction);
222
+ }
223
+ }
224
+ catch (adapterSendError) {
225
+ console.warn('Wallet adapter sendTransaction failed, falling back to sign+RPC send:', adapterSendError);
226
+ signatureString = await sendSignedViaRpc(versionedTransaction);
227
+ }
195
228
  }
196
229
  else {
197
- // For transactions without blockhash info, return success
198
- // The transaction will be confirmed by the network
199
- console.log('No blockhash/lastValidBlockHeight, skipping confirmation');
200
- result = {
201
- value: {
202
- err: null,
203
- },
204
- };
230
+ // Fallback path when adapter connection is unavailable.
231
+ signatureString = await sendSignedViaRpc(versionedTransaction);
232
+ }
233
+ // Immediate visibility check: if null for long time, tx likely not propagated to this RPC yet.
234
+ try {
235
+ const immediateStatus = await rpc
236
+ .getSignatureStatuses([signatureString], { searchTransactionHistory: true })
237
+ .send();
238
+ console.log('[SolanaTx] Immediate signature status:', immediateStatus.value?.[0] ?? null);
239
+ }
240
+ catch (statusErr) {
241
+ console.warn('[SolanaTx] Immediate signature status check failed:', statusErr);
242
+ }
243
+ // Wait for confirmation by polling signature status from RPC.
244
+ // This avoids false positives from single-shot checks.
245
+ console.log('Waiting for confirmation...');
246
+ const confirmationDeadline = Date.now() + 90_000;
247
+ let result = { value: { err: 'timeout' } };
248
+ let pollAttempt = 0;
249
+ while (Date.now() < confirmationDeadline) {
250
+ pollAttempt += 1;
251
+ const statusResp = await rpc
252
+ .getSignatureStatuses([signatureString], { searchTransactionHistory: true })
253
+ .send();
254
+ const status = statusResp.value?.[0];
255
+ console.log('[SolanaTx] Confirmation poll:', {
256
+ attempt: pollAttempt,
257
+ status: status ?? null,
258
+ });
259
+ if (status?.err) {
260
+ result = { value: { err: status.err } };
261
+ break;
262
+ }
263
+ if (status?.confirmationStatus === 'confirmed' || status?.confirmationStatus === 'finalized') {
264
+ result = { value: { err: null } };
265
+ break;
266
+ }
267
+ await new Promise((resolve) => setTimeout(resolve, 1500));
205
268
  }
206
269
  // Check for errors
207
270
  if (result.value.err) {
@@ -0,0 +1,43 @@
1
+ import type { BridgeTransaction } from '@silentswap/sdk';
2
+ /**
3
+ * Tron wallet connector interface
4
+ * Compatible with Tron wallet adapters (e.g., TronLink, etc.)
5
+ * Tron mainnet is EVM-compatible; relay may return EVM-style tx (to, value, data).
6
+ */
7
+ export interface TronWalletConnector {
8
+ /** Send a transaction (EVM-style: to, value, data) and return tx hash */
9
+ sendTransaction: (tx: BridgeTransaction) => Promise<string>;
10
+ getAccounts: () => Promise<string[]>;
11
+ currentAccount?: string | null;
12
+ }
13
+ /**
14
+ * Tron connection interface
15
+ * Kept for consistency with Solana/Bitcoin pattern; may not be used.
16
+ */
17
+ export interface TronConnection {
18
+ }
19
+ /**
20
+ * Tron transaction executor type
21
+ * Similar to BitcoinTransactionExecutor but for Tron (EVM-style or native).
22
+ */
23
+ export type TronTransactionExecutor = (tx: BridgeTransaction) => Promise<string>;
24
+ /**
25
+ * Create a Tron transaction executor.
26
+ * Handles Tron transactions from relay.link bridge quotes (EVM-style to/value/data).
27
+ *
28
+ * @param connector - Tron wallet connector
29
+ * @param _connection - Tron connection (optional, for consistency)
30
+ * @returns Tron transaction executor function
31
+ */
32
+ export declare function createTronTransactionExecutor(connector: TronWalletConnector, _connection?: TronConnection): TronTransactionExecutor;
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.
36
+ */
37
+ export declare function convertRelayTronStepToTransaction(stepData: {
38
+ chainId?: number;
39
+ to?: string;
40
+ value?: string;
41
+ data?: string;
42
+ gas?: string;
43
+ }, chainId?: number): BridgeTransaction;
@@ -0,0 +1,40 @@
1
+ import { N_RELAY_CHAIN_ID_TRON } from '@silentswap/sdk';
2
+ /**
3
+ * Create a Tron transaction executor.
4
+ * Handles Tron transactions from relay.link bridge quotes (EVM-style to/value/data).
5
+ *
6
+ * @param connector - Tron wallet connector
7
+ * @param _connection - Tron connection (optional, for consistency)
8
+ * @returns Tron transaction executor function
9
+ */
10
+ export function createTronTransactionExecutor(connector, _connection) {
11
+ return async (tx) => {
12
+ if (!connector.currentAccount) {
13
+ const accounts = await connector.getAccounts();
14
+ if (!accounts || accounts.length === 0) {
15
+ throw new Error('Tron wallet not connected');
16
+ }
17
+ }
18
+ const txHash = await connector.sendTransaction(tx);
19
+ if (!txHash || typeof txHash !== 'string' || txHash.trim().length === 0) {
20
+ throw new Error('Tron transaction was not broadcast successfully. The wallet returned an empty transaction ID.');
21
+ }
22
+ return txHash;
23
+ };
24
+ }
25
+ /**
26
+ * Helper to convert relay.link Tron step data to BridgeTransaction.
27
+ * Relay may return EVM-style fields (chainId, to, value, data) for Tron.
28
+ */
29
+ export function convertRelayTronStepToTransaction(stepData, chainId = N_RELAY_CHAIN_ID_TRON) {
30
+ if (!stepData.to || stepData.data === undefined) {
31
+ throw new Error('Tron transaction step must have to and data fields');
32
+ }
33
+ return {
34
+ chainId,
35
+ to: stepData.to,
36
+ value: stepData.value ?? '0',
37
+ data: stepData.data,
38
+ gasLimit: stepData.gas,
39
+ };
40
+ }
@@ -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
+ /** Optional: force a single bridge provider for testing. */
36
+ forceBridgeProvider?: 'relay' | 'debridge';
35
37
  }
36
38
  export interface UseQuoteCalculationReturn {
37
39
  /** Calculate quote and return result */
@@ -51,4 +53,4 @@ export interface UseQuoteCalculationReturn {
51
53
  * - Quote request construction and fetching
52
54
  * - Destination amount updates
53
55
  */
54
- export declare function useQuoteCalculation({ address, evmAddress, wallet, depositorAddress, getQuote, getPrice, setDestinations, proId, }: UseQuoteCalculationOptions): UseQuoteCalculationReturn;
56
+ export declare function useQuoteCalculation({ address, evmAddress, wallet, depositorAddress, getQuote, getPrice, setDestinations, proId, forceBridgeProvider, }: UseQuoteCalculationOptions): UseQuoteCalculationReturn;
@@ -13,7 +13,7 @@ import { BigNumber } from 'bignumber.js';
13
13
  * - Destination amount updates
14
14
  */
15
15
  // TODO: Simplify this hook by removing the useCallback and useState and just returning the calculateQuote function.
16
- export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddress, getQuote, getPrice, setDestinations, proId, }) {
16
+ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddress, getQuote, getPrice, setDestinations, proId, forceBridgeProvider, }) {
17
17
  const [loadingAmounts, setLoadingAmounts] = useState(false);
18
18
  const [depositAmountUsdc, setDepositAmountUsdc] = useState(0);
19
19
  const calculateQuote = useCallback(async (debouncedSourceAsset, debouncedSourceAmount, destinations, splits) => {
@@ -87,7 +87,7 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
87
87
  decimals: assetInfo.decimals,
88
88
  precision: assetInfo.precision,
89
89
  });
90
- const solveResult = await solveOptimalUsdcAmount(N_RELAY_CHAIN_ID_SOLANA, originCurrency, sourceAmountInUnitsForQuote, solanaAddress, undefined, X_MAX_IMPACT_PERCENT, depositorAddress, getAddress(evmAddress));
90
+ const solveResult = await solveOptimalUsdcAmount(N_RELAY_CHAIN_ID_SOLANA, originCurrency, sourceAmountInUnitsForQuote, solanaAddress, undefined, X_MAX_IMPACT_PERCENT, depositorAddress, getAddress(evmAddress), forceBridgeProvider);
91
91
  if (abortController.signal.aborted) {
92
92
  setLoadingAmounts(false);
93
93
  return null;
@@ -121,7 +121,8 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
121
121
  // to stay within the source balance (matches Svelte's relay_solve_uusdc_amount)
122
122
  const solveResult = await solveOptimalUsdcAmount(N_RELAY_CHAIN_ID_BITCOIN, SBTC_ADDR_BITCOIN_NATIVE, sourceAmountInUnitsForQuote, bitcoinAddress, // Bitcoin address for relay 'user' parameter
123
123
  undefined, // depositCalldata - will use phony
124
- X_MAX_IMPACT_PERCENT, depositorAddress, getAddress(evmAddress));
124
+ X_MAX_IMPACT_PERCENT, depositorAddress, getAddress(evmAddress), // EVM address for deposit calldata & recipient
125
+ forceBridgeProvider);
125
126
  if (abortController.signal.aborted) {
126
127
  setLoadingAmounts(false);
127
128
  return null;
@@ -143,7 +144,8 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
143
144
  const sourceAmountBN = BigNumber(debouncedSourceAmount);
144
145
  const sourceAmountInUnits = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
145
146
  const solveResult = await solveOptimalUsdcAmount(srcChainId, srcToken, sourceAmountInUnits, address, undefined, // depositCalldata
146
- X_MAX_IMPACT_PERCENT, depositorAddress);
147
+ X_MAX_IMPACT_PERCENT, depositorAddress, // Required depositor address
148
+ undefined, forceBridgeProvider);
147
149
  if (abortController.signal.aborted) {
148
150
  setLoadingAmounts(false);
149
151
  return null;
@@ -64,6 +64,8 @@ export interface useSilentQuoteOptions {
64
64
  setDestinations: (updater: Destination[] | ((prev: Destination[]) => Destination[])) => void;
65
65
  /** Pro user ID for volume tracking (sent with quote request, matching Svelte's AppState.proId) */
66
66
  proId?: string;
67
+ /** Optional: force a single bridge provider for testing. */
68
+ forceBridgeProvider?: 'relay' | 'debridge';
67
69
  }
68
70
  /**
69
71
  * Return type for useSilentQuote hook
@@ -99,4 +101,4 @@ export interface ExecuteSwapParams {
99
101
  /** Optional integrator ID for tracking */
100
102
  integratorId?: string;
101
103
  }
102
- export declare function useSilentQuote({ client, address, evmAddress, solAddress, walletClient, connector, wallet, walletLoading, walletError, generateWallet, requestWalletConnect, onStatus, solanaConnector, solanaConnection, solanaRpcUrl, bitcoinConnector, bitcoinConnection, getPrice, setDestinations, proId, }: useSilentQuoteOptions): useSilentQuoteReturn;
104
+ export declare function useSilentQuote({ client, address, evmAddress, solAddress, walletClient, connector, wallet, walletLoading, walletError, generateWallet, requestWalletConnect, onStatus, solanaConnector, solanaConnection, solanaRpcUrl, bitcoinConnector, bitcoinConnection, getPrice, setDestinations, proId, forceBridgeProvider, }: useSilentQuoteOptions): useSilentQuoteReturn;
@@ -6,7 +6,7 @@ import { useOrderSigning } from './useOrderSigning.js';
6
6
  import { APPROVE_POST_DELAY_MS, useBridgeExecution } from './useBridgeExecution.js';
7
7
  import { useTransaction } from '../useTransaction.js';
8
8
  import { useQuoteCalculation } from './useQuoteCalculation.js';
9
- export function useSilentQuote({ client, address, evmAddress, solAddress, walletClient, connector, wallet, walletLoading = false, walletError = null, generateWallet, requestWalletConnect, onStatus, solanaConnector, solanaConnection, solanaRpcUrl, bitcoinConnector, bitcoinConnection, getPrice, setDestinations, proId, }) {
9
+ export function useSilentQuote({ client, address, evmAddress, solAddress, walletClient, connector, wallet, walletLoading = false, walletError = null, generateWallet, requestWalletConnect, onStatus, solanaConnector, solanaConnection, solanaRpcUrl, bitcoinConnector, bitcoinConnection, getPrice, setDestinations, proId, forceBridgeProvider, }) {
10
10
  const [isLoading, setIsLoading] = useState(false);
11
11
  const [currentStep, setCurrentStep] = useState('');
12
12
  const [quote, setQuote] = useState(null);
@@ -86,6 +86,7 @@ export function useSilentQuote({ client, address, evmAddress, solAddress, wallet
86
86
  getPrice,
87
87
  setDestinations,
88
88
  proId,
89
+ forceBridgeProvider,
89
90
  });
90
91
  /**
91
92
  * Execute the complete swap flow
@@ -320,9 +320,9 @@ export function useQuote({ address, maxImpactPercent = X_MAX_IMPACT_PERCENT, for
320
320
  if (direction === 'egress') {
321
321
  return null; // Skip DeBridge for egress, will use Relay.link only
322
322
  }
323
- // Skip DeBridge for Solana and Bitcoin - use relay only
324
- if (isSolanaChain || isBitcoinAsset(assetCaip19)) {
325
- return null; // Skip DeBridge for Solana and Bitcoin, will use Relay.link only
323
+ // Skip DeBridge for Bitcoin only
324
+ if (isBitcoinAsset(assetCaip19)) {
325
+ return null; // Skip DeBridge for Bitcoin, will use Relay.link only
326
326
  }
327
327
  // For reverse calculation ingress: use srcChainTokenInAmount: 'auto' and specify dstChainTokenOutAmount
328
328
  const debridgeParams = {
@@ -330,6 +330,8 @@ export function useQuote({ address, maxImpactPercent = X_MAX_IMPACT_PERCENT, for
330
330
  srcChainTokenIn: tokenAddress === ZERO_ADDRESS ? ZERO_ADDRESS : tokenAddress,
331
331
  dstChainId: NI_CHAIN_ID_AVALANCHE,
332
332
  dstChainTokenOut: S0X_ADDR_USDC_AVALANCHE,
333
+ prependOperatingExpenses: true,
334
+ additionalTakerRewardBps: 0,
333
335
  };
334
336
  if (isReverseCalculation && direction === 'ingress' && targetAmount) {
335
337
  // For reverse ingress: specify destination amount, let DeBridge calculate source
@@ -349,6 +351,15 @@ export function useQuote({ address, maxImpactPercent = X_MAX_IMPACT_PERCENT, for
349
351
  debridgeParams.srcChainTokenInAmount = tokenAmount;
350
352
  debridgeParams.dstChainTokenOutAmount = 'auto';
351
353
  }
354
+ // Solana source requires Solana-format authority fields and estimate disabled.
355
+ if (isSolanaChain) {
356
+ debridgeParams.enableEstimate = false;
357
+ debridgeParams.account = normalizedAddress;
358
+ debridgeParams.srcChainOrderAuthorityAddress = normalizedAddress;
359
+ debridgeParams.srcChainRefundAddress = normalizedAddress;
360
+ debridgeParams.dstChainTokenOutRecipient = EVM_PHONY_ADDRESS;
361
+ debridgeParams.dstChainOrderAuthorityAddress = EVM_PHONY_ADDRESS;
362
+ }
352
363
  const quote = await fetchDebridgeOrder(debridgeParams, signal);
353
364
  return quote;
354
365
  }
@@ -3,6 +3,7 @@ import { erc20Abi } from 'viem';
3
3
  import { executeRelayBridge, executeDebridgeBridge, getBridgeStatus, createTransactionExecutor, createChainSwitcher, ensureChain, waitForTransactionConfirmation, parseTransactionRequestForViem, createPublicClientWithRpc, getChainById, } from '@silentswap/sdk';
4
4
  import { createSolanaTransactionExecutor } from './silent/solana-transaction.js';
5
5
  import { createBitcoinTransactionExecutor, convertRelayBitcoinStepToTransaction } from './silent/bitcoin-transaction.js';
6
+ import { N_DEBRIDGE_CHAIN_ID_SOLANA } from '@silentswap/sdk';
6
7
  import { N_RELAY_CHAIN_ID_BITCOIN } from '@silentswap/sdk';
7
8
  /**
8
9
  * React hook for executing transactions
@@ -105,8 +106,8 @@ export function useTransaction({ walletClient, connector, solanaConnector, solan
105
106
  return false;
106
107
  });
107
108
  const hasBitcoinTransactions = hasBitcoinInTxs || hasBitcoinInRoute;
108
- // Check if quote contains Solana transactions
109
- const hasSolanaTransactions = quote.txs.some((tx) => tx.instructions !== undefined);
109
+ // Check if quote contains Solana transactions (relay instructions or deBridge serialized tx)
110
+ const hasSolanaTransactions = quote.txs.some((tx) => tx.instructions !== undefined || tx.chainId === N_DEBRIDGE_CHAIN_ID_SOLANA);
110
111
  if (hasBitcoinTransactions) {
111
112
  // Bitcoin transactions require Bitcoin connector
112
113
  if (!bitcoinConnector) {
@@ -218,8 +219,10 @@ export function useTransaction({ walletClient, connector, solanaConnector, solan
218
219
  onStatus?.(step);
219
220
  });
220
221
  case 'debridge':
221
- // DeBridge doesn't support Solana
222
- throw new Error('DeBridge does not support Solana transactions');
222
+ return await executeDebridgeBridge(quote, executeTx, switchChain, (step) => {
223
+ setCurrentStep(step);
224
+ onStatus?.(step);
225
+ });
223
226
  default:
224
227
  throw new Error(`Unsupported bridge provider: ${quote.provider}`);
225
228
  }
@@ -421,7 +424,7 @@ export function useTransaction({ walletClient, connector, solanaConnector, solan
421
424
  catch (err) {
422
425
  const error = err instanceof Error ? err : new Error('Failed to get bridge status');
423
426
  setError(error);
424
- return null;
427
+ throw error;
425
428
  }
426
429
  finally {
427
430
  setIsLoading(false);
@@ -0,0 +1 @@
1
+ export declare function isTronAssetCaip19(caip19: string): boolean;
@@ -0,0 +1,3 @@
1
+ export function isTronAssetCaip19(caip19) {
2
+ return caip19.startsWith('tron:');
3
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@silentswap/react",
3
3
  "type": "module",
4
- "version": "0.0.91",
4
+ "version": "0.0.92",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -24,8 +24,8 @@
24
24
  "dependencies": {
25
25
  "@bigmi/core": "^0.6.5",
26
26
  "@ensdomains/ensjs": "^4.2.0",
27
- "@silentswap/sdk": "0.0.91",
28
- "@silentswap/ui-kit": "0.0.91",
27
+ "@silentswap/sdk": "0.0.92",
28
+ "@silentswap/ui-kit": "0.0.92",
29
29
  "@solana/codecs-strings": "^5.1.0",
30
30
  "@solana/kit": "^5.1.0",
31
31
  "@solana/rpc": "^5.1.0",