@silentswap/react 0.0.72 → 0.0.74

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.
@@ -43,7 +43,7 @@ export interface SilentSwapContextType {
43
43
  solanaRpcUrl?: string;
44
44
  }
45
45
  export declare function useSilentSwap(): SilentSwapContextType;
46
- export declare function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, }: {
46
+ export declare function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, requestWalletConnect, }: {
47
47
  children: React.ReactNode;
48
48
  client: SilentSwapClient;
49
49
  evmAddress?: string;
@@ -62,4 +62,6 @@ export declare function SilentSwapProvider({ children, client, evmAddress, solAd
62
62
  bitcoinRpcUrl?: string;
63
63
  /** Pro user ID for volume tracking (from ?pro= URL or localStorage silentswap:pro) */
64
64
  proId?: string;
65
+ /** Optional: request wallet connection when walletClient is missing (e.g. user switched account on mobile). */
66
+ requestWalletConnect?: () => Promise<void>;
65
67
  }): import("react/jsx-runtime").JSX.Element;
@@ -63,7 +63,7 @@ export function useSilentSwap() {
63
63
  }
64
64
  return context;
65
65
  }
66
- function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bitcoinAddress, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, config, solanaRpcUrl, connector, isConnected = false, walletClient, proId, }) {
66
+ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bitcoinAddress, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment, config, solanaRpcUrl, connector, isConnected = false, walletClient, proId, requestWalletConnect, }) {
67
67
  // Authentication hook - only for EVM
68
68
  const { auth, isLoading: authLoading } = useAuth({
69
69
  client,
@@ -125,6 +125,7 @@ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bit
125
125
  generateWallet,
126
126
  walletClient,
127
127
  connector,
128
+ requestWalletConnect,
128
129
  solanaConnector,
129
130
  solanaConnection,
130
131
  bitcoinConnector,
@@ -222,7 +223,7 @@ function SilentSwapInnerProvider({ children, client, evmAddress, solAddress, bit
222
223
  solanaRpcUrl,
223
224
  }, children: children });
224
225
  }
225
- export function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment = ENVIRONMENT.STAGING, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, }) {
226
+ export function SilentSwapProvider({ children, client, evmAddress, solAddress, connector, isConnected, solanaConnector, solanaConnection, bitcoinConnector, bitcoinConnection, environment = ENVIRONMENT.STAGING, baseUrl, solanaRpcUrl, walletClient, bitcoinAddress, bitcoinRpcUrl, proId, requestWalletConnect, }) {
226
227
  const config = useMemo(() => {
227
228
  const computedBaseUrl = baseUrl ?? ENVIRONMENT_CONFIGS[environment].baseUrl;
228
229
  console.log('[SilentSwapProvider] Creating config:', { environment, baseUrl, computedBaseUrl });
@@ -231,5 +232,5 @@ export function SilentSwapProvider({ children, client, evmAddress, solAddress, c
231
232
  baseUrl: computedBaseUrl,
232
233
  };
233
234
  }, [environment, baseUrl]);
234
- 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, children: children }) }) }) }) }));
235
+ 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 }) }) }) }) }));
235
236
  }
@@ -656,10 +656,20 @@ export function useBridgeExecution(walletClient, connector, solanaConnector, sol
656
656
  // Note: allowanceTarget is not needed here because approval is already handled
657
657
  // in executeSwap before bridge execution (matching Svelte silentswap.ts line 985-986).
658
658
  // DeBridge will provide its own allowanceTarget from the response if needed.
659
- return await executeDebridgeBridge(sourceChainId, sourceTokenAddress, isSourceNative, bridgeUsdcAmount, depositCalldata, evmSignerAddress, // Use EVM signer address for deposit calldata
660
- '', // allowanceTarget not needed - approval handled before bridge execution
661
- depositorAddress, // Pass environment-based depositor address
662
- walletClient, connector, setCurrentStep, onStatus);
659
+ try {
660
+ return await executeDebridgeBridge(sourceChainId, sourceTokenAddress, isSourceNative, bridgeUsdcAmount, depositCalldata, evmSignerAddress, // Use EVM signer address for deposit calldata
661
+ '', // allowanceTarget not needed - approval handled before bridge execution
662
+ depositorAddress, // Pass environment-based depositor address
663
+ walletClient, connector, setCurrentStep, onStatus);
664
+ }
665
+ catch (error) {
666
+ console.warn('DeBridge execution failed, falling back to relay.link bridge:', error instanceof Error ? error.message : String(error));
667
+ // On deBridge execution failure, fall back to relay.link using the same USDC amount.
668
+ // This matches the requirement: if deBridge quote/order fails, use relay quote/bridge.
669
+ return await executeRelayBridge(walletClient, connector, sourceChainId, sourceTokenAddress, isSourceNative, bridgeUsdcAmount, approveUsdcCalldata, depositCalldata, evmUserForBridge, // Use wallet account address for bridge quotes
670
+ depositorAddress, // Pass environment-based depositor address
671
+ setCurrentStep, onStatus);
672
+ }
663
673
  }
664
674
  else if (selectedProvider === 'relay') {
665
675
  return await executeRelayBridge(walletClient, connector, sourceChainId, sourceTokenAddress, isSourceNative, bridgeUsdcAmount, approveUsdcCalldata, depositCalldata, evmUserForBridge, // Use wallet account address for bridge quotes
@@ -9,6 +9,8 @@ export interface Destination {
9
9
  export interface QuoteCalculationResult {
10
10
  quote: QuoteResponse;
11
11
  usdcAmount: string;
12
+ /** Source amount in smallest units (same as used for the quote request). Use this for order/deposit to avoid mismatch. */
13
+ sourceAmountInUnits: string;
12
14
  facilitatorGroup: HdFacilitatorGroup | undefined;
13
15
  bridgeProvider: BridgeProvider;
14
16
  allowanceTarget: `0x${string}` | undefined;
@@ -30,6 +30,7 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
30
30
  const isSourceSolana = isSolanaAsset(debouncedSourceAsset);
31
31
  const isSourceBitcoin = isBitcoinAsset(debouncedSourceAsset);
32
32
  let usdcAmountOut;
33
+ let sourceAmountInUnitsForQuote;
33
34
  let bridgeProviderResult = 'none';
34
35
  let allowanceTargetResult = undefined;
35
36
  if (isSourceUsdcAvalanche) {
@@ -37,7 +38,8 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
37
38
  if (!assetInfo)
38
39
  throw new Error(`USDC asset not found`);
39
40
  const sourceAmountBN = BigNumber(debouncedSourceAmount);
40
- usdcAmountOut = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
41
+ sourceAmountInUnitsForQuote = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
42
+ usdcAmountOut = sourceAmountInUnitsForQuote;
41
43
  }
42
44
  else if (isSourceSolana) {
43
45
  // Solana assets - use relay.link only (not deBridge)
@@ -45,7 +47,8 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
45
47
  if (!assetInfo)
46
48
  throw new Error(`Solana asset not found`);
47
49
  const sourceAmountBN = BigNumber(debouncedSourceAmount);
48
- const sourceAmountInUnits = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
50
+ sourceAmountInUnitsForQuote = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
51
+ const sourceAmountInUnits = sourceAmountInUnitsForQuote;
49
52
  // Parse Solana CAIP-19 to get chain ID and token address
50
53
  const solanaParsed = parseSolanaCaip19(debouncedSourceAsset);
51
54
  if (!solanaParsed)
@@ -89,6 +92,7 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
89
92
  usdcAmountOut = relayQuote.details.currencyOut.amount;
90
93
  bridgeProviderResult = 'relay'; // Solana uses relay only
91
94
  allowanceTargetResult = undefined; // Relay doesn't need allowance target
95
+ // sourceAmountInUnitsForQuote already set above for Solana
92
96
  }
93
97
  else if (isSourceBitcoin) {
94
98
  // Bitcoin assets - only relay.link supports Bitcoin
@@ -96,7 +100,8 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
96
100
  if (!assetInfo)
97
101
  throw new Error(`Bitcoin asset not found`);
98
102
  const sourceAmountBN = BigNumber(debouncedSourceAmount);
99
- const sourceAmountInUnits = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
103
+ sourceAmountInUnitsForQuote = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
104
+ const sourceAmountInUnits = sourceAmountInUnitsForQuote;
100
105
  // For Bitcoin swaps, we need:
101
106
  // - Bitcoin address for the 'user' parameter in relay quote
102
107
  // - EVM address for the 'recipient' parameter
@@ -131,6 +136,7 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
131
136
  usdcAmountOut = relayQuote.details.currencyOut.amount;
132
137
  bridgeProviderResult = 'relay'; // Bitcoin only supports relay
133
138
  allowanceTargetResult = undefined; // Bitcoin doesn't need allowance
139
+ // sourceAmountInUnitsForQuote already set above for Bitcoin
134
140
  }
135
141
  else {
136
142
  // EVM assets
@@ -153,6 +159,7 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
153
159
  usdcAmountOut = solveResult.usdcAmountOut.toString();
154
160
  bridgeProviderResult = solveResult.provider;
155
161
  allowanceTargetResult = solveResult.allowanceTarget;
162
+ sourceAmountInUnitsForQuote = sourceAmountInUnits; // EVM: use same amount that was sent to solveOptimalUsdcAmount
156
163
  }
157
164
  // Use wallet's facilitator group if available, otherwise create a temporary one
158
165
  let facilitatorGroupForQuote;
@@ -402,12 +409,13 @@ export function useQuoteCalculation({ address, evmAddress, wallet, depositorAddr
402
409
  // Reset loading state only on successful quote
403
410
  setLoadingAmounts(false);
404
411
  }
405
- // Return both quote, usdcAmount, and facilitatorGroup (matches Svelte's solve_uusdc_amount return [usdcAmount, provider, allowanceTarget])
406
- // The facilitatorGroup is resolved during quote fetching and can be reused in executeSwap
412
+ // Return both quote, usdcAmount, sourceAmountInUnits, and facilitatorGroup (matches Svelte's solve_uusdc_amount return [usdcAmount, provider, allowanceTarget])
413
+ // Use sourceAmountInUnitsForQuote for order/deposit so amount always matches what the quote was built with
407
414
  return quoteResult
408
415
  ? {
409
416
  quote: quoteResult,
410
417
  usdcAmount: usdcAmountOut,
418
+ sourceAmountInUnits: sourceAmountInUnitsForQuote,
411
419
  facilitatorGroup: facilitatorGroupForQuote,
412
420
  bridgeProvider: bridgeProviderResult,
413
421
  allowanceTarget: allowanceTargetResult,
@@ -45,7 +45,8 @@ export interface useSilentQuoteOptions {
45
45
  walletError?: Error | null;
46
46
  /** Function to generate wallet (for retry on swap execution) */
47
47
  generateWallet?: () => Promise<void>;
48
- /** Facilitator group for the swap, or a function that resolves it */
48
+ /** Optional: request wallet connection when walletClient is missing (e.g. user switched account or declined). Called before throwing so UI can prompt user to connect and retry. */
49
+ requestWalletConnect?: () => Promise<void>;
49
50
  /** Status update callback */
50
51
  onStatus?: (status: string) => void;
51
52
  /** Solana wallet connector (required for Solana swaps) */
@@ -98,4 +99,4 @@ export interface ExecuteSwapParams {
98
99
  /** Optional integrator ID for tracking */
99
100
  integratorId?: string;
100
101
  }
101
- export declare function useSilentQuote({ client, address, evmAddress, solAddress, walletClient, connector, wallet, walletLoading, walletError, generateWallet, onStatus, solanaConnector, solanaConnection, solanaRpcUrl, bitcoinConnector, bitcoinConnection, getPrice, setDestinations, proId, }: useSilentQuoteOptions): useSilentQuoteReturn;
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;
@@ -1,13 +1,12 @@
1
1
  import { useCallback, useMemo, useState, useRef, useEffect } from 'react';
2
- import { hexToBase58, isSolanaAsset, isBitcoinAsset, parseEvmCaip19, S_CAIP19_USDC_AVALANCHE, NI_CHAIN_ID_AVALANCHE, getAssetByCaip19, } from '@silentswap/sdk';
2
+ import { hexToBase58, isSolanaAsset, isBitcoinAsset, parseEvmCaip19, S_CAIP19_USDC_AVALANCHE, NI_CHAIN_ID_AVALANCHE, } from '@silentswap/sdk';
3
3
  import { getAddress } from 'viem';
4
- import { BigNumber } from 'bignumber.js';
5
4
  import { useQuoteFetching } from './useQuoteFetching.js';
6
5
  import { useOrderSigning } from './useOrderSigning.js';
7
6
  import { useBridgeExecution } from './useBridgeExecution.js';
8
7
  import { useTransaction } from '../useTransaction.js';
9
8
  import { useQuoteCalculation } from './useQuoteCalculation.js';
10
- export function useSilentQuote({ client, address, evmAddress, solAddress, walletClient, connector, wallet, walletLoading = false, walletError = null, generateWallet, 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, }) {
11
10
  const [isLoading, setIsLoading] = useState(false);
12
11
  const [currentStep, setCurrentStep] = useState('');
13
12
  const [quote, setQuote] = useState(null);
@@ -103,7 +102,29 @@ export function useSilentQuote({ client, address, evmAddress, solAddress, wallet
103
102
  if (!rawAddress) {
104
103
  throw new Error('Address required for swap execution');
105
104
  }
105
+ // If no wallet client (e.g. user switched accounts on mobile or declined), request connection then throw so user can retry
106
106
  if (!walletClient) {
107
+ setCurrentStep('Wallet connection required...');
108
+ onStatus?.('Wallet connection required...');
109
+ const retryMessage = 'Please complete wallet connection in your wallet app and try again.';
110
+ try {
111
+ if (requestWalletConnect) {
112
+ await requestWalletConnect();
113
+ throw new Error(retryMessage);
114
+ }
115
+ // Fallback: try connector.connect() if available (e.g. wagmi connector)
116
+ const connectorWithConnect = connector;
117
+ if (connectorWithConnect?.connect) {
118
+ await connectorWithConnect.connect();
119
+ throw new Error(retryMessage);
120
+ }
121
+ }
122
+ catch (err) {
123
+ if (err instanceof Error && err.message === retryMessage) {
124
+ throw err;
125
+ }
126
+ throw new Error('Wallet connection required for swap. Please connect your wallet and try again.');
127
+ }
107
128
  throw new Error('Wallet client required for swap execution');
108
129
  }
109
130
  // Check if wallet generation previously failed - retry if so
@@ -161,18 +182,11 @@ export function useSilentQuote({ client, address, evmAddress, solAddress, wallet
161
182
  // Ensure loadingAmounts is reset if handleGetQuote failed
162
183
  throw new Error('Failed to get quote');
163
184
  }
164
- const { quote: quoteResponse, usdcAmount: effectiveUsdcAmount, facilitatorGroup: passedFacilitatorGroup, bridgeProvider: effectiveProvider, allowanceTarget: effectiveAllowanceTarget, } = quoteResult;
185
+ const { quote: quoteResponse, usdcAmount: effectiveUsdcAmount, sourceAmountInUnits, facilitatorGroup: passedFacilitatorGroup, bridgeProvider: effectiveProvider, allowanceTarget: effectiveAllowanceTarget, } = quoteResult;
165
186
  setCurrentStep('Executing swap...');
166
187
  onStatus?.('Executing swap...');
167
- // Convert source amount to units
168
- const assetInfo = getAssetByCaip19(sourceAsset);
169
- if (!assetInfo) {
170
- throw new Error(`Asset not found: ${sourceAsset}`);
171
- }
172
- const sourceAmountBN = BigNumber(sourceAmount);
173
- console.log('sourceAmountBN', sourceAmountBN);
174
- const sourceAmountInUnits = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
175
- console.log('sourceAmountInUnits', sourceAmountInUnits, assetInfo);
188
+ // Use the exact source amount in units from the quote result so order and deposit
189
+ // always match what was requested (avoids rounding/precision mismatch)
176
190
  // Determine provider for bridge swap
177
191
  const isDirectDeposit = sourceAsset === S_CAIP19_USDC_AVALANCHE;
178
192
  let providerForSwap = undefined;
@@ -295,6 +309,8 @@ export function useSilentQuote({ client, address, evmAddress, solAddress, wallet
295
309
  rawAddress,
296
310
  evmAddress,
297
311
  walletClient,
312
+ connector,
313
+ requestWalletConnect,
298
314
  walletLoading,
299
315
  walletError,
300
316
  generateWallet,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@silentswap/react",
3
3
  "type": "module",
4
- "version": "0.0.72",
4
+ "version": "0.0.74",
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.72",
28
- "@silentswap/ui-kit": "0.0.72",
27
+ "@silentswap/sdk": "0.0.74",
28
+ "@silentswap/ui-kit": "0.0.74",
29
29
  "@solana/codecs-strings": "^5.1.0",
30
30
  "@solana/kit": "^5.1.0",
31
31
  "@solana/rpc": "^5.1.0",