@xswap-link/sdk 0.10.0 → 0.10.2

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.
package/localTheme.ts CHANGED
@@ -108,6 +108,7 @@ const styleMap: ModalIntegrationThemeStyles & ModalIntegrationStyles = {
108
108
  errorLight: "--error-light",
109
109
  width: "--modal-width",
110
110
  chainlinkLogo: "--chainlink-logo",
111
+ fontFamily: "--font-family",
111
112
  };
112
113
 
113
114
  export function changeHostVariableColor(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xswap-link/sdk",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "JavaScript SDK for XSwap platform",
5
5
  "homepage": "https://github.com/xswap-link/xswap-sdk",
6
6
  "repository": {
@@ -56,6 +56,7 @@ export const TokenPicker = ({
56
56
  supportedChains,
57
57
  findTokenDataByAddressAndChain,
58
58
  tokenPrices,
59
+ addTokenToChainIfNotExists,
59
60
  } = useSwapContext();
60
61
  const { address } = useAccount();
61
62
  const evmContractApi = useEvmContractApi();
@@ -93,10 +94,18 @@ export const TokenPicker = ({
93
94
  };
94
95
  localStorage.setItem("custom-tokens", JSON.stringify(customTokens));
95
96
  }
96
- onSelect(newCustomToken);
97
+
98
+ addTokenToChainIfNotExists(newCustomToken.chainId, newCustomToken);
99
+
97
100
  setModalOpen(false);
98
101
  }
99
- }, [chain, searchValue, customTokenData, onSelect, setModalOpen]);
102
+ }, [
103
+ chain,
104
+ customTokenData,
105
+ searchValue,
106
+ addTokenToChainIfNotExists,
107
+ setModalOpen,
108
+ ]);
100
109
 
101
110
  const getCustomTokenData = useCallback(async () => {
102
111
  const nonce = Date.now();
@@ -236,21 +245,21 @@ export const TokenPicker = ({
236
245
  // Create groups based on chain and wallet connection status
237
246
  const groups = chain
238
247
  ? [
239
- ...(!!address && ownedFilteredTokens.length && type === "source"
248
+ ...(!!address && ownedFilteredTokens.length
240
249
  ? [ownedFilteredTokens]
241
250
  : []),
242
251
  currentChainTokens,
243
252
  otherChainTokens,
244
253
  ]
245
254
  : [
246
- ...(!!address && ownedFilteredTokens.length && type === "source"
255
+ ...(!!address && ownedFilteredTokens.length
247
256
  ? [ownedFilteredTokens]
248
257
  : []),
249
258
  [...currentChainTokens, ...otherChainTokens],
250
259
  ];
251
260
 
252
261
  return groups.flat();
253
- }, [filteredTokens, otherFilteredTokens, chain, address, type, tokenPrices]);
262
+ }, [filteredTokens, otherFilteredTokens, chain, address, tokenPrices]);
254
263
 
255
264
  const ownedTokens = useMemo(
256
265
  () => allTokens.filter((token) => token.balance?.gt(0)),
@@ -260,19 +269,15 @@ export const TokenPicker = ({
260
269
  const tokenGroups = useMemo(() => {
261
270
  return chain
262
271
  ? [
263
- ...(type === "source" && !!address && ownedTokens.length
264
- ? [ownedTokens]
265
- : []),
272
+ ...(!!address && ownedTokens.length ? [ownedTokens] : []),
266
273
  filteredTokens,
267
274
  otherFilteredTokens,
268
275
  ]
269
276
  : [
270
- ...(type === "source" && !!address && ownedTokens.length
271
- ? [ownedTokens]
272
- : []),
277
+ ...(!!address && ownedTokens.length ? [ownedTokens] : []),
273
278
  otherFilteredTokens,
274
279
  ];
275
- }, [chain, type, address, ownedTokens, filteredTokens, otherFilteredTokens]);
280
+ }, [chain, address, ownedTokens, filteredTokens, otherFilteredTokens]);
276
281
 
277
282
  return showImportWarning ? (
278
283
  <>
@@ -465,7 +470,7 @@ export const TokenPicker = ({
465
470
  if (!address) {
466
471
  return `${chain.displayName} network`;
467
472
  }
468
- return ownedTokens.length && type === "source"
473
+ return ownedTokens.length
469
474
  ? "My tokens"
470
475
  : `${chain.displayName} network`;
471
476
  }
@@ -489,7 +494,7 @@ export const TokenPicker = ({
489
494
  if (!address) {
490
495
  return "All networks";
491
496
  }
492
- return ownedTokens.length && type === "source"
497
+ return ownedTokens.length
493
498
  ? "My tokens"
494
499
  : "All networks";
495
500
  }
@@ -245,9 +245,9 @@ export const openTxConfigForm = async ({
245
245
  <TxConfigForm
246
246
  integratorId={integratorId}
247
247
  dstChainId={dstChainId}
248
- dstTokenAddr={dstTokenAddr}
248
+ dstTokenAddr={dstTokenAddr?.toLowerCase()}
249
249
  srcChainId={srcChainId}
250
- srcTokenAddr={srcTokenAddr}
250
+ srcTokenAddr={srcTokenAddr?.toLowerCase()}
251
251
  customContractCalls={customContractCalls}
252
252
  desc={desc}
253
253
  dstDisplayTokenAddr={dstDisplayTokenAddr}
@@ -11,6 +11,7 @@ import { useEffect, useState } from "react";
11
11
  import { Spinner } from "../Spinner";
12
12
  import { TxConfigForm } from "../TxConfigForm";
13
13
  import CSS from "../global.css";
14
+ import { addCustomTokensToChains } from "@src/utils/tokens";
14
15
 
15
16
  const TxWidget = ({
16
17
  integratorId,
@@ -56,7 +57,9 @@ const TxWidget = ({
56
57
  (bridgeSupported || swapSupported),
57
58
  );
58
59
 
59
- setSupportedChains(chains);
60
+ const chainsWithCustomTokens = addCustomTokensToChains(chains);
61
+
62
+ setSupportedChains(chainsWithCustomTokens);
60
63
  setLoading(false);
61
64
  })();
62
65
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -65,7 +65,6 @@ const createXPayRoot = async () => {
65
65
  const xswapElement = document.createElement("div");
66
66
  xpayShadowRoot = xswapElement.attachShadow({
67
67
  mode: "open",
68
- delegatesFocus: true,
69
68
  });
70
69
  xswapElement.setAttribute("id", "xpay-root");
71
70
  document.body.appendChild(xswapElement);
@@ -117,6 +117,10 @@ type SwapContext = {
117
117
  bridgeTokensDictionary: BridgeTokensDictionary | null;
118
118
  onConnectWallet: () => void;
119
119
  hasOnConnectWalletCallback: boolean;
120
+ addTokenToChainIfNotExists: (
121
+ chainId: string | undefined,
122
+ token: TokenOption,
123
+ ) => void;
120
124
  };
121
125
 
122
126
  const functionNotImplemented = () => {
@@ -183,6 +187,7 @@ const init: SwapContext = {
183
187
  hasOnConnectWalletCallback: false,
184
188
  srcChainOptions: [],
185
189
  dstChainOptions: [],
190
+ addTokenToChainIfNotExists: functionNotImplemented,
186
191
  };
187
192
 
188
193
  const swapContext = createContext(init);
@@ -668,6 +673,27 @@ export const SwapProvider = ({
668
673
  }
669
674
  }, [integrationConfig]);
670
675
 
676
+ const addTokenToChainIfNotExists = useCallback(
677
+ (chainId: string | undefined, token: TokenOption) => {
678
+ const chain = supportedChains.find((chain) => chain.chainId === chainId);
679
+
680
+ if (!chain) return chain;
681
+
682
+ const tokenExists = chain.tokens.some(
683
+ (existingToken) =>
684
+ existingToken.address.toLowerCase() === token.address.toLowerCase(),
685
+ );
686
+
687
+ const updated = tokenExists
688
+ ? chain
689
+ : { ...chain, tokens: [...chain.tokens, token] };
690
+
691
+ setSrcChain(updated);
692
+ setSrcToken(token);
693
+ },
694
+ [supportedChains],
695
+ );
696
+
671
697
  // =============================================================================
672
698
  // Effects
673
699
  // =============================================================================
@@ -987,7 +1013,8 @@ export const SwapProvider = ({
987
1013
  }
988
1014
  if (initDstChain) {
989
1015
  initDstToken = initDstChain.tokens.find(
990
- ({ address }) => address.toLowerCase() === dstTokenAddress,
1016
+ ({ address }) =>
1017
+ address.toLowerCase() === dstTokenAddress?.toLowerCase(),
991
1018
  );
992
1019
  }
993
1020
 
@@ -1182,6 +1209,7 @@ export const SwapProvider = ({
1182
1209
  bridgeTokensDictionary,
1183
1210
  onConnectWallet,
1184
1211
  hasOnConnectWalletCallback,
1212
+ addTokenToChainIfNotExists,
1185
1213
  }}
1186
1214
  >
1187
1215
  {children}
@@ -30,11 +30,12 @@ export type ModalIntegrationThemeStyles = {
30
30
  errorDark?: string;
31
31
  errorLight?: string;
32
32
  chainlinkLogo?: string;
33
+ fontFamily?: string;
33
34
  };
34
35
 
35
36
  export type ModalIntegrationStyles = Pick<
36
37
  ModalIntegrationThemeStyles,
37
- "mainAccentLight" | "mainAccentDark" | "textSecondary"
38
+ "mainAccentLight" | "mainAccentDark" | "textSecondary" | "fontFamily"
38
39
  > & { width?: string };
39
40
 
40
41
  export type ModalIntegrationPayload = {
@@ -87,13 +87,7 @@ const validateSwapTxData = (
87
87
  if (!supportedDstChain) {
88
88
  throw new Error(`Provided chain '${dstChain}' is not supported`);
89
89
  }
90
- if (
91
- dstToken &&
92
- (!isETHAddressValid(dstToken) ||
93
- supportedDstChain.tokens.findIndex(
94
- ({ address }) => address.toLowerCase() === dstToken.toLowerCase(),
95
- ) === -1)
96
- ) {
90
+ if (dstToken && !isETHAddressValid(dstToken)) {
97
91
  throw new Error(
98
92
  `Provided token address ${dstToken} on chain ${dstChain} is invalid`,
99
93
  );
@@ -107,13 +101,7 @@ const validateSwapTxData = (
107
101
  if (!supportedSrcChain) {
108
102
  throw new Error(`Provided chain '${srcChain}' is not supported`);
109
103
  }
110
- if (
111
- srcToken &&
112
- (!isETHAddressValid(srcToken) ||
113
- supportedSrcChain.tokens.findIndex(
114
- ({ address }) => address.toLowerCase() === srcToken.toLowerCase(),
115
- ) === -1)
116
- ) {
104
+ if (srcToken && !isETHAddressValid(srcToken)) {
117
105
  throw new Error(
118
106
  `Provided token address ${srcToken} on chain ${srcChain} is invalid`,
119
107
  );
@@ -0,0 +1,52 @@
1
+ import { Chain, Token } from "@src/models";
2
+ import { isServer } from "@src/utils";
3
+
4
+ /**
5
+ * Retrieves custom tokens from localStorage and adds them to the provided chains
6
+ * @param chains - The original chains to which custom tokens should be added
7
+ * @returns A new array of chains with custom tokens added
8
+ */
9
+ export const addCustomTokensToChains = (chains: Chain[]): Chain[] => {
10
+ const customTokensObj = !isServer
11
+ ? JSON.parse(localStorage.getItem("custom-tokens") || "{}")
12
+ : {};
13
+
14
+ const customTokens = Object.entries(customTokensObj).reduce(
15
+ (acc, [chainId, tokensObj]) => {
16
+ acc[chainId] = Object.values(tokensObj as Record<string, Token>);
17
+ return acc;
18
+ },
19
+ {} as Record<string, Token[]>,
20
+ );
21
+
22
+ return chains.map((chain) => {
23
+ const chainCustomTokens = customTokens[chain.chainId] || [];
24
+
25
+ if (chainCustomTokens.length === 0) {
26
+ return chain;
27
+ }
28
+
29
+ const filteredCustomTokens = chainCustomTokens.filter(
30
+ (customToken: Token) =>
31
+ !chain.tokens.some(
32
+ (existingToken) =>
33
+ existingToken.address.toLowerCase() ===
34
+ customToken.address.toLowerCase(),
35
+ ),
36
+ );
37
+
38
+ if (filteredCustomTokens.length === 0) {
39
+ return chain;
40
+ }
41
+
42
+ return {
43
+ ...chain,
44
+ tokens: [
45
+ ...chain.tokens,
46
+ ...filteredCustomTokens.map((customToken: Token) => ({
47
+ ...customToken,
48
+ })),
49
+ ],
50
+ };
51
+ });
52
+ };
@@ -1,12 +1,20 @@
1
1
  /** @type {import("tailwindcss").Config} */
2
2
 
3
+ const withFontVariable = (fallback) => `var(--font-family, ${fallback})`;
4
+
3
5
  export default {
4
6
  content: ["src/components/**/*.{ts,tsx}", "src/context/**/*.{ts,tsx}"],
5
7
  theme: {
6
8
  fontFamily: {
7
- body: ["'Satoshi-Medium'", "Tahoma", "Verdana", "ui-sans-serif"],
8
- sans: ["Satoshi-Medium", "Tahoma", "Verdana", "ui-sans-serif"],
9
- "sans-bold": ["Satoshi-Bold", "Tahoma", "Verdana", "ui-sans-serif"],
9
+ body: [
10
+ withFontVariable("'Satoshi-Medium', Tahoma, Verdana, ui-sans-serif"),
11
+ ],
12
+ sans: [
13
+ withFontVariable("'Satoshi-Medium', Tahoma, Verdana, ui-sans-serif"),
14
+ ],
15
+ "sans-bold": [
16
+ withFontVariable("'Satoshi-Bold', Tahoma, Verdana, ui-sans-serif"),
17
+ ],
10
18
  },
11
19
  extend: {
12
20
  colors: {