@xswap-link/sdk 0.10.5 → 0.10.7

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 (54) hide show
  1. package/.github/workflows/main.yml +1 -1
  2. package/CHANGELOG.md +13 -0
  3. package/dist/index.d.mts +46 -12
  4. package/dist/index.d.ts +46 -12
  5. package/dist/index.global.js +5290 -261
  6. package/dist/index.js +9 -9
  7. package/dist/index.mjs +9 -9
  8. package/package.json +13 -2
  9. package/src/assets/icons/StarsIcon.tsx +18 -0
  10. package/src/assets/icons/index.ts +1 -0
  11. package/src/components/AllWalletsConfig/index.tsx +40 -0
  12. package/src/components/Swap/HistoryView/index.tsx +28 -5
  13. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +73 -47
  14. package/src/components/Swap/SwapView/SwapButton/index.tsx +45 -10
  15. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +20 -2
  16. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +124 -4
  17. package/src/components/Swap/SwapView/SwapPanel/WalletPanel/index.tsx +71 -0
  18. package/src/components/Swap/SwapView/SwapPanel/index.tsx +4 -0
  19. package/src/components/Swap/SwapView/WalletPicker/index.tsx +63 -28
  20. package/src/components/Swap/index.tsx +2 -2
  21. package/src/components/TxConfigForm/index.tsx +12 -34
  22. package/src/components/TxDataCard/TxDataCardUI/index.tsx +16 -10
  23. package/src/components/TxDataCard/index.tsx +1 -0
  24. package/src/components/TxWidgetWC/index.tsx +4 -0
  25. package/src/components/TxWidgetWCWrapped/index.tsx +2 -0
  26. package/src/components/index.ts +1 -0
  27. package/src/config/wagmiConfig.ts +1 -6
  28. package/src/constants/index.ts +14 -0
  29. package/src/context/HistoryProvider.tsx +121 -25
  30. package/src/context/SwapProvider.tsx +104 -48
  31. package/src/context/TransactionProvider.tsx +60 -1
  32. package/src/context/TxUIWrapper.tsx +187 -71
  33. package/src/context/WalletProvider.tsx +108 -0
  34. package/src/contracts/addresses.ts +4 -0
  35. package/src/contracts/idl/jupiter.ts +2879 -0
  36. package/src/models/Ecosystem.ts +1 -0
  37. package/src/models/Route.ts +14 -3
  38. package/src/models/SolanaTransaction.ts +38 -0
  39. package/src/models/SolanaWeb3Network.ts +5 -0
  40. package/src/models/TxUIWrapper.ts +8 -4
  41. package/src/models/index.ts +1 -0
  42. package/src/models/payloads/GetBalancesPayload.ts +2 -1
  43. package/src/models/payloads/GetSolanaRoutePayload.ts +8 -0
  44. package/src/models/payloads/ModalIntegrationPayload.ts +1 -0
  45. package/src/models/payloads/WidgetIntegrationPayload.ts +1 -0
  46. package/src/models/payloads/index.ts +1 -0
  47. package/src/services/api.ts +21 -3
  48. package/src/services/integrations/transactions.ts +2 -0
  49. package/src/services/solana/jupiter/extract-swap-data.ts +183 -0
  50. package/src/services/solana/jupiter/get-events.ts +37 -0
  51. package/src/services/solana/jupiter/instruction-parser.ts +217 -0
  52. package/src/services/solana/jupiter/utils.ts +37 -0
  53. package/src/utils/strings.ts +16 -0
  54. package/test/context/SwapProvider.test.tsx +6 -9
@@ -28,9 +28,13 @@ import {
28
28
  useRef,
29
29
  useState,
30
30
  } from "react";
31
- import { useAccount } from "wagmi";
32
31
  import { useSwapContext } from "./SwapProvider";
33
- import { EvmHandlers, useTransaction } from "./TransactionProvider";
32
+ import {
33
+ EvmHandlers,
34
+ SolanaHandlers,
35
+ useTransaction,
36
+ } from "./TransactionProvider";
37
+ import { useWallet } from "./WalletProvider";
34
38
 
35
39
  const TxUIWrapperContext: Context<TxUIWrapperState> =
36
40
  createContext<TxUIWrapperState>({} as TxUIWrapperState);
@@ -39,7 +43,9 @@ type Props = {
39
43
  children: ReactNode;
40
44
  };
41
45
  export const TxUIWrapper = ({ children }: Props) => {
42
- const { chainId } = useAccount();
46
+ const { evm, solana } = useWallet();
47
+ const { chainId } = evm;
48
+ const { chainId: solanaChainId } = solana;
43
49
  const { enqueueTransaction: nativeEnqueueTransaction } = useTransaction();
44
50
  const { enqueueSnackbar, closeSnackbar } = useSnackbar();
45
51
  const [currentNetwork, setCurrentNetwork] = useState(Ecosystem.EVM);
@@ -175,59 +181,159 @@ export const TxUIWrapper = ({ children }: Props) => {
175
181
 
176
182
  newHandlers.onSuccess = (result) => {
177
183
  closeSnackbar(result?.transactionHash);
184
+
185
+ if (txKey.current !== currentKey) {
186
+ return;
187
+ }
188
+
189
+ setTxReceipt(result);
190
+ setTxError("");
191
+ setLoading(false);
192
+
193
+ if (handlers?.onSuccess) {
194
+ handlers.onSuccess(result);
195
+ }
196
+
197
+ if (isTxModalOpenRef.current || !showDefaultSuccessMessage) {
198
+ return;
199
+ }
200
+
201
+ const snackbarKey = `${txHash}-success`;
202
+ enqueueSnackbar(
203
+ <SnackMessage
204
+ message={{
205
+ text: `Transaction mined successfully!`,
206
+ variant: "success",
207
+ action: () => (
208
+ <div className="flex items-center flex-nowrap">
209
+ <Button
210
+ type="button"
211
+ onClick={() => {
212
+ window.open(
213
+ getTxExplorerUrl(
214
+ txHash,
215
+ supportedChains.find(
216
+ (chain) =>
217
+ chain.chainId === chainId?.toString() &&
218
+ chain.ecosystem === currentNetwork,
219
+ )?.transactionExplorer || "",
220
+ result,
221
+ ),
222
+ );
223
+ }}
224
+ >
225
+ View
226
+ </Button>
227
+ <div
228
+ className="fill-t_text_primary w-[14px] h-[14px] cursor-pointer p-[14px] leading-[0px]"
229
+ onClick={() => closeSnackbar(snackbarKey)}
230
+ >
231
+ <CloseIcon />
232
+ </div>
233
+ </div>
234
+ ),
235
+ }}
236
+ />,
237
+ { persist: false, key: snackbarKey },
238
+ );
239
+ };
240
+
241
+ newHandlers.onError = (err) => {
178
242
  if (txKey.current === currentKey) {
179
- setTxReceipt(result);
180
- if (!isTxModalOpenRef.current) {
181
- if (showDefaultSuccessMessage) {
182
- const snackbarKey = `${txHash}-success`;
183
- enqueueSnackbar(
184
- <SnackMessage
185
- message={{
186
- text: `Transaction mined successfully!`,
187
- variant: "success",
188
- action: () => (
189
- <div className="flex items-center flex-nowrap">
190
- <Button
191
- type="button"
192
- onClick={() => {
193
- window.open(
194
- getTxExplorerUrl(
195
- txHash,
196
- supportedChains.find(
197
- (chain) =>
198
- chain.chainId === chainId?.toString() &&
199
- chain.ecosystem === currentNetwork,
200
- )?.transactionExplorer || "",
201
- result,
202
- ),
203
- );
204
- }}
205
- >
206
- View
207
- </Button>
208
-
209
- <div
210
- className="fill-t_text_primary w-[14px] h-[14px] cursor-pointer p-[14px] leading-[0px]"
211
- onClick={() => closeSnackbar(snackbarKey)}
212
- >
213
- <CloseIcon />
214
- </div>
215
- </div>
216
- ),
217
- }}
218
- />,
219
- { persist: false, key: snackbarKey },
220
- );
221
- }
222
- }
223
- if (handlers?.onSuccess) {
224
- handlers?.onSuccess(result);
225
- }
226
- setTxError("");
243
+ setTxError(parseWeb3Error(err));
244
+ handlers?.onError?.(err);
227
245
  setLoading(false);
228
246
  }
229
247
  };
230
248
 
249
+ return newHandlers;
250
+ },
251
+ [
252
+ closeSnackbar,
253
+ txHash,
254
+ enqueueSnackbar,
255
+ getTxExplorerUrl,
256
+ supportedChains,
257
+ chainId,
258
+ currentNetwork,
259
+ ],
260
+ );
261
+
262
+ const addUILogicToHandlersSolana = useCallback(
263
+ (
264
+ currentKey: number,
265
+ handlers?: SolanaHandlers,
266
+ showDefaultSuccessMessage = true,
267
+ ): SolanaHandlers => {
268
+ const newHandlers: SolanaHandlers = { ...handlers };
269
+
270
+ newHandlers.onConfirm = (signedTransaction) => {
271
+ if (txKey.current === currentKey) {
272
+ if (handlers?.onConfirm) {
273
+ handlers?.onConfirm(signedTransaction);
274
+ }
275
+ setTxStatus((prevStatus) => prevStatus + 1);
276
+ }
277
+ };
278
+
279
+ newHandlers.onSuccess = (transactionHash) => {
280
+ closeSnackbar(transactionHash);
281
+
282
+ if (txKey.current !== currentKey) {
283
+ return;
284
+ }
285
+
286
+ if (handlers?.onSuccess) {
287
+ handlers?.onSuccess(transactionHash);
288
+ }
289
+ setTxHash(transactionHash || "");
290
+ setTxError("");
291
+ setLoading(false);
292
+
293
+ if (isTxModalOpenRef.current || !showDefaultSuccessMessage) {
294
+ return;
295
+ }
296
+
297
+ const snackbarKey = `${txHash}-success`;
298
+ enqueueSnackbar(
299
+ <SnackMessage
300
+ message={{
301
+ text: `Transaction mined successfully!`,
302
+ variant: "success",
303
+ action: () => (
304
+ <div className="flex items-center flex-nowrap">
305
+ <Button
306
+ type="button"
307
+ onClick={() => {
308
+ window.open(
309
+ getTxExplorerUrl(
310
+ txHash,
311
+ supportedChains.find(
312
+ (chain) =>
313
+ chain.chainId === solanaChainId?.toString() &&
314
+ chain.ecosystem === currentNetwork,
315
+ )?.transactionExplorer || "",
316
+ ),
317
+ );
318
+ }}
319
+ >
320
+ View
321
+ </Button>
322
+
323
+ <div
324
+ className="fill-t_text_primary w-[14px] h-[14px] cursor-pointer p-[14px] leading-[0px]"
325
+ onClick={() => closeSnackbar(snackbarKey)}
326
+ >
327
+ <CloseIcon />
328
+ </div>
329
+ </div>
330
+ ),
331
+ }}
332
+ />,
333
+ { persist: false, key: snackbarKey },
334
+ );
335
+ };
336
+
231
337
  newHandlers.onError = (err) => {
232
338
  if (txKey.current === currentKey) {
233
339
  setTxError(parseWeb3Error(err));
@@ -244,7 +350,7 @@ export const TxUIWrapper = ({ children }: Props) => {
244
350
  enqueueSnackbar,
245
351
  getTxExplorerUrl,
246
352
  supportedChains,
247
- chainId,
353
+ solanaChainId,
248
354
  currentNetwork,
249
355
  ],
250
356
  );
@@ -267,6 +373,12 @@ export const TxUIWrapper = ({ children }: Props) => {
267
373
  handlers as EvmHandlers,
268
374
  showDefaultSuccessMessage,
269
375
  );
376
+ } else if (network === Ecosystem.SOLANA) {
377
+ newHandlers = addUILogicToHandlersSolana(
378
+ currentKey,
379
+ handlers as SolanaHandlers,
380
+ showDefaultSuccessMessage,
381
+ );
270
382
  }
271
383
 
272
384
  // extracted tx logic
@@ -290,29 +402,33 @@ export const TxUIWrapper = ({ children }: Props) => {
290
402
 
291
403
  return currentKey;
292
404
  },
293
- [addUILogicToHandlersEVM, nativeEnqueueTransaction],
405
+ [
406
+ addUILogicToHandlersEVM,
407
+ addUILogicToHandlersSolana,
408
+ nativeEnqueueTransaction,
409
+ ],
294
410
  );
295
411
 
296
- const txExplorerUrl = useMemo(
297
- () =>
298
- getTxExplorerUrl(
299
- txHash,
300
- supportedChains.find(
301
- (chain) =>
302
- chain.chainId === chainId?.toString() &&
303
- chain.ecosystem === currentNetwork,
304
- )?.transactionExplorer || "",
305
- txReceipt,
306
- ),
307
- [
308
- chainId,
309
- currentNetwork,
310
- getTxExplorerUrl,
311
- supportedChains,
412
+ const txExplorerUrl = useMemo(() => {
413
+ return getTxExplorerUrl(
312
414
  txHash,
415
+ supportedChains.find(
416
+ (chain) =>
417
+ (chain.chainId === chainId?.toString() ||
418
+ chain.chainId === solanaChainId?.toString()) &&
419
+ chain.ecosystem === currentNetwork,
420
+ )?.transactionExplorer || "",
313
421
  txReceipt,
314
- ],
315
- );
422
+ );
423
+ }, [
424
+ chainId,
425
+ solanaChainId,
426
+ currentNetwork,
427
+ getTxExplorerUrl,
428
+ supportedChains,
429
+ txHash,
430
+ txReceipt,
431
+ ]);
316
432
 
317
433
  const setModalOpen = useCallback((flag: boolean) => {
318
434
  setTxModalOpen(flag);
@@ -0,0 +1,108 @@
1
+ import React, {
2
+ Context,
3
+ createContext,
4
+ Dispatch,
5
+ useCallback,
6
+ useContext,
7
+ useMemo,
8
+ useState,
9
+ } from "react";
10
+ import { useAccount, useConnect, useDisconnect } from "wagmi";
11
+ import { useWallet as useSolanaWallet } from "@solana/wallet-adapter-react";
12
+ import { SolanaWeb3Network } from "@src/models";
13
+
14
+ interface WalletInfo {
15
+ address: string | undefined;
16
+ isConnected: boolean;
17
+ chainId: string | undefined;
18
+ connectWallet: () => Promise<void>;
19
+ disconnectWallet: () => Promise<void>;
20
+ }
21
+ export interface WalletState {
22
+ openEVMWalletModal: boolean;
23
+ setOpenEVMWalletModal: Dispatch<React.SetStateAction<boolean>>;
24
+ openSolanaWalletModal: boolean;
25
+ setOpenSolanaWalletModal: Dispatch<React.SetStateAction<boolean>>;
26
+ evm: WalletInfo;
27
+ solana: WalletInfo;
28
+ }
29
+
30
+ // Wallet Context
31
+ const WalletContext: Context<WalletState> = createContext<WalletState>(
32
+ {} as WalletState,
33
+ );
34
+
35
+ export const WalletProvider = ({ children }) => {
36
+ const [openEVMWalletModal, setOpenEVMWalletModal] = useState(false);
37
+ const [openSolanaWalletModal, setOpenSolanaWalletModal] = useState(false);
38
+
39
+ const {
40
+ address: evmAddress,
41
+ isConnected: isEvmConnected,
42
+ chainId: evmChainId,
43
+ } = useAccount();
44
+ const { connect, connectors } = useConnect();
45
+ const { disconnect: evmDisconnect } = useDisconnect();
46
+
47
+ const solanaWallet = useSolanaWallet();
48
+
49
+ const connectEvmWallet = useCallback(async () => {
50
+ connect({ connector: connectors[0]! }); // Default to MetaMask
51
+ }, [connect, connectors]);
52
+
53
+ const connectSolanaWallet = useCallback(async () => {
54
+ await solanaWallet.connect();
55
+ }, [solanaWallet]);
56
+
57
+ const disconnectEvmWallet = useCallback(
58
+ () => new Promise<void>((resolve) => resolve(evmDisconnect())),
59
+ [evmDisconnect],
60
+ );
61
+ const disconnectSolanaWallet = useCallback(
62
+ () => solanaWallet.disconnect(),
63
+ [solanaWallet],
64
+ );
65
+ const state = useMemo(() => {
66
+ return {
67
+ evm: {
68
+ address: evmAddress?.toString(),
69
+ isConnected: isEvmConnected,
70
+ chainId: evmChainId?.toString(),
71
+ connectWallet: connectEvmWallet,
72
+ disconnectWallet: disconnectEvmWallet,
73
+ },
74
+ solana: {
75
+ address: solanaWallet.publicKey?.toBase58(),
76
+ isConnected: !!solanaWallet.publicKey,
77
+ chainId: SolanaWeb3Network.MAINNET,
78
+ connectWallet: connectSolanaWallet,
79
+ disconnectWallet: disconnectSolanaWallet,
80
+ },
81
+ };
82
+ }, [
83
+ evmAddress,
84
+ isEvmConnected,
85
+ evmChainId,
86
+ connectEvmWallet,
87
+ disconnectEvmWallet,
88
+ solanaWallet.publicKey,
89
+ connectSolanaWallet,
90
+ disconnectSolanaWallet,
91
+ ]);
92
+ return (
93
+ <WalletContext.Provider
94
+ value={{
95
+ openEVMWalletModal,
96
+ setOpenEVMWalletModal,
97
+ openSolanaWalletModal,
98
+ setOpenSolanaWalletModal,
99
+ ...state,
100
+ }}
101
+ >
102
+ {children}
103
+ </WalletContext.Provider>
104
+ );
105
+ };
106
+
107
+ // Custom Hook
108
+ export const useWallet = () => useContext(WalletContext);
@@ -179,4 +179,8 @@ export const ADDRESSES: Addresses = {
179
179
  [ContractName.XSwapRouterSingleChain]:
180
180
  "0x0bb24c4302889ea0fb22dfacbb845e5602f35972",
181
181
  },
182
+ // solana
183
+ "mainnet-beta": {
184
+ [ContractName.FeeCollector]: "XSwapfFzSz84kkGk5PCYzLrntPfLubFRJGbJRQjC7th",
185
+ },
182
186
  };