@silentswap/react 0.0.73 → 0.0.75

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
  }
@@ -206,6 +206,18 @@ export function useAuth({ client, address, walletClient, domain = typeof window
206
206
  const isAuthenticated = useCallback(() => {
207
207
  return auth !== null && auth.authExpires > Date.now();
208
208
  }, [auth]);
209
+ // Clear auth when address changes (e.g. user switched from MetaMask to Phantom)
210
+ const prevAddressRef = useRef(normalizedAddress);
211
+ useEffect(() => {
212
+ if (prevAddressRef.current && normalizedAddress && prevAddressRef.current !== normalizedAddress) {
213
+ setAuth(null);
214
+ setNonce(null);
215
+ setError(null);
216
+ setIsAutoAuthenticating(false);
217
+ hasAutoAuthenticatedRef.current = false;
218
+ }
219
+ prevAddressRef.current = normalizedAddress;
220
+ }, [normalizedAddress]);
209
221
  // Load cached auth on mount
210
222
  useEffect(() => {
211
223
  if (normalizedAddress && !auth && !isLoading) {
@@ -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
@@ -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;
@@ -6,7 +6,7 @@ import { useOrderSigning } from './useOrderSigning.js';
6
6
  import { 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, 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, }) {
10
10
  const [isLoading, setIsLoading] = useState(false);
11
11
  const [currentStep, setCurrentStep] = useState('');
12
12
  const [quote, setQuote] = useState(null);
@@ -102,7 +102,29 @@ export function useSilentQuote({ client, address, evmAddress, solAddress, wallet
102
102
  if (!rawAddress) {
103
103
  throw new Error('Address required for swap execution');
104
104
  }
105
+ // If no wallet client (e.g. user switched accounts on mobile or declined), request connection then throw so user can retry
105
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
+ }
106
128
  throw new Error('Wallet client required for swap execution');
107
129
  }
108
130
  // Check if wallet generation previously failed - retry if so
@@ -145,6 +167,25 @@ export function useSilentQuote({ client, address, evmAddress, solAddress, wallet
145
167
  throw new Error('Wallet is being generated. Please wait for wallet generation to complete before executing swap.');
146
168
  }
147
169
  }
170
+ // Wallet generation hasn't started yet (e.g. auto-trigger effect hasn't fired) - trigger it now
171
+ if (!walletRef.current && !walletLoadingRef.current && !walletErrorRef.current && generateWalletRef.current) {
172
+ setCurrentStep('Generating wallet...');
173
+ onStatus?.('Generating wallet...');
174
+ try {
175
+ await generateWalletRef.current();
176
+ // Wait for wallet ref to update after generation
177
+ const maxWaitTime = 30000;
178
+ const checkInterval = 100;
179
+ const startTime = Date.now();
180
+ while (!walletRef.current && Date.now() - startTime < maxWaitTime) {
181
+ await new Promise((resolve) => setTimeout(resolve, checkInterval));
182
+ }
183
+ }
184
+ catch (err) {
185
+ const error = err instanceof Error ? err : new Error('Failed to generate wallet');
186
+ throw new Error(`Wallet generation failed: ${error.message}. Please try again or ensure wallet is connected and authenticated.`);
187
+ }
188
+ }
148
189
  // Final check: wallet must exist for swap execution
149
190
  if (!walletRef.current) {
150
191
  throw new Error('Wallet is required for swap execution. Please ensure wallet is connected and authenticated.');
@@ -287,6 +328,8 @@ export function useSilentQuote({ client, address, evmAddress, solAddress, wallet
287
328
  rawAddress,
288
329
  evmAddress,
289
330
  walletClient,
331
+ connector,
332
+ requestWalletConnect,
290
333
  walletLoading,
291
334
  walletError,
292
335
  generateWallet,
@@ -1,4 +1,4 @@
1
- import { useCallback, useState, useEffect } from 'react';
1
+ import { useCallback, useState, useEffect, useRef } from 'react';
2
2
  import { createHdFacilitatorGroupFromEntropy, createEip712DocForWalletGeneration, queryDepositCount, loadWalletData, saveWalletData, clearWalletData, XT_TTL_SESSION_CACHE, ensureChain, base93ToBytes, bytesToBase93, } from '@silentswap/sdk';
3
3
  import { getAddress, hexToBytes } from 'viem';
4
4
  /**
@@ -182,6 +182,15 @@ export function useWallet({ client, address, auth, walletClient, connector, allD
182
182
  clearWallet();
183
183
  await generateWallet();
184
184
  }, [clearWallet, generateWallet]);
185
+ // Clear wallet when address changes (e.g. user switched from MetaMask to Phantom)
186
+ const prevAddressRef = useRef(normalizedAddress);
187
+ useEffect(() => {
188
+ if (prevAddressRef.current && normalizedAddress && prevAddressRef.current !== normalizedAddress) {
189
+ setWallet(null);
190
+ setError(null);
191
+ }
192
+ prevAddressRef.current = normalizedAddress;
193
+ }, [normalizedAddress]);
185
194
  // Auto-load cached wallet on mount
186
195
  useEffect(() => {
187
196
  if (normalizedAddress && !wallet && !isLoading) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@silentswap/react",
3
3
  "type": "module",
4
- "version": "0.0.73",
4
+ "version": "0.0.75",
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.73",
28
- "@silentswap/ui-kit": "0.0.73",
27
+ "@silentswap/sdk": "0.0.75",
28
+ "@silentswap/ui-kit": "0.0.75",
29
29
  "@solana/codecs-strings": "^5.1.0",
30
30
  "@solana/kit": "^5.1.0",
31
31
  "@solana/rpc": "^5.1.0",