@xswap-link/sdk 0.8.6 → 0.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xswap-link/sdk",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "JavaScript SDK for XSwap platform",
5
5
  "homepage": "https://github.com/xswap-link/xswap-sdk",
6
6
  "repository": {
@@ -9,11 +9,11 @@ import { useMemo, useRef } from "react";
9
9
  type Props = {
10
10
  chain: Chain;
11
11
  type: SwapPanelType;
12
- disabledChains: string[];
12
+ disabled: boolean;
13
13
  onClick: (chainId: string) => void;
14
14
  };
15
15
 
16
- export const ChainItem = ({ chain, type, onClick, disabledChains }: Props) => {
16
+ export const ChainItem = ({ chain, type, onClick, disabled }: Props) => {
17
17
  const {
18
18
  networks: { evm },
19
19
  } = useNetworks();
@@ -40,12 +40,10 @@ export const ChainItem = ({ chain, type, onClick, disabledChains }: Props) => {
40
40
  return type === "destination" ? srcChain?.chainId : dstChain?.chainId;
41
41
  }, [bridgeUI, dstChain?.chainId, srcChain?.chainId, type]);
42
42
 
43
- const isInDisabledChains = disabledChains.includes(chain.chainId);
44
-
45
43
  return (
46
44
  <div
47
45
  onClick={() => {
48
- if (chain.chainId === sameChainId || isInDisabledChains) {
46
+ if (chain.chainId === sameChainId || disabled) {
49
47
  return;
50
48
  }
51
49
  onClick(chain.chainId);
@@ -53,7 +51,7 @@ export const ChainItem = ({ chain, type, onClick, disabledChains }: Props) => {
53
51
  className={`flex justify-between items-center gap-3 mt-2 mb-2 cursor-pointer p-4 border border-solid border-white border-opacity-0 hover:selected-chain-item ${
54
52
  currentChainId === chain.chainId ? "selected-chain-item" : ""
55
53
  } ${
56
- chain.chainId === sameChainId || isInDisabledChains
54
+ chain.chainId === sameChainId || disabled
57
55
  ? "disabled-chain-item cursor-default"
58
56
  : ""
59
57
  }`}
@@ -70,7 +68,7 @@ export const ChainItem = ({ chain, type, onClick, disabledChains }: Props) => {
70
68
  </div>
71
69
  )}
72
70
 
73
- {isInDisabledChains && chain.chainId !== sameChainId && (
71
+ {disabled && chain.chainId !== sameChainId && (
74
72
  <div className="flex items-center gap-1">
75
73
  <Tooltip
76
74
  text={
@@ -101,6 +101,22 @@ export const ChainPicker = ({
101
101
  supportedChains,
102
102
  ]);
103
103
 
104
+ const sortedChains = useMemo(() => {
105
+ return filteredChains
106
+ .map((chain) => ({
107
+ ...chain,
108
+ disabled: disabledChains.includes(chain.chainId),
109
+ }))
110
+ .sort((a, b) => {
111
+ if (a.disabled === b.disabled) {
112
+ return 0;
113
+ }
114
+
115
+ // Place false before true.
116
+ return a.disabled ? 1 : -1;
117
+ });
118
+ }, [filteredChains, disabledChains]);
119
+
104
120
  return (
105
121
  <div className="flex flex-col h-full">
106
122
  <div className="flex justify-between">
@@ -117,12 +133,12 @@ export const ChainPicker = ({
117
133
  {filteredChains.length ? (
118
134
  <Virtuoso
119
135
  style={{ height: "100%" }}
120
- data={filteredChains}
136
+ data={sortedChains}
121
137
  itemContent={(_, chain) => (
122
138
  <ChainItem
123
139
  chain={chain}
124
140
  type={type}
125
- disabledChains={disabledChains}
141
+ disabled={chain.disabled}
126
142
  onClick={select}
127
143
  />
128
144
  )}
@@ -2,8 +2,7 @@ import { ArrowDownIcon } from "@src/assets/icons";
2
2
  import { Modal } from "@src/components/Modal";
3
3
  import { useSwapContext } from "@src/context";
4
4
  import { Chain, Token } from "@src/models";
5
- import { useCallback, useEffect, useMemo, useState } from "react";
6
- import { useAccount } from "wagmi";
5
+ import { useCallback, useMemo, useState } from "react";
7
6
  import { SwapPanelType } from "../../../SwapView";
8
7
  import { ChainPicker } from "./ChainPicker";
9
8
 
@@ -15,8 +14,6 @@ type Props = {
15
14
 
16
15
  export const ChainPanel = ({ chain, type, className }: Props) => {
17
16
  const {
18
- supportedChains,
19
- srcChain,
20
17
  setSrcChain,
21
18
  setDstChain,
22
19
  srcToken,
@@ -25,13 +22,10 @@ export const ChainPanel = ({ chain, type, className }: Props) => {
25
22
  setDstToken,
26
23
  srcChainLocked,
27
24
  dstChainLocked,
28
- integrationConfig,
29
25
  bridgeUI,
30
26
  bridgeTokensDictionary,
31
27
  } = useSwapContext();
32
28
 
33
- const { chainId } = useAccount();
34
-
35
29
  const projectLockedDestinationChain = useMemo(
36
30
  () => type === "destination" && dstChainLocked,
37
31
  [type, dstChainLocked],
@@ -42,23 +36,6 @@ export const ChainPanel = ({ chain, type, className }: Props) => {
42
36
  [type, srcChainLocked],
43
37
  );
44
38
 
45
- // select chain from user wallet
46
- useEffect(() => {
47
- if (!chainId || integrationConfig.srcChainId) return;
48
-
49
- const userChainFromWallet = supportedChains.find(
50
- (chain) => chain.chainId === String(chainId),
51
- );
52
- if (
53
- !userChainFromWallet ||
54
- srcChain?.chainId === userChainFromWallet.chainId
55
- )
56
- return;
57
-
58
- setSrcChain(userChainFromWallet);
59
- // eslint-disable-next-line react-hooks/exhaustive-deps
60
- }, [chainId]);
61
-
62
39
  const [chainPickerModalOpen, setChainPickerModalOpen] = useState(false);
63
40
 
64
41
  const handleSrcChainChange = useCallback(
@@ -1,7 +1,5 @@
1
1
  import { TokenLogo } from "@src/components";
2
- import { useSwapContext } from "@src/context";
3
2
  import { TokenOption } from "@src/models";
4
- import { useMemo } from "react";
5
3
 
6
4
  type Props = {
7
5
  token: TokenOption;
@@ -13,13 +11,6 @@ export const QuickPickTokenItem = ({
13
11
  selectedTokenAddress,
14
12
  onClick,
15
13
  }: Props) => {
16
- const { supportedChains } = useSwapContext();
17
-
18
- const chain = useMemo(
19
- () => supportedChains.find(({ chainId }) => chainId === token.chainId),
20
- [supportedChains, token],
21
- );
22
-
23
14
  return (
24
15
  <button
25
16
  type="button"
@@ -36,9 +27,9 @@ export const QuickPickTokenItem = ({
36
27
  generatedLogoClassName="w-6 h-6"
37
28
  />
38
29
  <div className="flex flex-col gap-1 text-xs font-medium !leading-3">
39
- <div className="text-t_text_primary">{token.symbol}</div>
30
+ <div className="text-t_text_primary">{token.name}</div>
40
31
  <div className="text-t_text_primary text-opacity-50">
41
- {chain?.displayName}
32
+ {token.symbol}
42
33
  </div>
43
34
  </div>
44
35
  </button>
@@ -41,7 +41,7 @@ export const TokenItem = ({ token, selectedTokenAddress, onClick }: Props) => {
41
41
  {token.name}
42
42
  </div>
43
43
  <div className="text-xs text-t_text_primary text-opacity-50 font-medium">
44
- {chain?.displayName}
44
+ {token.symbol}
45
45
  </div>
46
46
  </div>
47
47
 
@@ -22,7 +22,7 @@ import { TokenItem } from "./TokenItem";
22
22
 
23
23
  type Props = {
24
24
  type: SwapPanelType;
25
- chain: Chain;
25
+ chain?: Chain;
26
26
  tokens: TokenOption[];
27
27
  otherTokens: TokenOption[];
28
28
  selectedTokenAddress?: string;
@@ -59,7 +59,7 @@ export const TokenPicker = ({
59
59
  }, [isOpen]);
60
60
 
61
61
  const importCustomToken = useCallback(() => {
62
- if (customTokenData && isETHAddressValid(searchValue)) {
62
+ if (chain && customTokenData && isETHAddressValid(searchValue)) {
63
63
  let customTokens = {};
64
64
  if (!isServer) {
65
65
  customTokens = JSON.parse(
@@ -97,7 +97,7 @@ export const TokenPicker = ({
97
97
  currentRouteRequestNonce.current = nonce;
98
98
  const rpcUrl = supportedChains.find(
99
99
  ({ ecosystem, chainId }) =>
100
- ecosystem === Ecosystem.EVM && chainId === chain.chainId,
100
+ ecosystem === Ecosystem.EVM && chainId === chain?.chainId,
101
101
  )?.publicRpcUrls[0];
102
102
  const customTokenContract = new ethers.Contract(
103
103
  searchValue.toLowerCase(),
@@ -134,6 +134,7 @@ export const TokenPicker = ({
134
134
  if (
135
135
  !bridgeUI &&
136
136
  isETHAddressValid(searchValue) &&
137
+ chain &&
137
138
  !findTokenDataByAddressAndChain(searchValue.toLowerCase(), chain.chainId)
138
139
  ) {
139
140
  getCustomTokenData();
@@ -385,9 +386,15 @@ export const TokenPicker = ({
385
386
  groupContent={(index) => (
386
387
  <div className="py-1 bg-t_bg_primary text-t_text_primary">
387
388
  <div className="px-4 text-left">
388
- {index === 0
389
- ? `${chain.displayName} network`
390
- : "Other networks"}
389
+ {(() => {
390
+ if (chain) {
391
+ return index === 0
392
+ ? `${chain.displayName} network`
393
+ : "Other networks";
394
+ }
395
+
396
+ return "All networks";
397
+ })()}
391
398
  </div>
392
399
  <div className="horizontal-separator" />
393
400
  {!tokenGroups[index]?.length && (
@@ -12,7 +12,7 @@ export const ROUTE_TIMEOUT_MS = 10000;
12
12
  export const MINIMUM_DISPLAYED_TOKEN_AMOUNT = 0.0001;
13
13
  export const DEFAULT_SOURCE_CHAIN_ID = "1";
14
14
  export const DEFAULT_SOURCE_TOKEN_ADDR = constants.AddressZero;
15
- export const DEFAULT_DESTINATION_CHAIN_ID = "8453";
15
+ export const DEFAULT_DESTINATION_CHAIN_ID = "1";
16
16
  export const DEFAULT_DESTINATION_TOKEN_ADDR =
17
17
  "0x8fe815417913a93ea99049fc0718ee1647a2a07c"; // XSWAP token
18
18
  export const CCIP_EXPLORER = "https://ccip.chain.link/";
@@ -194,7 +194,7 @@ export const SwapProvider = ({
194
194
  wagmiConfig,
195
195
  overlay,
196
196
  }: Props) => {
197
- const { address } = useAccount();
197
+ const { address, chainId: walletChainId } = useAccount();
198
198
  const [tab, setTab] = useState(init.tab);
199
199
  const [error, setError] = useState(init.error);
200
200
  const [expressDelivery, setExpressDelivery] = useState(init.expressDelivery);
@@ -560,16 +560,13 @@ export const SwapProvider = ({
560
560
  }, [quoteRoute]);
561
561
 
562
562
  useEffect(() => {
563
- if (!srcChain || !dstChain) {
564
- return;
565
- }
566
563
  (async () => {
567
564
  // source chain
568
565
  const srcTokenOptions: TokenOption[] = getTokenOptions(
569
- srcChain.chainId,
570
- dstChain.chainId,
566
+ srcChain?.chainId,
567
+ dstChain?.chainId,
571
568
  ).filter(({ symbol }) =>
572
- dstChain.chainId === srcChain.chainId
569
+ dstChain?.chainId === srcChain?.chainId
573
570
  ? symbol !== dstToken?.symbol
574
571
  : true,
575
572
  );
@@ -577,10 +574,10 @@ export const SwapProvider = ({
577
574
 
578
575
  // destination chain
579
576
  const dstTokenOptions: TokenOption[] = getTokenOptions(
580
- dstChain.chainId,
581
- srcChain.chainId,
577
+ dstChain?.chainId,
578
+ dstChain?.chainId,
582
579
  ).filter(({ symbol }) =>
583
- dstChain.chainId === srcChain.chainId
580
+ dstChain?.chainId === srcChain?.chainId
584
581
  ? symbol !== srcToken?.symbol
585
582
  : true,
586
583
  );
@@ -591,7 +588,7 @@ export const SwapProvider = ({
591
588
  const srcOtherChains = supportedChains.filter(
592
589
  ({ chainId, web3Environment, swapSupported, bridgeSupported }) => {
593
590
  if (
594
- chainId === srcChain.chainId ||
591
+ chainId === srcChain?.chainId ||
595
592
  web3Environment !== Web3Environment.MAINNET
596
593
  ) {
597
594
  return false;
@@ -606,8 +603,8 @@ export const SwapProvider = ({
606
603
  );
607
604
  for (const { chainId } of srcOtherChains) {
608
605
  srcOtherTokenOptions.push(
609
- ...getTokenOptions(chainId, dstChain.chainId).filter(({ symbol }) =>
610
- chainId === dstChain.chainId ? symbol !== dstToken?.symbol : true,
606
+ ...getTokenOptions(chainId, dstChain?.chainId).filter(({ symbol }) =>
607
+ chainId === dstChain?.chainId ? symbol !== dstToken?.symbol : true,
611
608
  ),
612
609
  );
613
610
  }
@@ -617,14 +614,14 @@ export const SwapProvider = ({
617
614
  const dstOtherTokenOptions: TokenOption[] = [];
618
615
  const dstOtherChains = supportedChains.filter(
619
616
  ({ chainId, web3Environment, swapSupported }) =>
620
- chainId !== dstChain.chainId &&
617
+ chainId !== dstChain?.chainId &&
621
618
  web3Environment === Web3Environment.MAINNET &&
622
619
  swapSupported,
623
620
  );
624
621
  for (const { chainId } of dstOtherChains) {
625
622
  dstOtherTokenOptions.push(
626
- ...getTokenOptions(chainId, srcChain.chainId).filter(({ symbol }) =>
627
- chainId === srcChain.chainId ? symbol !== srcToken?.symbol : true,
623
+ ...getTokenOptions(chainId, dstChain?.chainId).filter(({ symbol }) =>
624
+ chainId === srcChain?.chainId ? symbol !== srcToken?.symbol : true,
628
625
  ),
629
626
  );
630
627
  }
@@ -657,6 +654,7 @@ export const SwapProvider = ({
657
654
  }
658
655
  })();
659
656
  }, [
657
+ bridgeUI,
660
658
  srcChain,
661
659
  dstChain,
662
660
  address,
@@ -717,33 +715,60 @@ export const SwapProvider = ({
717
715
  }, [srcChain]);
718
716
 
719
717
  useEffect(() => {
720
- const initSrcChain = supportedChains.find(
721
- ({ chainId }) =>
722
- chainId === (integrationConfig?.srcChainId || DEFAULT_SOURCE_CHAIN_ID),
723
- );
724
- const initSrcToken = initSrcChain?.tokens.find(
725
- ({ address }) =>
726
- address.toLowerCase() ===
727
- (integrationConfig?.srcTokenAddr?.toLowerCase() ||
728
- DEFAULT_SOURCE_TOKEN_ADDR.toLocaleLowerCase()),
729
- );
730
- const initDstChain = supportedChains.find(
731
- ({ chainId }) =>
732
- chainId ===
733
- (integrationConfig.dstChainId || DEFAULT_DESTINATION_CHAIN_ID),
734
- );
735
- const initDstToken = initDstChain?.tokens.find(
736
- ({ address }) =>
737
- address.toLowerCase() ===
738
- (integrationConfig.dstTokenAddr?.toLowerCase() ||
739
- DEFAULT_DESTINATION_TOKEN_ADDR.toLowerCase()),
740
- );
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
+ );
732
+
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
+ );
741
740
 
742
- setSrcChain(initSrcChain);
743
- setSrcToken(initSrcToken);
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
+ });
750
+
751
+ const initDstChain = bridgeUI
752
+ ? undefined
753
+ : supportedChains.find(
754
+ ({ chainId }) =>
755
+ chainId ===
756
+ (integrationConfig.dstChainId || DEFAULT_DESTINATION_CHAIN_ID),
757
+ );
744
758
  setDstChain(initDstChain);
745
- setDstToken(initDstToken);
746
- }, [integrationConfig, supportedChains]);
759
+
760
+ if (initDstChain) {
761
+ const initDstToken = initDstChain.tokens.find(
762
+ ({ address }) =>
763
+ address.toLowerCase() ===
764
+ (integrationConfig.dstTokenAddr?.toLowerCase() ||
765
+ DEFAULT_DESTINATION_TOKEN_ADDR.toLowerCase()),
766
+ );
767
+ setDstToken(initDstToken);
768
+ } else {
769
+ setDstToken(undefined);
770
+ }
771
+ }, [integrationConfig, supportedChains, bridgeUI, walletChainId]);
747
772
 
748
773
  useEffect(() => {
749
774
  if (!bridgeTokensDictionary) {