@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,55 @@
1
+ import { useMemo } from 'react';
2
+ import { useWallet as useSolanaWallet, useConnection } from '@solana/wallet-adapter-react';
3
+ import { createSolanaRpc } from '@solana/rpc';
4
+ /**
5
+ * Reusable hook for Solana wallet adapter setup
6
+ * Returns the Solana connector and connection adapter for use with SilentSwap hooks
7
+ */
8
+ export function useSolanaAdapter() {
9
+ const solanaWallet = useSolanaWallet();
10
+ const solanaConnection = useConnection();
11
+ // Prepare Solana connector if wallet is connected
12
+ const solanaConnector = useMemo(() => {
13
+ if (!solanaWallet.publicKey || !solanaWallet.signTransaction || !solanaWallet.sendTransaction) {
14
+ return undefined;
15
+ }
16
+ return {
17
+ signTransaction: solanaWallet.signTransaction.bind(solanaWallet),
18
+ sendTransaction: solanaWallet.sendTransaction.bind(solanaWallet),
19
+ publicKey: solanaWallet.publicKey,
20
+ };
21
+ }, [solanaWallet.publicKey, solanaWallet.signTransaction, solanaWallet.sendTransaction]);
22
+ // Prepare Solana connection
23
+ const solanaConnectionAdapter = useMemo(() => {
24
+ if (!solanaConnection.connection) {
25
+ return undefined;
26
+ }
27
+ const rpc = createSolanaRpc(solanaConnection.connection.rpcEndpoint);
28
+ return {
29
+ rpc,
30
+ // Include the actual connection object for wallet adapter compatibility
31
+ connection: solanaConnection.connection,
32
+ getLatestBlockhash: async (commitment) => {
33
+ const result = await rpc.getLatestBlockhash({ commitment: commitment }).send();
34
+ return {
35
+ blockhash: result.value.blockhash,
36
+ lastValidBlockHeight: BigInt(result.value.lastValidBlockHeight),
37
+ };
38
+ },
39
+ confirmTransaction: async (config, commitment) => {
40
+ // Note: @solana/rpc doesn't have a direct confirmTransaction method
41
+ // We'll use getSignatureStatuses instead
42
+ const result = await rpc.getSignatureStatuses([config.signature], { searchTransactionHistory: true }).send();
43
+ return {
44
+ value: {
45
+ err: result.value?.[0]?.err || null,
46
+ },
47
+ };
48
+ },
49
+ };
50
+ }, [solanaConnection.connection]);
51
+ return {
52
+ solanaConnector,
53
+ solanaConnectionAdapter,
54
+ };
55
+ }
@@ -0,0 +1,25 @@
1
+ export interface StatusResponse {
2
+ serviceFeeRate?: number;
3
+ overheadUsd?: string;
4
+ minimumDepositUusdc?: string;
5
+ maximumDepositUusdc?: string;
6
+ platformHealth?: 'UNKNOWN' | 'ON' | 'OFF' | 'PAUSED' | 'ONLINE' | 'DEPOSITS_PAUSED' | 'MAINTENANCE';
7
+ outputLimit?: number;
8
+ volume?: number;
9
+ liquidity?: number;
10
+ identity?: string;
11
+ }
12
+ /**
13
+ * Hook to fetch the current platform status, fee rates, and limits
14
+ */
15
+ export declare function useStatus(): {
16
+ status: StatusResponse;
17
+ isLoading: boolean;
18
+ error: Error | null;
19
+ serviceFeeRate: number;
20
+ overheadUsd: number;
21
+ minimumDepositUusdc: string | undefined;
22
+ maximumDepositUusdc: string | undefined;
23
+ platformHealth: "UNKNOWN" | "ON" | "OFF" | "PAUSED" | "ONLINE" | "DEPOSITS_PAUSED" | "MAINTENANCE" | undefined;
24
+ outputLimit: number | undefined;
25
+ };
@@ -0,0 +1,60 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useSilentSwap } from '../contexts/SilentSwapContext.js';
3
+ /**
4
+ * Hook to fetch the current platform status, fee rates, and limits
5
+ */
6
+ export function useStatus() {
7
+ const { config } = useSilentSwap();
8
+ const baseUrl = config.baseUrl;
9
+ const [status, setStatus] = useState({});
10
+ const [isLoading, setIsLoading] = useState(true);
11
+ const [error, setError] = useState(null);
12
+ useEffect(() => {
13
+ let isMounted = true;
14
+ const fetchStatus = async () => {
15
+ try {
16
+ setIsLoading(true);
17
+ const response = await fetch(`${baseUrl}/status`);
18
+ if (!response.ok) {
19
+ throw new Error(`Failed to fetch status: ${response.statusText}`);
20
+ }
21
+ const data = await response.json();
22
+ if (isMounted) {
23
+ setStatus(data);
24
+ setError(null);
25
+ }
26
+ }
27
+ catch (err) {
28
+ if (isMounted) {
29
+ setError(err instanceof Error ? err : new Error('Failed to fetch status'));
30
+ // Use defaults on error
31
+ setStatus({});
32
+ }
33
+ }
34
+ finally {
35
+ if (isMounted) {
36
+ setIsLoading(false);
37
+ }
38
+ }
39
+ };
40
+ fetchStatus();
41
+ return () => {
42
+ isMounted = false;
43
+ };
44
+ }, [baseUrl]);
45
+ // Convert overheadUsd from string to number
46
+ const overheadUsd = status.overheadUsd ? parseFloat(status.overheadUsd) : 0;
47
+ // serviceFeeRate is a Decimal (0.01 = 1%), convert to number
48
+ const serviceFeeRate = status.serviceFeeRate ?? 0.01; // Default 1%
49
+ return {
50
+ status,
51
+ isLoading,
52
+ error,
53
+ serviceFeeRate,
54
+ overheadUsd,
55
+ minimumDepositUusdc: status.minimumDepositUusdc,
56
+ maximumDepositUusdc: status.maximumDepositUusdc,
57
+ platformHealth: status.platformHealth,
58
+ outputLimit: status.outputLimit,
59
+ };
60
+ }
@@ -0,0 +1,67 @@
1
+ import { type AssetInfo } from '@silentswap/sdk';
2
+ export declare const X_RANGE_SLIDER_MIN_GAP = 0.01;
3
+ export declare const OUTPUT_LIMIT = 5;
4
+ export declare const DEFAULT_SOURCE_ASSET = "eip155:1/slip44:60";
5
+ export declare const DEFAULT_DEST_ASSET = "eip155:1/erc20:0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599";
6
+ export declare const DEFAULT_OUTPUT_ASSET = "eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
7
+ export declare const getSourceAssetCaip19: (tokenIn: AssetInfo | null) => string;
8
+ export interface Destination {
9
+ asset: string;
10
+ contact: string;
11
+ amount: string;
12
+ priceUsd?: number;
13
+ }
14
+ export interface SwapState {
15
+ tokenIn: AssetInfo | null;
16
+ inputAmount: string;
17
+ isSwapLoading: boolean;
18
+ isMultiSwapLoading: boolean;
19
+ srcChain: number;
20
+ dstChain: number;
21
+ slippage: number;
22
+ isAutoSlippage: boolean;
23
+ latestSuggestedAutoSlippageValue: number;
24
+ recipientAddress: string;
25
+ privacyEnabled: boolean;
26
+ usdInputMode: boolean;
27
+ destinations: Destination[];
28
+ splits: number[];
29
+ isDraggingSlider: boolean;
30
+ setTokenIn: (token: AssetInfo) => void;
31
+ setSrcChain: (chainId: number) => void;
32
+ setDstChain: (chainId: number) => void;
33
+ setInputAmount: (amount: string) => void;
34
+ setRecipientAddress: (recipientAddress: string) => void;
35
+ setSwapLoading: (value: boolean) => void;
36
+ swapTokens: (amount: string) => void;
37
+ setSlippage: (amount: number) => void;
38
+ setIsAutoSlippage: (value: boolean) => void;
39
+ setLatestSuggestedAutoSlippageValue: (value: number) => void;
40
+ setPrivacyEnabled: (enabled: boolean) => void;
41
+ setUsdInputMode: (enabled: boolean) => void;
42
+ toggleUsdInputMode: () => void;
43
+ setDestinations: (updater: Destination[] | ((prev: Destination[]) => Destination[])) => void;
44
+ setSplits: (updater: number[] | ((prev: number[]) => number[])) => void;
45
+ handleAddOutput: (caip19Asset?: string, defaultAddress?: string) => void;
46
+ handleDeleteOutput: (index: number) => void;
47
+ propagateRange: (i_split: number, x_value: number, b_hi: boolean, b_whole: boolean) => boolean;
48
+ updateDestinationAsset: (index: number, asset: string) => void;
49
+ updateDestinationContact: (index: number, contact: string) => void;
50
+ updateDestinationAmount: (index: number, amount: string) => void;
51
+ setIsDraggingSlider: (isDragging: boolean) => void;
52
+ getCanAddOutput: () => boolean;
53
+ getHasMultipleOutputs: () => boolean;
54
+ }
55
+ export declare const useSwap: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<SwapState>, "setState" | "persist"> & {
56
+ setState(partial: SwapState | Partial<SwapState> | ((state: SwapState) => SwapState | Partial<SwapState>), replace?: false | undefined): unknown;
57
+ setState(state: SwapState | ((state: SwapState) => SwapState), replace: true): unknown;
58
+ persist: {
59
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<SwapState, unknown, unknown>>) => void;
60
+ clearStorage: () => void;
61
+ rehydrate: () => Promise<void> | void;
62
+ hasHydrated: () => boolean;
63
+ onHydrate: (fn: (state: SwapState) => void) => () => void;
64
+ onFinishHydration: (fn: (state: SwapState) => void) => () => void;
65
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<SwapState, unknown, unknown>>;
66
+ };
67
+ }>;
@@ -0,0 +1,285 @@
1
+ import { create } from 'zustand';
2
+ import { persist, createJSONStorage } from 'zustand/middleware';
3
+ import { getAssetByCaip19 } from '@silentswap/sdk';
4
+ const DEFAULT_SLIPPAGE = 1;
5
+ // Constants matching Svelte app
6
+ export const X_RANGE_SLIDER_MIN_GAP = 0.01;
7
+ export const OUTPUT_LIMIT = 5;
8
+ // Default assets
9
+ export const DEFAULT_SOURCE_ASSET = 'eip155:1/slip44:60'; // ETH on Ethereum
10
+ export const DEFAULT_DEST_ASSET = 'eip155:1/erc20:0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'; // WBTC on Ethereum
11
+ export const DEFAULT_OUTPUT_ASSET = 'eip155:8453/erc20:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base
12
+ // Helper to get source asset CAIP19 from tokenIn
13
+ export const getSourceAssetCaip19 = (tokenIn) => {
14
+ return tokenIn?.caip19 || DEFAULT_SOURCE_ASSET;
15
+ };
16
+ export const useSwap = create()(persist((set, get) => ({
17
+ // Regular swap state
18
+ isAutoSlippage: true,
19
+ tokenIn: getAssetByCaip19(DEFAULT_SOURCE_ASSET) ?? null,
20
+ srcChain: 1,
21
+ dstChain: 1,
22
+ inputAmount: '',
23
+ isSwapLoading: false,
24
+ isMultiSwapLoading: false,
25
+ latestSuggestedAutoSlippageValue: 0,
26
+ slippage: DEFAULT_SLIPPAGE,
27
+ recipientAddress: '',
28
+ // Privacy/UI state
29
+ privacyEnabled: true,
30
+ usdInputMode: false,
31
+ // Hidden swap state (uses tokenIn and inputAmount)
32
+ // For regular swaps, destinations[0] represents the output token and recipient
33
+ destinations: [{ asset: DEFAULT_DEST_ASSET, contact: '', amount: '' }],
34
+ splits: [1],
35
+ isDraggingSlider: false,
36
+ // Regular swap setters
37
+ setTokenIn: (token) => {
38
+ set({ tokenIn: token });
39
+ },
40
+ setSrcChain: (chainId) => set({ srcChain: chainId }),
41
+ setDstChain: (chainId) => set({ dstChain: chainId }),
42
+ setInputAmount: (amount) => set({ inputAmount: amount }),
43
+ setRecipientAddress: (recipientAddress) => set({ recipientAddress }),
44
+ setSwapLoading: (value) => set({ isSwapLoading: value }),
45
+ swapTokens: (amount) => set((state) => {
46
+ if (!state.tokenIn || state.destinations.length === 0 || !state.destinations[0]?.asset) {
47
+ return state;
48
+ }
49
+ // Remove any commas or special characters and ensure the string is valid
50
+ const sanitizedAmount = amount.replace(/[',]/g, '');
51
+ // Check if the sanitized amount is a valid number
52
+ const number = !isNaN(parseFloat(sanitizedAmount)) ? parseFloat(sanitizedAmount) : null;
53
+ // Get current destination asset
54
+ const currentDestAsset = state.destinations[0]?.asset;
55
+ const currentDestContact = state.destinations[0]?.contact || '';
56
+ // Swap tokenIn with destination asset
57
+ const newTokenIn = getAssetByCaip19(currentDestAsset);
58
+ if (!newTokenIn)
59
+ return state;
60
+ // Only add inputAmount if the number is valid
61
+ return {
62
+ ...state,
63
+ ...(number !== null && { inputAmount: number.toString() }), // Only include inputAmount if number is valid
64
+ tokenIn: newTokenIn,
65
+ destinations: [{ asset: state.tokenIn.caip19, contact: currentDestContact, amount: '' }],
66
+ recipientAddress: '',
67
+ };
68
+ }),
69
+ setSlippage: (value) => {
70
+ return set({ slippage: value, isAutoSlippage: false });
71
+ },
72
+ setIsAutoSlippage: (value) => {
73
+ return set({ isAutoSlippage: value });
74
+ },
75
+ setLatestSuggestedAutoSlippageValue: (value) => {
76
+ return set({ latestSuggestedAutoSlippageValue: value });
77
+ },
78
+ setPrivacyEnabled: (enabled) => {
79
+ return set({ privacyEnabled: enabled });
80
+ },
81
+ setUsdInputMode: (enabled) => {
82
+ return set({ usdInputMode: enabled });
83
+ },
84
+ toggleUsdInputMode: () => {
85
+ return set((state) => ({ usdInputMode: !state.usdInputMode }));
86
+ },
87
+ // Hidden swap setters
88
+ setDestinations: (updater) => {
89
+ const current = get().destinations;
90
+ const next = typeof updater === 'function' ? updater(current) : updater;
91
+ set({ destinations: next });
92
+ },
93
+ setSplits: (updater) => {
94
+ const current = get().splits;
95
+ const next = typeof updater === 'function' ? updater(current) : updater;
96
+ set({ splits: next });
97
+ },
98
+ // Hidden swap actions
99
+ handleAddOutput: (_, defaultAddress) => {
100
+ const state = get();
101
+ const newAsset = DEFAULT_OUTPUT_ASSET;
102
+ const numOutputs = state.splits.length + 1;
103
+ const newSplits = Array.from({ length: numOutputs }, (_, i) => (i + 1) / numOutputs);
104
+ const newDestination = {
105
+ asset: newAsset,
106
+ contact: defaultAddress || '',
107
+ amount: '',
108
+ };
109
+ set((prev) => ({
110
+ destinations: [...prev.destinations, newDestination],
111
+ splits: newSplits,
112
+ }));
113
+ },
114
+ handleDeleteOutput: (index) => {
115
+ const state = get();
116
+ // Only allow deletion if there are multiple outputs
117
+ if (state.splits.length <= 1)
118
+ return;
119
+ set((prev) => {
120
+ const newDestinations = [...prev.destinations];
121
+ const newSplits = [...prev.splits];
122
+ // Update primary destination if deleting index 0
123
+ if (index === 0) {
124
+ newDestinations[0] = {
125
+ contact: newDestinations[0]?.contact ?? '',
126
+ asset: newDestinations[0]?.asset ?? '',
127
+ amount: newDestinations[0]?.amount ?? '',
128
+ priceUsd: newDestinations[0]?.priceUsd,
129
+ };
130
+ }
131
+ // If deleting the last split, remove the split at index-1
132
+ if (index === newSplits.length - 1) {
133
+ newSplits.splice(index - 1, 1);
134
+ }
135
+ // Otherwise, remove the split at index
136
+ else {
137
+ newSplits.splice(index, 1);
138
+ }
139
+ // Remove destination at index if destinations length > splits length
140
+ if (newDestinations.length > newSplits.length) {
141
+ newDestinations.splice(index, 1);
142
+ }
143
+ return {
144
+ destinations: newDestinations,
145
+ splits: newSplits,
146
+ };
147
+ });
148
+ },
149
+ propagateRange: (i_split, x_value, b_hi, b_whole) => {
150
+ const state = get();
151
+ const newSplits = [...state.splits];
152
+ if (b_hi) {
153
+ const x_max_safe = 1 - X_RANGE_SLIDER_MIN_GAP * (state.splits.length - i_split - 1);
154
+ if (x_value > x_max_safe)
155
+ return true;
156
+ const x_scale = (1 - x_value) / (1 - state.splits[i_split]);
157
+ for (let i_each = state.splits.length - 1; i_each > i_split; i_each--) {
158
+ const x_new = 1 - x_scale * (1 - state.splits[i_each]);
159
+ if ((state.splits[i_each + 1] ?? 1) - x_new < X_RANGE_SLIDER_MIN_GAP) {
160
+ newSplits[i_each] = (state.splits[i_each + 1] ?? 1) - X_RANGE_SLIDER_MIN_GAP;
161
+ }
162
+ else {
163
+ newSplits[i_each] = x_new;
164
+ }
165
+ }
166
+ }
167
+ else {
168
+ if (x_value < X_RANGE_SLIDER_MIN_GAP * i_split)
169
+ return true;
170
+ const x_scale = x_value / (state.splits[i_split - 1] ?? 0);
171
+ for (let i_each = 0; i_each < i_split - 1; i_each++) {
172
+ const x_new = state.splits[i_each] * x_scale;
173
+ if (x_new - (i_each < 1 ? 0 : state.splits[i_each - 1]) < X_RANGE_SLIDER_MIN_GAP) {
174
+ newSplits[i_each] = X_RANGE_SLIDER_MIN_GAP;
175
+ }
176
+ else {
177
+ newSplits[i_each] = x_new;
178
+ }
179
+ }
180
+ }
181
+ if (b_whole) {
182
+ for (let i_each = 0; i_each < state.splits.length - 1; i_each++) {
183
+ newSplits[i_each] = Math.round(newSplits[i_each] * 100) / 100;
184
+ }
185
+ }
186
+ if (b_hi) {
187
+ newSplits[i_split] = x_value;
188
+ }
189
+ else if (i_split > 0) {
190
+ newSplits[i_split - 1] = x_value;
191
+ }
192
+ newSplits[newSplits.length - 1] = 1.0;
193
+ set({ splits: newSplits });
194
+ return false;
195
+ },
196
+ updateDestinationAsset: (index, asset) => {
197
+ set((state) => {
198
+ return {
199
+ destinations: state.destinations.map((dest, idx) => idx === index
200
+ ? {
201
+ asset: String(asset),
202
+ contact: String(dest.contact),
203
+ amount: '',
204
+ priceUsd: dest.priceUsd,
205
+ }
206
+ : {
207
+ asset: String(dest.asset),
208
+ contact: String(dest.contact),
209
+ amount: String(dest.amount),
210
+ priceUsd: dest.priceUsd,
211
+ }),
212
+ };
213
+ });
214
+ },
215
+ updateDestinationContact: (index, contact) => {
216
+ set((state) => ({
217
+ destinations: state.destinations.map((dest, idx) => idx === index
218
+ ? {
219
+ asset: String(dest.asset),
220
+ contact: String(contact),
221
+ amount: String(dest.amount),
222
+ priceUsd: dest.priceUsd,
223
+ }
224
+ : {
225
+ asset: String(dest.asset),
226
+ contact: String(dest.contact),
227
+ amount: String(dest.amount),
228
+ priceUsd: dest.priceUsd,
229
+ }),
230
+ }));
231
+ },
232
+ updateDestinationAmount: (index, amount) => {
233
+ set((state) => ({
234
+ destinations: state.destinations.map((dest, idx) => idx === index
235
+ ? {
236
+ asset: String(dest.asset),
237
+ contact: String(dest.contact),
238
+ amount: String(amount),
239
+ priceUsd: dest.priceUsd,
240
+ }
241
+ : {
242
+ asset: String(dest.asset),
243
+ contact: String(dest.contact),
244
+ amount: String(dest.amount),
245
+ priceUsd: dest.priceUsd,
246
+ }),
247
+ }));
248
+ },
249
+ setIsDraggingSlider: (isDragging) => set({ isDraggingSlider: isDragging }),
250
+ // Computed functions
251
+ getCanAddOutput: () => {
252
+ return get().splits.length < OUTPUT_LIMIT;
253
+ },
254
+ getHasMultipleOutputs: () => {
255
+ return get().splits.length > 1;
256
+ },
257
+ }), {
258
+ name: 'swap-store',
259
+ storage: createJSONStorage(() => localStorage),
260
+ // Persist privacy mode, tokenIn, and inputAmount
261
+ partialize: (state) => ({
262
+ privacyEnabled: state.privacyEnabled,
263
+ tokenInCaip19: state.tokenIn ? state.tokenIn.caip19 : null,
264
+ inputAmount: state.inputAmount,
265
+ usdInputMode: state.usdInputMode,
266
+ }),
267
+ merge: (persistedState, currentState) => {
268
+ // Helper to restore AssetInfo from CAIP19
269
+ const restoreAsset = (caip19) => {
270
+ if (!caip19)
271
+ return null;
272
+ return getAssetByCaip19(caip19) || null;
273
+ };
274
+ return {
275
+ ...currentState,
276
+ // Restore persisted fields
277
+ usdInputMode: persistedState?.usdInputMode ?? currentState.usdInputMode,
278
+ privacyEnabled: persistedState?.privacyEnabled ?? currentState.privacyEnabled,
279
+ tokenIn: persistedState?.tokenInCaip19
280
+ ? restoreAsset(persistedState.tokenInCaip19) || currentState.tokenIn
281
+ : currentState.tokenIn,
282
+ inputAmount: persistedState?.inputAmount ?? currentState.inputAmount,
283
+ };
284
+ },
285
+ }));
@@ -0,0 +1,119 @@
1
+ import type { Hex, WalletClient } from 'viem';
2
+ import type { Connector } from 'wagmi';
3
+ import type { BridgeProvider, BridgeQuote, BridgeStatus, OrderResponse } from '@silentswap/sdk';
4
+ import type { SolanaWalletConnector, SolanaConnection } from './silent/solana-transaction.js';
5
+ export interface useTransactionOptions {
6
+ /** User's EVM address */
7
+ address: `0x${string}`;
8
+ /** Wallet client for signing operations */
9
+ walletClient?: WalletClient;
10
+ /** Wagmi connector */
11
+ connector?: Connector;
12
+ /** Solana wallet connector (required for Solana transactions) */
13
+ solanaConnector?: SolanaWalletConnector;
14
+ /** Solana RPC connection (required for Solana transactions) */
15
+ solanaConnection?: SolanaConnection;
16
+ /** Solana RPC URL (optional, will create connection if not provided) */
17
+ solanaRpcUrl?: string;
18
+ /** Optional callback to set current step (for external state management) */
19
+ setCurrentStep?: (step: string) => void;
20
+ /** Optional status update callback */
21
+ onStatus?: (status: string) => void;
22
+ }
23
+ /**
24
+ * Transaction execution result for OrderResponse transactions
25
+ */
26
+ export interface SwapTransaction {
27
+ hash: Hex;
28
+ chainId: number;
29
+ status: 'pending' | 'confirmed' | 'failed';
30
+ confirmations?: number;
31
+ }
32
+ export interface useTransactionReturn {
33
+ isLoading: boolean;
34
+ currentStep: string;
35
+ error: Error | null;
36
+ /** Execute a bridge transaction from a BridgeQuote */
37
+ executeTransaction: (quote: BridgeQuote) => Promise<BridgeStatus | null>;
38
+ /** Execute a deposit transaction from an OrderResponse */
39
+ executeSwapTransaction: (orderResponse: OrderResponse) => Promise<SwapTransaction>;
40
+ /** Approve token spending for a given allowance target */
41
+ approveTokenSpending: (chainId: number, tokenAddress: `0x${string}`, allowanceTarget: `0x${string}`, amount: string, userAddress: `0x${string}`) => Promise<Hex | null>;
42
+ /** Get bridge status for a request */
43
+ getStatus: (requestId: string, provider: BridgeProvider) => Promise<BridgeStatus | null>;
44
+ }
45
+ /**
46
+ * React hook for executing transactions
47
+ *
48
+ * This hook provides transaction execution for:
49
+ * - Bridge quotes from any provider (relay.link, deBridge)
50
+ * - SilentSwap deposit transactions (OrderResponse)
51
+ * - Token approvals for ERC-20 tokens
52
+ *
53
+ * It handles chain switching and transaction sending automatically for all transaction types.
54
+ *
55
+ * @param options - Configuration options for transaction execution
56
+ * @returns Object with transaction execution state and methods
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * import { useTransaction, useQuote } from '@silentswap/react';
61
+ * import { useWalletClient, useAccount } from 'wagmi';
62
+ *
63
+ * function BridgeComponent() {
64
+ * const { address, connector } = useAccount();
65
+ * const { data: walletClient } = useWalletClient();
66
+ *
67
+ * // Get the best quote from all providers
68
+ * const { getQuote: getBestQuote } = useQuote({ address });
69
+ *
70
+ * // Execute the transaction
71
+ * const {
72
+ * executeTransaction,
73
+ * getStatus,
74
+ * isLoading,
75
+ * currentStep,
76
+ * error
77
+ * } = useTransaction({
78
+ * address: address!,
79
+ * walletClient,
80
+ * connector,
81
+ * });
82
+ *
83
+ * const handleBridge = async () => {
84
+ * // Get the best quote from all providers
85
+ * const quote = await getBestQuote({
86
+ * srcChainId: 1, // Ethereum
87
+ * dstChainId: 43114, // Avalanche
88
+ * srcToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
89
+ * dstToken: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', // USDC.e
90
+ * amount: '1000000', // 1 USDC
91
+ * recipient: address!,
92
+ * });
93
+ *
94
+ * if (quote) {
95
+ * // Execute the bridge transaction
96
+ * const result = await executeTransaction(quote);
97
+ * console.log('Bridge completed:', result);
98
+ *
99
+ * // Check status if needed
100
+ * if (result?.requestId) {
101
+ * const status = await getStatus(result.requestId, quote.provider);
102
+ * console.log('Bridge status:', status);
103
+ * }
104
+ * }
105
+ * };
106
+ *
107
+ * return (
108
+ * <div>
109
+ * {isLoading && <div>{currentStep}...</div>}
110
+ * {error && <div>Error: {error.message}</div>}
111
+ * <button onClick={handleBridge} disabled={isLoading}>
112
+ * Bridge Assets
113
+ * </button>
114
+ * </div>
115
+ * );
116
+ * }
117
+ * ```
118
+ */
119
+ export declare function useTransaction({ walletClient, connector, solanaConnector, solanaConnection, solanaRpcUrl, setCurrentStep: externalSetCurrentStep, onStatus: externalOnStatus, }: useTransactionOptions): useTransactionReturn;