@xswap-link/sdk 0.8.7 → 0.9.1

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 (42) hide show
  1. package/.github/workflows/main.yml +2 -1
  2. package/CHANGELOG.md +12 -0
  3. package/babel.config.cjs +8 -0
  4. package/dist/index.d.mts +4 -2
  5. package/dist/index.d.ts +4 -2
  6. package/dist/index.global.js +132 -135
  7. package/dist/index.js +9 -9
  8. package/dist/index.mjs +10 -10
  9. package/jest.config.ts +37 -0
  10. package/package.json +17 -5
  11. package/src/components/Modal/index.tsx +1 -1
  12. package/src/components/Swap/Header/index.tsx +1 -1
  13. package/src/components/Swap/ReorderButton/ReorderButton.tsx +12 -10
  14. package/src/components/Swap/SwapView/FeesPanel/index.tsx +1 -1
  15. package/src/components/Swap/SwapView/SwapButton/index.tsx +23 -21
  16. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +4 -2
  17. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +51 -59
  18. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +47 -13
  19. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +138 -35
  20. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +6 -2
  21. package/src/components/Swap/SwapView/SwapPanel/index.tsx +1 -1
  22. package/src/components/Swap/SwapView/index.tsx +2 -2
  23. package/src/components/Tooltip/index.tsx +1 -1
  24. package/src/context/HistoryProvider.tsx +2 -2
  25. package/src/context/SwapProvider.tsx +174 -100
  26. package/src/context/TransactionProvider.tsx +1 -1
  27. package/src/models/TokenData.ts +3 -1
  28. package/src/models/payloads/GetPricesPayload.ts +1 -1
  29. package/src/utils/validation.ts +50 -16
  30. package/tailwind.config.js +1 -0
  31. package/test/context/SwapProvider.test.tsx +851 -0
  32. package/test/fileMock.ts +1 -0
  33. package/test/fixtures/bridgeTokens.mock.ts +1318 -0
  34. package/test/fixtures/bridgeTokensDictionary.mock.ts +1272 -0
  35. package/test/fixtures/integrationConfig.mock.ts +10 -0
  36. package/test/fixtures/supportedChains.mock.ts +32950 -0
  37. package/test/{api.test.ts → services/getChains.test.ts} +6 -5
  38. package/test/setup.ts +13 -0
  39. package/test/styleMock.ts +1 -0
  40. package/jest.config.json +0 -8
  41. package/test/api.mock.ts +0 -106
  42. package/test/setupTests.ts +0 -3
@@ -1,10 +1,8 @@
1
1
  import { TxConfigFormPayload } from "@src/components";
2
2
  import {
3
3
  DEFAULT_DESTINATION_CHAIN_ID,
4
- DEFAULT_DESTINATION_TOKEN_ADDR,
5
4
  DEFAULT_REQUEST_ABORT_MSG,
6
5
  DEFAULT_SOURCE_CHAIN_ID,
7
- DEFAULT_SOURCE_TOKEN_ADDR,
8
6
  } from "@src/constants";
9
7
  import { ADDRESSES } from "@src/contracts";
10
8
  import {
@@ -32,9 +30,8 @@ import {
32
30
  safeBigNumberFrom,
33
31
  weiToHumanReadable,
34
32
  } from "@src/utils";
35
- import { mapToValidPath } from "@src/utils/validation";
36
33
  import BigNumberJS from "bignumber.js";
37
- import { BigNumber, constants } from "ethers";
34
+ import { BigNumber, constants, ethers } from "ethers";
38
35
  import {
39
36
  createContext,
40
37
  Dispatch,
@@ -49,6 +46,7 @@ import {
49
46
  } from "react";
50
47
  import { Config, useAccount } from "wagmi";
51
48
  import { useHistory } from "./HistoryProvider";
49
+ import { mapToValidPath } from "@src/utils/validation";
52
50
 
53
51
  type Tab = "Swap" | "History" | "Settings";
54
52
  type SwapContext = {
@@ -66,8 +64,7 @@ type SwapContext = {
66
64
  setSrcChain: Dispatch<SetStateAction<Chain | undefined>>;
67
65
  srcChainTokensOptions: TokenOption[];
68
66
  srcChainOtherTokensOptions: TokenOption[];
69
- srcChainTokensPrices: TokenPrices;
70
- dstChainTokensPrices: TokenPrices;
67
+ tokenPrices: TokenPrices;
71
68
  srcToken: Token | undefined;
72
69
  setSrcToken: Dispatch<SetStateAction<Token | undefined>>;
73
70
  dstChain: Chain | undefined;
@@ -130,8 +127,7 @@ const init: SwapContext = {
130
127
  srcChain: undefined,
131
128
  srcChainTokensOptions: [],
132
129
  srcChainOtherTokensOptions: [],
133
- srcChainTokensPrices: {},
134
- dstChainTokensPrices: {},
130
+ tokenPrices: {},
135
131
  dstChain: undefined,
136
132
  dstChainTokensOptions: [],
137
133
  dstChainOtherTokensOptions: [],
@@ -150,7 +146,7 @@ const init: SwapContext = {
150
146
  dstValueMin: "",
151
147
  lastTx: undefined,
152
148
  expressDelivery: true,
153
- infiniteApproval: false,
149
+ infiniteApproval: true,
154
150
  slippage: "1.5",
155
151
  route: undefined,
156
152
  isAsyncDataLoaded: false,
@@ -248,12 +244,7 @@ export const SwapProvider = ({
248
244
  const [dstChainTokensOptions, setDstChainTokensOptions] = useState(
249
245
  init.dstChainTokensOptions,
250
246
  );
251
- const [srcChainTokensPrices, setSrcChainTokensPrices] = useState(
252
- init.srcChainTokensPrices,
253
- );
254
- const [dstChainTokensPrices, setDstChainTokensPrices] = useState(
255
- init.dstChainTokensPrices,
256
- );
247
+ const [tokenPrices, setTokenPrices] = useState(init.tokenPrices);
257
248
  const [srcChainOtherTokensOptions, setSrcChainOtherTokensOptions] = useState(
258
249
  init.srcChainOtherTokensOptions,
259
250
  );
@@ -267,6 +258,15 @@ export const SwapProvider = ({
267
258
 
268
259
  const { history } = useHistory();
269
260
 
261
+ useEffect(() => {
262
+ if (supportedChains.length > 0) {
263
+ const chainIds = supportedChains.map((chain) => chain.chainId);
264
+ getPrices({ chainId: chainIds }).then((prices) => {
265
+ setTokenPrices(prices);
266
+ });
267
+ }
268
+ }, [supportedChains]);
269
+
270
270
  const supportedTokens: Record<string, Record<string, Token>> = useMemo(() => {
271
271
  const customTokens = !isServer
272
272
  ? JSON.parse(localStorage.getItem("custom-tokens") || "{}")
@@ -344,8 +344,8 @@ export const SwapProvider = ({
344
344
  }, [bridgeTokens]);
345
345
 
346
346
  const getTokenOptions = useCallback(
347
- (chainId: string | undefined, oppositeChainId: string | undefined) => {
348
- if (!chainId || !oppositeChainId) {
347
+ (chainId: string | undefined) => {
348
+ if (!chainId) {
349
349
  return [];
350
350
  }
351
351
 
@@ -408,36 +408,21 @@ export const SwapProvider = ({
408
408
  [srcToken?.decimals, srcValue],
409
409
  );
410
410
 
411
- // fetch srcTokenPrices
412
- useEffect(() => {
413
- if (srcChain) {
414
- getPrices({ chainId: srcChain.chainId, currency: "USD" }).then((prices) =>
415
- setSrcChainTokensPrices(prices),
416
- );
417
- }
418
- }, [srcChain]);
419
-
420
- // fetch dstTokenPrices
421
- useEffect(() => {
422
- if (dstChain) {
423
- getPrices({ chainId: dstChain.chainId, currency: "USD" }).then((prices) =>
424
- setDstChainTokensPrices(prices),
425
- );
426
- }
427
- }, [dstChain]);
428
-
429
411
  const srcValueUsd = useMemo(() => {
430
412
  if (
431
413
  srcToken &&
432
- srcChainTokensPrices &&
433
- srcChainTokensPrices[srcToken.address]
414
+ srcChain &&
415
+ tokenPrices &&
416
+ tokenPrices[srcChain.chainId] &&
417
+ tokenPrices[srcChain.chainId]?.[srcToken.address]
434
418
  ) {
435
419
  return (
436
- Number(srcValue) * Number(srcChainTokensPrices[srcToken.address])
420
+ Number(srcValue) *
421
+ Number(tokenPrices[srcChain.chainId]?.[srcToken.address])
437
422
  ).toFixed(2);
438
423
  }
439
424
  return null;
440
- }, [srcChainTokensPrices, srcToken, srcValue]);
425
+ }, [tokenPrices, srcToken, srcValue, srcChain]);
441
426
  const dstValue = useMemo(
442
427
  () =>
443
428
  weiToHumanReadable({
@@ -459,15 +444,18 @@ export const SwapProvider = ({
459
444
  const dstValueUsd = useMemo(() => {
460
445
  if (
461
446
  dstToken &&
462
- dstChainTokensPrices &&
463
- dstChainTokensPrices[dstToken.address]
447
+ dstChain &&
448
+ tokenPrices &&
449
+ tokenPrices[dstChain?.chainId] &&
450
+ tokenPrices[dstChain?.chainId]?.[dstToken.address]
464
451
  ) {
465
452
  return (
466
- Number(dstValue) * Number(dstChainTokensPrices[dstToken.address])
453
+ Number(dstValue) *
454
+ Number(tokenPrices[dstChain?.chainId]?.[dstToken.address])
467
455
  ).toFixed(2);
468
456
  }
469
457
  return null;
470
- }, [dstChainTokensPrices, dstToken, dstValue]);
458
+ }, [tokenPrices, dstToken, dstValue, dstChain]);
471
459
  const dstValueMin = useMemo(
472
460
  () =>
473
461
  weiToHumanReadable({
@@ -564,7 +552,6 @@ export const SwapProvider = ({
564
552
  // source chain
565
553
  const srcTokenOptions: TokenOption[] = getTokenOptions(
566
554
  srcChain?.chainId,
567
- dstChain?.chainId,
568
555
  ).filter(({ symbol }) =>
569
556
  dstChain?.chainId === srcChain?.chainId
570
557
  ? symbol !== dstToken?.symbol
@@ -575,7 +562,6 @@ export const SwapProvider = ({
575
562
  // destination chain
576
563
  const dstTokenOptions: TokenOption[] = getTokenOptions(
577
564
  dstChain?.chainId,
578
- dstChain?.chainId,
579
565
  ).filter(({ symbol }) =>
580
566
  dstChain?.chainId === srcChain?.chainId
581
567
  ? symbol !== srcToken?.symbol
@@ -603,7 +589,7 @@ export const SwapProvider = ({
603
589
  );
604
590
  for (const { chainId } of srcOtherChains) {
605
591
  srcOtherTokenOptions.push(
606
- ...getTokenOptions(chainId, dstChain?.chainId).filter(({ symbol }) =>
592
+ ...getTokenOptions(chainId).filter(({ symbol }) =>
607
593
  chainId === dstChain?.chainId ? symbol !== dstToken?.symbol : true,
608
594
  ),
609
595
  );
@@ -620,7 +606,7 @@ export const SwapProvider = ({
620
606
  );
621
607
  for (const { chainId } of dstOtherChains) {
622
608
  dstOtherTokenOptions.push(
623
- ...getTokenOptions(chainId, dstChain?.chainId).filter(({ symbol }) =>
609
+ ...getTokenOptions(chainId).filter(({ symbol }) =>
624
610
  chainId === srcChain?.chainId ? symbol !== srcToken?.symbol : true,
625
611
  ),
626
612
  );
@@ -714,64 +700,149 @@ export const SwapProvider = ({
714
700
  );
715
701
  }, [srcChain]);
716
702
 
703
+ // Add new state to track initial setup
704
+ const [isInitialStateSet, setIsInitialStateSet] = useState(false);
705
+
717
706
  useEffect(() => {
718
- setSrcChain((state) => {
719
- const searchedChainId =
720
- // currently set chain
721
- state?.chainId ||
722
- // chain set by the integrator
723
- integrationConfig?.srcChainId ||
724
- // wallet chain
725
- (walletChainId ? `${walletChainId}` : undefined) ||
726
- // default chain
727
- (bridgeUI ? undefined : DEFAULT_SOURCE_CHAIN_ID);
728
-
729
- const initSrcChain = supportedChains.find(
730
- ({ chainId }) => chainId === searchedChainId,
731
- );
707
+ // apply wallet chain only after initial state is set
708
+ if (!walletChainId || !isInitialStateSet || integrationConfig?.srcChainId) {
709
+ return;
710
+ }
711
+ const initSrcChain = supportedChains.find(
712
+ ({ chainId }) => chainId === `${walletChainId}`,
713
+ );
732
714
 
733
- if (initSrcChain) {
734
- const initSrcToken = initSrcChain.tokens.find(
735
- ({ address }) =>
736
- address.toLowerCase() ===
737
- (integrationConfig?.srcTokenAddr?.toLowerCase() ||
738
- DEFAULT_SOURCE_TOKEN_ADDR.toLowerCase()),
739
- );
715
+ if (initSrcChain) {
716
+ setSrcChain(initSrcChain);
717
+ }
718
+ }, [
719
+ walletChainId,
720
+ isInitialStateSet,
721
+ supportedChains,
722
+ integrationConfig?.srcChainId,
723
+ ]);
740
724
 
741
- // This useEffect is only triggered by config and wallet changes.
742
- // So it will run when user connects wallet.
743
- // We want to keep the token that was already selected before connecting and only use initSrcToken if the token filed was empty.
744
- setSrcToken((state) => state || initSrcToken);
745
- } else {
746
- setSrcToken(undefined);
747
- }
748
- return initSrcChain;
749
- });
725
+ useEffect(() => {
726
+ if (isInitialStateSet) {
727
+ return;
728
+ }
729
+ setSrcToken(undefined);
730
+ setDstToken(undefined);
731
+ setSrcChain(undefined);
732
+ setDstChain(undefined);
733
+
734
+ // eslint-disable-next-line react-hooks/exhaustive-deps
735
+ }, [bridgeUI]);
736
+
737
+ useEffect(() => {
738
+ // Skip if initial state was already set or wait if supported chains are not loaded yet
750
739
 
751
- const initDstChain = bridgeUI
740
+ if (
741
+ isInitialStateSet ||
742
+ !supportedChains ||
743
+ supportedChains.length === 0 ||
744
+ !bridgeTokensDictionary
745
+ ) {
746
+ return;
747
+ }
748
+
749
+ let srcTokenAddress = integrationConfig.srcTokenAddr;
750
+ let dstTokenAddress = integrationConfig.dstTokenAddr;
751
+ // is it a valid eth address? if not try to find it by symbol / tokenId
752
+ if (srcTokenAddress && !ethers.utils.isAddress(srcTokenAddress)) {
753
+ srcTokenAddress = supportedChains
754
+ .find((chain) => chain.chainId === integrationConfig?.srcChainId)
755
+ ?.tokens.find(
756
+ (token) => token.tokenId === integrationConfig?.srcTokenAddr,
757
+ )
758
+ ?.address?.toLowerCase();
759
+ }
760
+
761
+ if (dstTokenAddress && !ethers.utils.isAddress(dstTokenAddress)) {
762
+ dstTokenAddress = supportedChains
763
+ .find((chain) => chain.chainId === integrationConfig?.dstChainId)
764
+ ?.tokens.find(
765
+ (token) => token.tokenId === integrationConfig?.dstTokenAddr,
766
+ )
767
+ ?.address?.toLowerCase();
768
+ }
769
+
770
+ const searchedChainId =
771
+ // currently set chain
772
+ srcChain ||
773
+ // chain set by the integrator
774
+ integrationConfig?.srcChainId ||
775
+ // wallet chain
776
+ (walletChainId ? `${walletChainId}` : undefined) ||
777
+ // default chain
778
+ (bridgeUI ? undefined : DEFAULT_SOURCE_CHAIN_ID);
779
+
780
+ const initSrcChain = supportedChains.find(
781
+ ({ chainId }) => chainId === searchedChainId,
782
+ );
783
+
784
+ let initSrcToken: Token | undefined;
785
+ if (initSrcChain) {
786
+ initSrcToken = initSrcChain.tokens.find(
787
+ ({ address }) =>
788
+ address.toLowerCase() === srcTokenAddress?.toLowerCase(),
789
+ );
790
+ }
791
+
792
+ let initDstChain: Chain | undefined = bridgeUI
752
793
  ? undefined
753
794
  : supportedChains.find(
754
- ({ chainId }) =>
755
- chainId ===
756
- (integrationConfig.dstChainId || DEFAULT_DESTINATION_CHAIN_ID),
795
+ ({ chainId }) => chainId === DEFAULT_DESTINATION_CHAIN_ID,
757
796
  );
758
- setDstChain(initDstChain);
759
797
 
798
+ // if integrator set it, use it
799
+ if (integrationConfig.dstChainId) {
800
+ initDstChain = supportedChains.find(
801
+ ({ chainId }) => chainId === integrationConfig.dstChainId,
802
+ );
803
+ }
804
+
805
+ let initDstToken: Token | undefined;
760
806
  if (initDstChain) {
761
- const initDstToken = initDstChain.tokens.find(
762
- ({ address }) =>
763
- address.toLowerCase() ===
764
- (integrationConfig.dstTokenAddr?.toLowerCase() ||
765
- DEFAULT_DESTINATION_TOKEN_ADDR.toLowerCase()),
807
+ initDstToken = initDstChain.tokens.find(
808
+ ({ address }) => address.toLowerCase() === dstTokenAddress,
766
809
  );
767
- setDstToken(initDstToken);
768
- } else {
769
- setDstToken(undefined);
770
810
  }
771
- }, [integrationConfig, supportedChains, bridgeUI, walletChainId]);
811
+
812
+ // the values here should not be from state but from the code above
813
+ const { source, target } = mapToValidPath({
814
+ source: { chain: initSrcChain, token: initSrcToken },
815
+ target: { chain: initDstChain, token: initDstToken },
816
+ type: bridgeUI ? "BRIDGE" : "SWAP",
817
+ supportedChains,
818
+ bridgeTokensDictionary,
819
+ });
820
+
821
+ setSrcChain(source.chain);
822
+ setDstChain(target.chain);
823
+ setSrcToken(source.token);
824
+ setDstToken(target.token);
825
+ // Mark initial state as set
826
+ setIsInitialStateSet(true);
827
+ // eslint-disable-next-line react-hooks/exhaustive-deps
828
+ }, [
829
+ integrationConfig?.srcChainId,
830
+ integrationConfig?.srcTokenAddr,
831
+ integrationConfig?.dstChainId,
832
+ integrationConfig?.dstTokenAddr,
833
+ supportedChains,
834
+ bridgeUI,
835
+ walletChainId,
836
+ isInitialStateSet,
837
+ bridgeTokensDictionary,
838
+ ]);
772
839
 
773
840
  useEffect(() => {
774
- if (!bridgeTokensDictionary) {
841
+ if (
842
+ !bridgeTokensDictionary ||
843
+ supportedChains.length === 0 ||
844
+ !isInitialStateSet
845
+ ) {
775
846
  return;
776
847
  }
777
848
 
@@ -814,11 +885,12 @@ export const SwapProvider = ({
814
885
  } else if (window.xPayOnDstChainChange) {
815
886
  window.xPayOnDstChainChange(dstChain?.chainId);
816
887
  }
817
- }, [integrationConfig, dstChain]);
888
+ // eslint-disable-next-line react-hooks/exhaustive-deps
889
+ }, [integrationConfig.onDstChainChange, dstChain]);
818
890
 
819
891
  useEffect(() => {
820
892
  const token = dstToken
821
- ? { address: dstToken.address.toLowerCase(), symbol: dstToken.symbol }
893
+ ? { address: dstToken.address.toLowerCase(), symbol: dstToken.tokenId }
822
894
  : undefined;
823
895
 
824
896
  if (integrationConfig.onDstTokenChange) {
@@ -826,7 +898,8 @@ export const SwapProvider = ({
826
898
  } else if (window.xPayOnDstTokenChange) {
827
899
  window.xPayOnDstTokenChange(token);
828
900
  }
829
- }, [integrationConfig, dstToken]);
901
+ // eslint-disable-next-line react-hooks/exhaustive-deps
902
+ }, [integrationConfig.onDstTokenChange, dstToken]);
830
903
 
831
904
  useEffect(() => {
832
905
  if (integrationConfig.onSrcChainChange) {
@@ -834,11 +907,12 @@ export const SwapProvider = ({
834
907
  } else if (window.xPayOnSrcChainChange) {
835
908
  window.xPayOnSrcChainChange(srcChain?.chainId);
836
909
  }
837
- }, [integrationConfig, srcChain]);
910
+ // eslint-disable-next-line react-hooks/exhaustive-deps
911
+ }, [integrationConfig.onSrcChainChange, srcChain]);
838
912
 
839
913
  useEffect(() => {
840
914
  const token = srcToken
841
- ? { address: srcToken.address.toLowerCase(), symbol: srcToken.symbol }
915
+ ? { address: srcToken.address.toLowerCase(), symbol: srcToken.tokenId }
842
916
  : undefined;
843
917
 
844
918
  if (integrationConfig.onSrcTokenChange) {
@@ -846,7 +920,8 @@ export const SwapProvider = ({
846
920
  } else if (window.xPayOnSrcTokenChange) {
847
921
  window.xPayOnSrcTokenChange(token);
848
922
  }
849
- }, [integrationConfig, srcToken]);
923
+ // eslint-disable-next-line react-hooks/exhaustive-deps
924
+ }, [integrationConfig.onSrcTokenChange, srcToken]);
850
925
 
851
926
  return (
852
927
  <swapContext.Provider
@@ -865,8 +940,7 @@ export const SwapProvider = ({
865
940
  setSrcChain,
866
941
  srcChainTokensOptions,
867
942
  srcChainOtherTokensOptions,
868
- dstChainTokensPrices,
869
- srcChainTokensPrices,
943
+ tokenPrices,
870
944
  dstChain,
871
945
  setDstChain,
872
946
  dstChainTokensOptions,
@@ -1,4 +1,4 @@
1
- import { Ecosystem, EnqueueTxProps, TxStatus } from "@src/models";
1
+ import { Ecosystem, EnqueueTxProps } from "@src/models";
2
2
  import { ContractReceipt, ContractTransaction } from "ethers";
3
3
  import {
4
4
  Context,
@@ -40,7 +40,9 @@ export type Prices = {
40
40
  };
41
41
 
42
42
  export type TokenPrices = {
43
- [tokenAddress: string]: string;
43
+ [chainId: string]: {
44
+ [tokenAddress: string]: string;
45
+ };
44
46
  };
45
47
 
46
48
  export type TokenBalances = {
@@ -1,4 +1,4 @@
1
1
  export type GetPricesPayload = {
2
- chainId: string;
2
+ chainId: string[];
3
3
  currency?: string;
4
4
  };
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  DEFAULT_DESTINATION_CHAIN_ID,
3
+ DEFAULT_DESTINATION_TOKEN_ADDR,
3
4
  DEFAULT_SOURCE_CHAIN_ID,
5
+ DEFAULT_SOURCE_TOKEN_ADDR,
4
6
  } from "@src/constants";
5
7
  import { BridgeTokensDictionary, Chain, Token } from "@src/models";
6
8
 
7
- const getDefaultSourceChain = ({
9
+ export const getDefaultSwapSourceChain = ({
8
10
  supportedChains,
9
11
  }: {
10
12
  supportedChains: Chain[];
@@ -23,7 +25,7 @@ const getDefaultSourceChain = ({
23
25
  return defaultSourceChain;
24
26
  };
25
27
 
26
- const getDefaultDestinationChain = ({
28
+ export const getDefaultDestinationChain = ({
27
29
  supportedChains,
28
30
  }: {
29
31
  supportedChains: Chain[];
@@ -42,15 +44,26 @@ const getDefaultDestinationChain = ({
42
44
  return defaultTargetChain;
43
45
  };
44
46
 
45
- const getDefaultSwapTokenForChain = (
47
+ export const getDefaultSwapTokenForChain = (
46
48
  chain: Chain,
49
+ defaultTokenAddress: string,
47
50
  sameChain?: boolean,
48
51
  sourceTokenId?: string,
49
52
  ) => {
50
- const xswapToken = chain.tokens.find((token) => token.tokenId === "XSWAP");
53
+ // First check if there are any supported tokens at all
54
+ const hasAnySupportedTokens = chain.tokens.some((token) => token.supported);
55
+ if (!hasAnySupportedTokens) {
56
+ return undefined;
57
+ }
51
58
 
52
- if (xswapToken) {
53
- return xswapToken;
59
+ const defaultToken = chain.tokens.find(
60
+ (token) =>
61
+ token.address.toLowerCase() === defaultTokenAddress.toLowerCase() &&
62
+ token.supported,
63
+ );
64
+
65
+ if (defaultToken) {
66
+ return defaultToken;
54
67
  }
55
68
 
56
69
  const fallbackToken = chain.tokens.find((token) =>
@@ -63,10 +76,10 @@ const getDefaultSwapTokenForChain = (
63
76
  return fallbackToken;
64
77
  }
65
78
 
66
- return chain.tokens[0];
79
+ return undefined;
67
80
  };
68
81
 
69
- const getDefaultBridgeTokenForChain = ({
82
+ export const getDefaultBridgeTokenForChain = ({
70
83
  chain,
71
84
  bridgeTokensDictionary,
72
85
  }: {
@@ -158,15 +171,17 @@ const mapToValidPathSwap = ({
158
171
  supportedChains,
159
172
  }: Omit<MapToValidPathParams, "type">) => {
160
173
  let newSourceChain: Chain | undefined =
161
- source.chain || getDefaultSourceChain({ supportedChains });
174
+ source.chain || getDefaultSwapSourceChain({ supportedChains });
162
175
  let newTargetChain: Chain | undefined =
163
176
  target.chain || getDefaultDestinationChain({ supportedChains });
164
177
  let newSourceToken: Token | undefined =
165
- source.token || getDefaultSwapTokenForChain(newSourceChain);
178
+ source.token ||
179
+ getDefaultSwapTokenForChain(newSourceChain, DEFAULT_SOURCE_TOKEN_ADDR);
166
180
  let newTargetToken: Token | undefined =
167
181
  target.token ||
168
182
  getDefaultSwapTokenForChain(
169
183
  newTargetChain,
184
+ DEFAULT_DESTINATION_TOKEN_ADDR,
170
185
  newSourceChain.chainId === newTargetChain.chainId,
171
186
  newSourceToken?.tokenId,
172
187
  );
@@ -185,7 +200,7 @@ const mapToValidPathSwap = ({
185
200
  }
186
201
 
187
202
  if (!isSwapSourceValid(newSourceChain)) {
188
- newSourceChain = getDefaultSourceChain({ supportedChains });
203
+ newSourceChain = getDefaultSwapSourceChain({ supportedChains });
189
204
  // We can't have dangling token from a different chain.
190
205
  newSourceToken = undefined;
191
206
 
@@ -242,11 +257,15 @@ const mapToValidPathSwap = ({
242
257
  // Setting tokens
243
258
 
244
259
  if (!newSourceToken) {
245
- newSourceToken = getDefaultSwapTokenForChain(newSourceChain);
260
+ newSourceToken = getDefaultSwapTokenForChain(
261
+ newSourceChain,
262
+ DEFAULT_SOURCE_TOKEN_ADDR,
263
+ );
246
264
  }
247
- if (!newTargetToken) {
265
+ if (!newTargetToken || newSourceToken === newTargetToken) {
248
266
  newTargetToken = getDefaultSwapTokenForChain(
249
267
  newTargetChain,
268
+ DEFAULT_DESTINATION_TOKEN_ADDR,
250
269
  newSourceChain.chainId === newTargetChain.chainId,
251
270
  newSourceToken?.tokenId,
252
271
  );
@@ -270,8 +289,7 @@ const mapToValidPathBridge = ({
270
289
  let newTargetToken = target.token;
271
290
 
272
291
  if (!isBridgeSourceValid(newSourceChain)) {
273
- newSourceChain = getDefaultSourceChain({ supportedChains });
274
- // We can't have dangling token from a different chain.
292
+ newSourceChain = undefined;
275
293
  newSourceToken = undefined;
276
294
 
277
295
  if (!isBridgeSourceValid(newSourceChain)) {
@@ -297,7 +315,7 @@ const mapToValidPathBridge = ({
297
315
  sourceToken: newSourceToken,
298
316
  })
299
317
  ) {
300
- newTargetChain = getDefaultDestinationChain({ supportedChains });
318
+ newTargetChain = undefined;
301
319
  // We can't have dangling token from a different chain.
302
320
  newTargetToken = undefined;
303
321
 
@@ -329,6 +347,18 @@ const mapToValidPathBridge = ({
329
347
 
330
348
  // Setting tokens
331
349
 
350
+ // check if the source token is bridgeable
351
+ if (newSourceToken && newSourceChain && newTargetChain) {
352
+ const isBridgeable =
353
+ !!bridgeTokensDictionary[newSourceChain.chainId]?.[
354
+ newSourceToken.address.toLowerCase()
355
+ ]?.[newTargetChain.chainId];
356
+
357
+ if (!isBridgeable) {
358
+ newSourceToken = undefined;
359
+ }
360
+ }
361
+
332
362
  if (!newSourceToken) {
333
363
  const defaultToken = getDefaultBridgeTokenForChain({
334
364
  chain: newSourceChain,
@@ -378,6 +408,10 @@ const mapToValidPathBridge = ({
378
408
  }
379
409
  }
380
410
 
411
+ if (!newTargetToken) {
412
+ newTargetChain = undefined;
413
+ }
414
+
381
415
  return {
382
416
  source: { chain: newSourceChain, token: newSourceToken },
383
417
  target: { chain: newTargetChain, token: newTargetToken },
@@ -6,6 +6,7 @@ export default {
6
6
  fontFamily: {
7
7
  body: ["'Satoshi-Medium'", "Tahoma", "Verdana", "ui-sans-serif"],
8
8
  sans: ["Satoshi-Medium", "Tahoma", "Verdana", "ui-sans-serif"],
9
+ "sans-bold": ["Satoshi-Bold", "Tahoma", "Verdana", "ui-sans-serif"],
9
10
  },
10
11
  extend: {
11
12
  colors: {