@silentswap/react 0.0.41

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.
Files changed (86) hide show
  1. package/README.md +48 -0
  2. package/dist/contexts/AssetsContext.d.ts +24 -0
  3. package/dist/contexts/AssetsContext.js +83 -0
  4. package/dist/contexts/BalancesContext.d.ts +28 -0
  5. package/dist/contexts/BalancesContext.js +533 -0
  6. package/dist/contexts/OrdersContext.d.ts +53 -0
  7. package/dist/contexts/OrdersContext.js +240 -0
  8. package/dist/contexts/PricesContext.d.ts +12 -0
  9. package/dist/contexts/PricesContext.js +109 -0
  10. package/dist/contexts/SilentSwapContext.d.ts +58 -0
  11. package/dist/contexts/SilentSwapContext.js +205 -0
  12. package/dist/hooks/silent/orderTrackingWebSocketManager.d.ts +48 -0
  13. package/dist/hooks/silent/orderTrackingWebSocketManager.js +284 -0
  14. package/dist/hooks/silent/solana-transaction.d.ts +60 -0
  15. package/dist/hooks/silent/solana-transaction.js +236 -0
  16. package/dist/hooks/silent/useAuth.d.ts +90 -0
  17. package/dist/hooks/silent/useAuth.js +269 -0
  18. package/dist/hooks/silent/useBridgeExecution.d.ts +39 -0
  19. package/dist/hooks/silent/useBridgeExecution.js +877 -0
  20. package/dist/hooks/silent/useOrderSigning.d.ts +34 -0
  21. package/dist/hooks/silent/useOrderSigning.js +133 -0
  22. package/dist/hooks/silent/useOrderTracking.d.ts +174 -0
  23. package/dist/hooks/silent/useOrderTracking.js +524 -0
  24. package/dist/hooks/silent/useQuoteCalculation.d.ts +50 -0
  25. package/dist/hooks/silent/useQuoteCalculation.js +331 -0
  26. package/dist/hooks/silent/useQuoteFetching.d.ts +18 -0
  27. package/dist/hooks/silent/useQuoteFetching.js +54 -0
  28. package/dist/hooks/silent/useRefund.d.ts +26 -0
  29. package/dist/hooks/silent/useRefund.js +134 -0
  30. package/dist/hooks/silent/useSilentClient.d.ts +16 -0
  31. package/dist/hooks/silent/useSilentClient.js +32 -0
  32. package/dist/hooks/silent/useSilentOrders.d.ts +174 -0
  33. package/dist/hooks/silent/useSilentOrders.js +73 -0
  34. package/dist/hooks/silent/useSilentQuote.d.ts +88 -0
  35. package/dist/hooks/silent/useSilentQuote.js +381 -0
  36. package/dist/hooks/silent/useWallet.d.ts +76 -0
  37. package/dist/hooks/silent/useWallet.js +203 -0
  38. package/dist/hooks/useAssetPrice.d.ts +8 -0
  39. package/dist/hooks/useAssetPrice.js +47 -0
  40. package/dist/hooks/useContacts.d.ts +52 -0
  41. package/dist/hooks/useContacts.js +259 -0
  42. package/dist/hooks/useEgressEstimates.d.ts +32 -0
  43. package/dist/hooks/useEgressEstimates.js +230 -0
  44. package/dist/hooks/useHiddenSwapFees.d.ts +22 -0
  45. package/dist/hooks/useHiddenSwapFees.js +81 -0
  46. package/dist/hooks/useOrderEstimates.d.ts +37 -0
  47. package/dist/hooks/useOrderEstimates.js +393 -0
  48. package/dist/hooks/useOutputAssetInfo.d.ts +12 -0
  49. package/dist/hooks/useOutputAssetInfo.js +38 -0
  50. package/dist/hooks/usePrices.d.ts +60 -0
  51. package/dist/hooks/usePrices.js +188 -0
  52. package/dist/hooks/useQuote.d.ts +73 -0
  53. package/dist/hooks/useQuote.js +507 -0
  54. package/dist/hooks/useResetSwapForm.d.ts +16 -0
  55. package/dist/hooks/useResetSwapForm.js +68 -0
  56. package/dist/hooks/useSlippageUsd.d.ts +11 -0
  57. package/dist/hooks/useSlippageUsd.js +19 -0
  58. package/dist/hooks/useSolanaAdapter.d.ts +15 -0
  59. package/dist/hooks/useSolanaAdapter.js +55 -0
  60. package/dist/hooks/useStatus.d.ts +25 -0
  61. package/dist/hooks/useStatus.js +60 -0
  62. package/dist/hooks/useSwap.d.ts +67 -0
  63. package/dist/hooks/useSwap.js +285 -0
  64. package/dist/hooks/useTransaction.d.ts +119 -0
  65. package/dist/hooks/useTransaction.js +353 -0
  66. package/dist/hooks/useTransactionAddress.d.ts +11 -0
  67. package/dist/hooks/useTransactionAddress.js +26 -0
  68. package/dist/hooks/useUsdValue.d.ts +7 -0
  69. package/dist/hooks/useUsdValue.js +19 -0
  70. package/dist/index.d.ts +54 -0
  71. package/dist/index.js +41 -0
  72. package/dist/stories/SilentSwapOverview.stories.d.ts +10 -0
  73. package/dist/stories/SilentSwapOverview.stories.js +364 -0
  74. package/dist/stories/useAuth.stories.d.ts +6 -0
  75. package/dist/stories/useAuth.stories.js +55 -0
  76. package/dist/stories/useSilentClient.stories.d.ts +9 -0
  77. package/dist/stories/useSilentClient.stories.js +39 -0
  78. package/dist/stories/useSilentOrders.stories.d.ts +1 -0
  79. package/dist/stories/useSilentOrders.stories.js +1 -0
  80. package/dist/stories/useSilentQuote.stories.d.ts +6 -0
  81. package/dist/stories/useSilentQuote.stories.js +267 -0
  82. package/dist/stories/useTransaction.stories.d.ts +6 -0
  83. package/dist/stories/useTransaction.stories.js +121 -0
  84. package/dist/utils/formatters.d.ts +33 -0
  85. package/dist/utils/formatters.js +82 -0
  86. package/package.json +67 -0
@@ -0,0 +1,381 @@
1
+ import { useCallback, useMemo, useState } from 'react';
2
+ import { hexToBase58, isSolanaAsset, parseEvmCaip19, S_CAIP19_USDC_AVALANCHE, NI_CHAIN_ID_AVALANCHE, getAssetByCaip19, } from '@silentswap/sdk';
3
+ import { getAddress } from 'viem';
4
+ import { BigNumber } from 'bignumber.js';
5
+ import { useQuoteFetching } from './useQuoteFetching.js';
6
+ import { useOrderSigning } from './useOrderSigning.js';
7
+ import { useBridgeExecution } from './useBridgeExecution.js';
8
+ import { useTransaction } from '../useTransaction.js';
9
+ import { useQuoteCalculation } from './useQuoteCalculation.js';
10
+ export function useSilentQuote({ client, address, evmAddress, solAddress, walletClient, connector, wallet, walletLoading = false, onStatus, solanaConnector, solanaConnection, solanaRpcUrl, getPrice, setDestinations, }) {
11
+ const [isLoading, setIsLoading] = useState(false);
12
+ const [currentStep, setCurrentStep] = useState('');
13
+ const [quote, setQuote] = useState(null);
14
+ const [error, setError] = useState(null);
15
+ const [orderId, setOrderId] = useState(null);
16
+ const [viewingAuth, setViewingAuth] = useState(null);
17
+ const [isSwapping, setIsSwapping] = useState(false);
18
+ // Normalize address - only normalize EVM addresses
19
+ // Note: For Solana swaps, we still need an EVM address for facilitator operations
20
+ // The address parameter should be EVM for facilitator operations, but can be Solana for quote requests
21
+ const normalizedAddress = useMemo(() => {
22
+ if (!address)
23
+ return null;
24
+ // Check if it's an EVM address (starts with 0x)
25
+ if (typeof address === 'string' && address.startsWith('0x')) {
26
+ try {
27
+ return getAddress(address);
28
+ }
29
+ catch {
30
+ // If getAddress fails, it might be invalid, but we'll keep it as-is for now
31
+ return address;
32
+ }
33
+ }
34
+ // For Solana addresses, return null (they're handled separately for transactions)
35
+ // But facilitator operations still require EVM address
36
+ return null;
37
+ }, [address]);
38
+ // Keep raw address for quote requests (can be EVM or Solana)
39
+ const rawAddress = useMemo(() => address || null, [address]);
40
+ // Get environment-based depositor address from client
41
+ const depositorAddress = useMemo(() => client.s0xDepositorAddress, [client]);
42
+ // Initialize specialized hooks
43
+ const { getQuote: getQuoteInternal } = useQuoteFetching(client, rawAddress, setIsLoading, setCurrentStep, setError, onStatus);
44
+ const { signAuthorizations, createOrder } = useOrderSigning(walletClient, connector, client, setCurrentStep, onStatus);
45
+ const { executeSolanaBridge, executeEvmBridge } = useBridgeExecution(walletClient, connector, solanaConnector, solanaConnection, solanaRpcUrl, setCurrentStep, depositorAddress, onStatus);
46
+ const { executeSwapTransaction, approveTokenSpending } = useTransaction({
47
+ address: normalizedAddress || '0x0000000000000000000000000000000000000000',
48
+ walletClient,
49
+ connector,
50
+ solanaConnector,
51
+ solanaConnection,
52
+ solanaRpcUrl,
53
+ setCurrentStep,
54
+ onStatus,
55
+ });
56
+ /**
57
+ * Get a quote for a swap
58
+ *
59
+ * Wrapper around the quote fetching hook that also stores the quote in state.
60
+ */
61
+ const getQuote = useCallback(async (request) => {
62
+ const quoteResponse = await getQuoteInternal(request);
63
+ if (quoteResponse) {
64
+ setQuote(quoteResponse);
65
+ }
66
+ return quoteResponse;
67
+ }, [getQuoteInternal]);
68
+ // Use the quote calculation hook
69
+ const { calculateQuote, loadingAmounts, depositAmountUsdc } = useQuoteCalculation({
70
+ address: rawAddress || '0x0000000000000000000000000000000000000000',
71
+ evmAddress,
72
+ wallet,
73
+ depositorAddress,
74
+ getQuote,
75
+ getPrice,
76
+ setDestinations,
77
+ });
78
+ /**
79
+ * Execute the complete swap flow
80
+ *
81
+ * Orchestrates the entire swap process:
82
+ * 1. Fetches quote and calculates provider/allowance/usdcAmount
83
+ * 2. Determines swap type (Solana, EVM bridge, or direct deposit)
84
+ * 3. Creates and signs the order
85
+ * 4. Executes bridge transactions if needed
86
+ * 5. Executes deposit transaction
87
+ * 6. Returns swap result with order ID and transaction hashes
88
+ */
89
+ const executeSwap = useCallback(async ({ sourceAsset, sourceAmount, destinations, splits, senderContactId, }) => {
90
+ if (!rawAddress) {
91
+ throw new Error('Address required for swap execution');
92
+ }
93
+ if (!walletClient) {
94
+ throw new Error('Wallet client required for swap execution');
95
+ }
96
+ // Check if wallet is being generated - wait a bit and check again
97
+ if (walletLoading) {
98
+ throw new Error('Wallet is being generated. Please wait for wallet generation to complete before executing swap.');
99
+ }
100
+ setIsLoading(true);
101
+ setError(null);
102
+ try {
103
+ setIsSwapping(true);
104
+ setCurrentStep('Fetching quote...');
105
+ onStatus?.('Fetching quote...');
106
+ const quoteResult = await calculateQuote(sourceAsset, sourceAmount, destinations, splits);
107
+ if (!quoteResult) {
108
+ // Ensure loadingAmounts is reset if handleGetQuote failed
109
+ throw new Error('Failed to get quote');
110
+ }
111
+ const { quote: quoteResponse, usdcAmount: effectiveUsdcAmount, facilitatorGroup: passedFacilitatorGroup, bridgeProvider: effectiveProvider, allowanceTarget: effectiveAllowanceTarget, } = quoteResult;
112
+ setCurrentStep('Executing swap...');
113
+ onStatus?.('Executing swap...');
114
+ // Convert source amount to units
115
+ const assetInfo = getAssetByCaip19(sourceAsset);
116
+ if (!assetInfo) {
117
+ throw new Error(`Asset not found: ${sourceAsset}`);
118
+ }
119
+ const sourceAmountBN = BigNumber(sourceAmount);
120
+ console.log('sourceAmountBN', sourceAmountBN);
121
+ const sourceAmountInUnits = sourceAmountBN.shiftedBy(assetInfo.decimals).toFixed(0);
122
+ console.log('sourceAmountInUnits', sourceAmountInUnits, assetInfo);
123
+ // Determine provider for bridge swap
124
+ const isDirectDeposit = sourceAsset === S_CAIP19_USDC_AVALANCHE;
125
+ let providerForSwap = undefined;
126
+ if (effectiveUsdcAmount && !isDirectDeposit) {
127
+ // When usdcAmount is provided and not direct deposit, we're doing a bridge swap and MUST have a provider
128
+ if (effectiveProvider === 'relay' || effectiveProvider === 'debridge') {
129
+ providerForSwap = effectiveProvider;
130
+ }
131
+ else {
132
+ throw new Error(`Provider is required for bridge swap. ` +
133
+ `usdcAmount is set but provider is '${effectiveProvider}'. ` +
134
+ `Please ensure a valid quote has been fetched.`);
135
+ }
136
+ }
137
+ const resolvedGroup = passedFacilitatorGroup;
138
+ if (!resolvedGroup) {
139
+ throw new Error('Facilitator group not available. Please ensure wallet is connected and authenticated.');
140
+ }
141
+ // Check swap type
142
+ const isSourceSolana = isSolanaAsset(sourceAsset);
143
+ const isDepositingDirectly = sourceAsset === S_CAIP19_USDC_AVALANCHE;
144
+ const isSourceEvm = !isSourceSolana && !isDepositingDirectly && sourceAsset.startsWith('eip155:');
145
+ // Generate viewing authorization
146
+ const viewer = await resolvedGroup.viewer();
147
+ const evmSigner = await viewer.evmSigner();
148
+ const viewingAuth = hexToBase58(evmSigner.address);
149
+ // CRITICAL: Use user's wallet address for deposit calldata (matches Svelte's s0x_signer)
150
+ // In Svelte: s0x_signer = await contact_from_id(UserState.senderAtNamespace('eip155')).address()
151
+ // This is the user's EVM wallet address, NOT the facilitator group's viewer EVM signer
152
+ // The signer address in quote request (already set above) and deposit calldata must match
153
+ if (!evmAddress) {
154
+ throw new Error('EVM address required for swap execution');
155
+ }
156
+ const evmSignerAddress = getAddress(evmAddress); // Use user's wallet address, not facilitator group's viewer EVM signer
157
+ // Handle Solana swaps
158
+ if (isSourceSolana) {
159
+ const result = await executeSolanaSwap(quoteResponse, sourceAsset, sourceAmountInUnits, effectiveUsdcAmount, senderContactId, solanaConnector, evmSignerAddress, // Use EVM signer address, not normalizedAddress
160
+ viewingAuth, createOrder, executeSolanaBridge, resolvedGroup);
161
+ setOrderId(result.orderId);
162
+ setViewingAuth(result.viewingAuth);
163
+ return result;
164
+ }
165
+ // Sign authorizations (for EVM swaps)
166
+ const signedAuths = await signAuthorizations(quoteResponse.authorizations, isDepositingDirectly);
167
+ // Create order with resolved facilitator group
168
+ // Pass the resolved group as override parameter (matches Svelte behavior where
169
+ // sender_resolve_group() is called right before swap)
170
+ const orderResponse = await createOrder(quoteResponse, signedAuths, {
171
+ sourceAsset: {
172
+ caip19: sourceAsset,
173
+ amount: `${BigInt(sourceAmountInUnits)}`,
174
+ },
175
+ sourceSender: {
176
+ contactId: senderContactId,
177
+ },
178
+ }, resolvedGroup);
179
+ // Handle ERC-20 approvals if needed
180
+ if (effectiveAllowanceTarget && !isDepositingDirectly && evmSignerAddress) {
181
+ const sourceAssetMatch = sourceAsset.match(/eip155:(\d+):erc20:(0x[a-fA-F0-9]+)/);
182
+ if (sourceAssetMatch) {
183
+ const sourceChainId = parseInt(sourceAssetMatch[1]);
184
+ const tokenAddress = sourceAssetMatch[2];
185
+ await approveTokenSpending(sourceChainId, tokenAddress, effectiveAllowanceTarget, sourceAmountInUnits, evmSignerAddress);
186
+ }
187
+ }
188
+ // Handle EVM bridge swaps
189
+ if (isSourceEvm) {
190
+ // Use the USDC amount from solveOptimalUsdcAmount (matches Svelte behavior)
191
+ // In Svelte: zg_amount_src_usdc is passed directly to bridge execution (line 1028)
192
+ const result = await executeEvmSwap(sourceAsset, sourceAmountInUnits, effectiveUsdcAmount, // Use amount from solveOptimalUsdcAmount (matches Svelte)
193
+ providerForSwap, orderResponse, evmSignerAddress, // Use EVM signer address, not normalizedAddress
194
+ viewingAuth, executeEvmBridge, senderContactId);
195
+ setOrderId(result.orderId);
196
+ setViewingAuth(result.viewingAuth);
197
+ return result;
198
+ }
199
+ // Execute direct deposit transaction (for direct USDC deposits)
200
+ const depositTx = await executeSwapTransaction(orderResponse);
201
+ const resultOrderId = orderResponse.response.orderId;
202
+ setOrderId(resultOrderId);
203
+ setViewingAuth(viewingAuth);
204
+ return {
205
+ orderId: resultOrderId,
206
+ viewingAuth,
207
+ depositTransaction: depositTx,
208
+ };
209
+ }
210
+ catch (err) {
211
+ const error = err instanceof Error ? err : new Error('Swap execution failed');
212
+ console.error('Swap execution failed:', {
213
+ sourceAsset,
214
+ sourceAmount,
215
+ destinationsCount: destinations.length,
216
+ error: error.message,
217
+ stack: error.stack,
218
+ });
219
+ setError(error);
220
+ // Ensure all loading states are reset on error
221
+ return null;
222
+ }
223
+ finally {
224
+ setIsSwapping(false);
225
+ setIsLoading(false);
226
+ setCurrentStep('');
227
+ }
228
+ }, [
229
+ rawAddress,
230
+ evmAddress,
231
+ walletClient,
232
+ walletLoading,
233
+ solanaConnector,
234
+ signAuthorizations,
235
+ createOrder,
236
+ approveTokenSpending,
237
+ executeSolanaBridge,
238
+ executeEvmBridge,
239
+ executeSwapTransaction,
240
+ calculateQuote,
241
+ onStatus,
242
+ ]);
243
+ /**
244
+ * Clear current quote
245
+ */
246
+ const clearQuote = useCallback(() => {
247
+ setQuote(null);
248
+ setError(null);
249
+ setOrderId(null);
250
+ setViewingAuth(null);
251
+ }, []);
252
+ return {
253
+ // State
254
+ isLoading,
255
+ isSwapping,
256
+ currentStep,
257
+ quote,
258
+ error,
259
+ orderId,
260
+ viewingAuth,
261
+ // Methods
262
+ executeSwap,
263
+ clearQuote,
264
+ depositAmountUsdc,
265
+ loadingAmounts,
266
+ };
267
+ }
268
+ /**
269
+ * Execute Solana swap flow
270
+ *
271
+ * Handles the complete flow for Solana-based swaps:
272
+ * 1. Validates Solana connection
273
+ * 2. Creates order (with empty authorizations for Solana)
274
+ * 3. Executes bridge transaction
275
+ * 4. Returns swap result
276
+ */
277
+ async function executeSolanaSwap(quoteResponse, sourceAsset, sourceAmount, usdcAmount, senderContactId, solanaConnector, evmSignerAddress, // EVM signer address (matches Svelte's s0x_signer)
278
+ viewingAuth, createOrder, executeSolanaBridge, facilitatorGroup) {
279
+ if (!solanaConnector) {
280
+ throw new Error('Solana connector required for Solana swaps');
281
+ }
282
+ if (!solanaConnector.publicKey) {
283
+ throw new Error('Solana wallet not connected');
284
+ }
285
+ // Get Solana sender address (base58)
286
+ const solanaSenderAddress = solanaConnector.publicKey.toString() || '';
287
+ if (!solanaSenderAddress) {
288
+ throw new Error('Failed to get Solana sender address');
289
+ }
290
+ // Create order with empty authorizations (Solana doesn't use EIP-3009)
291
+ // Pass resolved facilitator group (matches Svelte behavior)
292
+ const orderResponse = await createOrder(quoteResponse, quoteResponse.authorizations.map((auth) => ({
293
+ ...auth,
294
+ signature: '0x', // No EIP-3009 deposit for Solana
295
+ })), {
296
+ sourceAsset: {
297
+ caip19: sourceAsset,
298
+ amount: `${BigInt(sourceAmount)}`,
299
+ },
300
+ sourceSender: {
301
+ contactId: senderContactId,
302
+ },
303
+ }, facilitatorGroup);
304
+ // Get deposit parameters from order
305
+ const depositParams = orderResponse.transaction.metadata?.params;
306
+ if (!depositParams) {
307
+ throw new Error('Missing deposit parameters in order response');
308
+ }
309
+ // Execute bridge transaction
310
+ // Use EVM signer address for deposit calldata (matches Svelte's s0x_signer)
311
+ // Pass depositParams so executeSolanaBridge can encode the actual deposit calldata
312
+ // Use usdcAmount from solveOptimalUsdcAmount (matches Svelte: zg_amount_src_usdc is passed directly)
313
+ const bridgeResult = await executeSolanaBridge(sourceAsset, sourceAmount, usdcAmount, // Use amount from solveOptimalUsdcAmount (matches Svelte line 1028)
314
+ solanaSenderAddress, evmSignerAddress, // Use EVM signer address, not normalizedAddress
315
+ depositParams);
316
+ const resultOrderId = orderResponse.response.orderId;
317
+ // Note: setOrderId is not available in this function scope,
318
+ // but the orderId will be set by the calling executeSwap function
319
+ return {
320
+ orderId: resultOrderId,
321
+ viewingAuth,
322
+ depositTransaction: {
323
+ hash: bridgeResult.depositTxHash,
324
+ chainId: NI_CHAIN_ID_AVALANCHE,
325
+ status: 'confirmed',
326
+ confirmations: 1,
327
+ },
328
+ };
329
+ }
330
+ /**
331
+ * Execute EVM swap flow
332
+ *
333
+ * Handles the complete flow for EVM-based bridge swaps:
334
+ * 1. Parses source asset to get chain ID and token address
335
+ * 2. Gets deposit parameters from order
336
+ * 3. Executes bridge transaction
337
+ * 4. Returns swap result
338
+ */
339
+ async function executeEvmSwap(sourceAsset, sourceAmount, usdcAmount, provider, orderResponse, evmSignerAddress, // EVM signer address (matches Svelte's s0x_signer)
340
+ viewingAuth, executeEvmBridge, senderContactId) {
341
+ // Parse source asset to get chain ID and token address
342
+ const sourceEvmParsed = parseEvmCaip19(sourceAsset);
343
+ if (!sourceEvmParsed) {
344
+ throw new Error(`Failed to parse EVM source asset: ${sourceAsset}`);
345
+ }
346
+ const sourceChainId = sourceEvmParsed.chainId;
347
+ const sourceTokenAddress = sourceEvmParsed.tokenAddress || '0x0000000000000000000000000000000000000000';
348
+ // Get deposit parameters from order
349
+ const depositParams = orderResponse.transaction.metadata?.params;
350
+ if (!depositParams) {
351
+ throw new Error('Missing deposit parameters in order response');
352
+ }
353
+ // Extract sender address from senderContactId (matches Svelte's s0x_sender)
354
+ // Format: caip10:eip155:*:0x... or caip10:eip155:chainId:0x...
355
+ let evmSenderAddress = evmSignerAddress; // Default to signer address
356
+ const senderMatch = senderContactId.match(/caip10:eip155(?::\*|:\d+):(0x[a-fA-F0-9]+)/i);
357
+ if (senderMatch) {
358
+ evmSenderAddress = getAddress(senderMatch[1]);
359
+ }
360
+ // Execute bridge transaction
361
+ // Use EVM sender address for bridge quotes (matches Svelte's s0x_sender)
362
+ // Use EVM signer address for deposit calldata (matches Svelte's s0x_signer)
363
+ // Use usdcAmount from solveOptimalUsdcAmount (matches Svelte: zg_amount_src_usdc is passed directly at line 1028)
364
+ const bridgeResult = await executeEvmBridge(sourceChainId, sourceTokenAddress, sourceAmount, usdcAmount, // Use amount from solveOptimalUsdcAmount (matches Svelte)
365
+ depositParams, evmSignerAddress, // Use EVM signer address for deposit calldata
366
+ evmSenderAddress, // Use EVM sender address for bridge quotes
367
+ provider);
368
+ const resultOrderId = orderResponse.response.orderId;
369
+ // Note: setOrderId is not available in this function scope,
370
+ // but the orderId will be set by the calling executeSwap function
371
+ return {
372
+ orderId: resultOrderId,
373
+ viewingAuth,
374
+ depositTransaction: {
375
+ hash: bridgeResult.depositTxHash,
376
+ chainId: NI_CHAIN_ID_AVALANCHE,
377
+ status: 'confirmed',
378
+ confirmations: 1,
379
+ },
380
+ };
381
+ }
@@ -0,0 +1,76 @@
1
+ import { type HdFacilitatorGroup, type AuthResponse, SilentSwapClient } from '@silentswap/sdk';
2
+ import type { WalletClient } from 'viem';
3
+ import type { Connector } from 'wagmi';
4
+ export interface FacilitatorAccount {
5
+ group: () => Promise<HdFacilitatorGroup>;
6
+ nonce: number;
7
+ }
8
+ export interface SilentSwapWallet {
9
+ entropy: Uint8Array;
10
+ accounts: FacilitatorAccount[];
11
+ }
12
+ export interface useWalletOptions {
13
+ /** SilentSwap client */
14
+ client: SilentSwapClient;
15
+ /** The user's EVM address */
16
+ address: `0x${string}`;
17
+ /** Authentication response from SIWE */
18
+ auth?: AuthResponse;
19
+ /** Wallet client for signing operations */
20
+ walletClient?: WalletClient;
21
+ /** Wagmi connector */
22
+ connector?: Connector;
23
+ /** Whether to generate all deposit accounts or just the latest */
24
+ allDeposits?: boolean;
25
+ }
26
+ export interface useWalletReturn {
27
+ wallet: SilentSwapWallet | null;
28
+ isLoading: boolean;
29
+ error: Error | null;
30
+ generateWallet: () => Promise<void>;
31
+ getCachedWallet: () => SilentSwapWallet | null;
32
+ clearWallet: () => void;
33
+ refreshWallet: () => Promise<void>;
34
+ }
35
+ /**
36
+ * React hook for managing SilentSwap facilitator wallets and entropy generation
37
+ *
38
+ * This hook handles:
39
+ * - Generating and caching wallet entropy from SIWE authentication
40
+ * - Creating facilitator groups for different deposit nonces
41
+ * - Session storage persistence for wallet data
42
+ * - Automatic wallet refresh when deposits change
43
+ *
44
+ * @param options - Configuration options for wallet generation
45
+ * @returns Object with wallet state and management methods
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * import { useWallet, useAuth } from '@silentswap/react';
50
+ * import { useWalletClient } from 'wagmi';
51
+ *
52
+ * function MyComponent() {
53
+ * const { data: walletClient } = useWalletClient();
54
+ * const { auth } = useAuth();
55
+ * const { wallet, generateWallet, isLoading } = useWallet({
56
+ * address: '0x123...',
57
+ * auth,
58
+ * walletClient: walletClient as any,
59
+ * });
60
+ *
61
+ * useEffect(() => {
62
+ * if (auth && walletClient && !wallet) {
63
+ * generateWallet();
64
+ * }
65
+ * }, [auth, walletClient, wallet, generateWallet]);
66
+ *
67
+ * return (
68
+ * <div>
69
+ * {isLoading && <div>Generating wallet...</div>}
70
+ * {wallet && <div>Wallet ready with {wallet.accounts.length} accounts</div>}
71
+ * </div>
72
+ * );
73
+ * }
74
+ * ```
75
+ */
76
+ export declare function useWallet({ client, address, auth, walletClient, connector, allDeposits, }: useWalletOptions): useWalletReturn;
@@ -0,0 +1,203 @@
1
+ import { useCallback, useState, useEffect } from 'react';
2
+ import { createHdFacilitatorGroupFromEntropy, createEip712DocForWalletGeneration, queryDepositCount, loadWalletData, saveWalletData, clearWalletData, XT_TTL_SESSION_CACHE, ensureChain, base93ToBytes, bytesToBase93, } from '@silentswap/sdk';
3
+ import { getAddress, hexToBytes } from 'viem';
4
+ /**
5
+ * React hook for managing SilentSwap facilitator wallets and entropy generation
6
+ *
7
+ * This hook handles:
8
+ * - Generating and caching wallet entropy from SIWE authentication
9
+ * - Creating facilitator groups for different deposit nonces
10
+ * - Session storage persistence for wallet data
11
+ * - Automatic wallet refresh when deposits change
12
+ *
13
+ * @param options - Configuration options for wallet generation
14
+ * @returns Object with wallet state and management methods
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { useWallet, useAuth } from '@silentswap/react';
19
+ * import { useWalletClient } from 'wagmi';
20
+ *
21
+ * function MyComponent() {
22
+ * const { data: walletClient } = useWalletClient();
23
+ * const { auth } = useAuth();
24
+ * const { wallet, generateWallet, isLoading } = useWallet({
25
+ * address: '0x123...',
26
+ * auth,
27
+ * walletClient: walletClient as any,
28
+ * });
29
+ *
30
+ * useEffect(() => {
31
+ * if (auth && walletClient && !wallet) {
32
+ * generateWallet();
33
+ * }
34
+ * }, [auth, walletClient, wallet, generateWallet]);
35
+ *
36
+ * return (
37
+ * <div>
38
+ * {isLoading && <div>Generating wallet...</div>}
39
+ * {wallet && <div>Wallet ready with {wallet.accounts.length} accounts</div>}
40
+ * </div>
41
+ * );
42
+ * }
43
+ * ```
44
+ */
45
+ export function useWallet({ client, address, auth, walletClient, connector, allDeposits = false, }) {
46
+ const [wallet, setWallet] = useState(null);
47
+ const [isLoading, setIsLoading] = useState(false);
48
+ const [error, setError] = useState(null);
49
+ // Normalize address
50
+ const normalizedAddress = address ? getAddress(address) : null;
51
+ /**
52
+ * Get cached wallet from sessionStorage
53
+ */
54
+ const getCachedWallet = useCallback(() => {
55
+ if (!normalizedAddress)
56
+ return null;
57
+ const cached = loadWalletData(normalizedAddress);
58
+ if (!cached)
59
+ return null;
60
+ try {
61
+ // Decode entropy
62
+ const entropy = base93ToBytes(cached.entropy);
63
+ // Create accounts
64
+ const accounts = cached.accounts.map(({ nonce }) => ({
65
+ group: () => createHdFacilitatorGroupFromEntropy(entropy, nonce),
66
+ nonce,
67
+ }));
68
+ return {
69
+ entropy,
70
+ accounts,
71
+ };
72
+ }
73
+ catch (err) {
74
+ console.warn('Failed to load cached wallet:', err);
75
+ clearWalletData(normalizedAddress);
76
+ return null;
77
+ }
78
+ }, [normalizedAddress]);
79
+ /**
80
+ * Generate wallet entropy from authentication
81
+ */
82
+ const generateEntropy = useCallback(async () => {
83
+ if (!auth) {
84
+ throw new Error('Authentication required for wallet generation');
85
+ }
86
+ if (!walletClient) {
87
+ throw new Error('Wallet client required for entropy generation');
88
+ }
89
+ if (!connector) {
90
+ throw new Error('Connector required for chain switching');
91
+ }
92
+ // Ensure we're on Ethereum mainnet for entropy generation (asked core devs about this in tg)
93
+ await ensureChain(1, walletClient, connector);
94
+ // Create EIP-712 document for wallet generation
95
+ const eip712Doc = createEip712DocForWalletGeneration(`eip155:43114:${address}`, // Gateway contract scope (Avalanche)
96
+ auth.secretToken);
97
+ // Fix chainId to be bigint
98
+ if (eip712Doc.domain && typeof eip712Doc.domain.chainId === 'number') {
99
+ eip712Doc.domain.chainId = BigInt(eip712Doc.domain.chainId);
100
+ }
101
+ // Sign the document to generate entropy
102
+ const signature = await walletClient.signTypedData({
103
+ ...eip712Doc,
104
+ account: walletClient.account,
105
+ });
106
+ return hexToBytes(signature);
107
+ }, [auth, walletClient, connector, address]);
108
+ /**
109
+ * Generate new wallet with facilitator accounts
110
+ */
111
+ const generateWallet = useCallback(async () => {
112
+ if (!normalizedAddress) {
113
+ throw new Error('Address required for wallet generation');
114
+ }
115
+ setIsLoading(true);
116
+ setError(null);
117
+ try {
118
+ // Try to get cached wallet first
119
+ const cachedWallet = getCachedWallet();
120
+ if (cachedWallet) {
121
+ setWallet(cachedWallet);
122
+ return;
123
+ }
124
+ // Query the actual deposit count from the blockchain
125
+ const depositCount = await queryDepositCount(normalizedAddress, client.s0xGatewayAddress);
126
+ console.log('[useWallet] depositCount', depositCount);
127
+ // Generate new entropy
128
+ const entropy = await generateEntropy();
129
+ // Create accounts based on deposit count
130
+ const accounts = [];
131
+ // Match the Svelte implementation: if not allDeposits, start from depositCount (not depositCount-1)
132
+ // and use <= comparison to include the last account
133
+ const startNonce = allDeposits ? 0 : Number(depositCount);
134
+ const endNonce = Number(depositCount);
135
+ for (let nonce = startNonce; nonce <= endNonce; nonce++) {
136
+ accounts.push({
137
+ group: () => createHdFacilitatorGroupFromEntropy(entropy, nonce),
138
+ nonce,
139
+ });
140
+ }
141
+ // Ensure we always have at least one account (for new users with depositCount = 0)
142
+ // This guarantees wallet.accounts[0] exists
143
+ if (accounts.length === 0) {
144
+ accounts.push({
145
+ group: () => createHdFacilitatorGroupFromEntropy(entropy, 0),
146
+ nonce: 0,
147
+ });
148
+ }
149
+ const newWallet = {
150
+ entropy,
151
+ accounts,
152
+ };
153
+ // Cache wallet data
154
+ saveWalletData(normalizedAddress, {
155
+ entropy: bytesToBase93(entropy),
156
+ accounts: accounts.map(({ nonce }) => ({ nonce })),
157
+ }, XT_TTL_SESSION_CACHE);
158
+ setWallet(newWallet);
159
+ }
160
+ catch (err) {
161
+ const error = err instanceof Error ? err : new Error('Failed to generate wallet');
162
+ setError(error);
163
+ throw error;
164
+ }
165
+ finally {
166
+ setIsLoading(false);
167
+ }
168
+ }, [normalizedAddress, getCachedWallet, queryDepositCount, generateEntropy, allDeposits, connector, client.s0xGatewayAddress]);
169
+ /**
170
+ * Clear cached wallet
171
+ */
172
+ const clearWallet = useCallback(() => {
173
+ if (!normalizedAddress)
174
+ return;
175
+ clearWalletData(normalizedAddress);
176
+ setWallet(null);
177
+ }, [normalizedAddress]);
178
+ /**
179
+ * Refresh wallet (regenerate if needed)
180
+ */
181
+ const refreshWallet = useCallback(async () => {
182
+ clearWallet();
183
+ await generateWallet();
184
+ }, [clearWallet, generateWallet]);
185
+ // Auto-load cached wallet on mount
186
+ useEffect(() => {
187
+ if (normalizedAddress && !wallet && !isLoading) {
188
+ const cached = getCachedWallet();
189
+ if (cached) {
190
+ setWallet(cached);
191
+ }
192
+ }
193
+ }, [normalizedAddress, wallet, isLoading, getCachedWallet]);
194
+ return {
195
+ wallet,
196
+ isLoading,
197
+ error,
198
+ generateWallet,
199
+ getCachedWallet,
200
+ clearWallet,
201
+ refreshWallet,
202
+ };
203
+ }
@@ -0,0 +1,8 @@
1
+ import type { AssetInfo } from '@silentswap/sdk';
2
+ /**
3
+ * Hook to fetch and manage asset price from the global context.
4
+ * Handles price fetching with optional override.
5
+ * This hook now uses the PricesContext to prevent duplicate API calls.
6
+ * Automatically updates when prices are refreshed (after 10 seconds).
7
+ */
8
+ export declare function useAssetPrice(asset: AssetInfo | null, priceOverride?: number): number | undefined;