@xswap-link/sdk 0.10.1 → 0.10.3

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.1",
3
+ "version": "0.10.3",
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();
@@ -29,6 +29,20 @@ export type TxConfigFormPayload = Omit<
29
29
  integratorFeeReceiverAddress?: string;
30
30
  };
31
31
 
32
+ type TxConfigFormProps = TxConfigFormPayload &
33
+ (
34
+ | {
35
+ onSubmit: (route: Route) => void;
36
+ onClose: () => void;
37
+ overlay: true;
38
+ }
39
+ | {
40
+ onSubmit?: undefined;
41
+ onClose?: undefined;
42
+ overlay: false;
43
+ }
44
+ );
45
+
32
46
  const queryClient = new QueryClient();
33
47
 
34
48
  const WagmiReload: FC = () => {
@@ -52,6 +66,14 @@ const WagmiReload: FC = () => {
52
66
  return <></>;
53
67
  };
54
68
 
69
+ const normalizeTxConfigFormProps = (props: TxConfigFormProps) => {
70
+ return {
71
+ ...props,
72
+ dstTokenAddr: props.dstTokenAddr?.toLowerCase(),
73
+ srcTokenAddr: props.srcTokenAddr?.toLowerCase(),
74
+ };
75
+ };
76
+
55
77
  const PendingTransactionsEmitter: FC<{
56
78
  onPendingTransactionsChange: ModalIntegrationPayload["onPendingTransactionsChange"];
57
79
  }> = ({ onPendingTransactionsChange }) => {
@@ -73,49 +95,44 @@ const PendingTransactionsEmitter: FC<{
73
95
  return <></>;
74
96
  };
75
97
 
76
- export const TxConfigForm = ({
77
- integratorId,
78
- dstChainId,
79
- dstTokenAddr,
80
- srcChainId,
81
- srcTokenAddr,
82
- customContractCalls,
83
- desc,
84
- dstDisplayTokenAddr,
85
- supportedChains,
86
- onSubmit,
87
- onClose,
88
- overlay,
89
- lightTheme,
90
- defaultWalletPicker,
91
- styles,
92
- onPendingTransactionsChange,
93
- dstTokenLocked,
94
- dstChainLocked,
95
- srcTokenLocked,
96
- srcChainLocked,
97
- onDstTokenChange,
98
- onDstChainChange,
99
- onSrcTokenChange,
100
- onSrcChainChange,
101
- bridge,
102
- onConnectWallet,
103
- override,
104
- integratorFee,
105
- integratorFeeReceiverAddress,
106
- }: TxConfigFormPayload &
107
- (
108
- | {
109
- onSubmit: (route: Route) => void;
110
- onClose: () => void;
111
- overlay: true;
112
- }
113
- | {
114
- onSubmit?: undefined;
115
- onClose?: undefined;
116
- overlay: false;
117
- }
118
- )) => {
98
+ export const TxConfigForm = (props: TxConfigFormProps) => {
99
+ const normalizedProps = useMemo(
100
+ () => normalizeTxConfigFormProps(props),
101
+ [props],
102
+ );
103
+
104
+ const {
105
+ integratorId,
106
+ dstChainId,
107
+ dstTokenAddr,
108
+ srcChainId,
109
+ srcTokenAddr,
110
+ customContractCalls,
111
+ desc,
112
+ dstDisplayTokenAddr,
113
+ supportedChains,
114
+ onSubmit,
115
+ onClose,
116
+ overlay,
117
+ lightTheme,
118
+ defaultWalletPicker,
119
+ styles,
120
+ onPendingTransactionsChange,
121
+ dstTokenLocked,
122
+ dstChainLocked,
123
+ srcTokenLocked,
124
+ srcChainLocked,
125
+ onDstTokenChange,
126
+ onDstChainChange,
127
+ onSrcTokenChange,
128
+ onSrcChainChange,
129
+ bridge,
130
+ onConnectWallet,
131
+ override,
132
+ integratorFee,
133
+ integratorFeeReceiverAddress,
134
+ } = normalizedProps;
135
+
119
136
  const onBackdropClick = (e: MouseEvent) => {
120
137
  e.stopPropagation();
121
138
  e.nativeEvent.stopImmediatePropagation();
@@ -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: {