@xswap-link/sdk 0.7.0 → 0.8.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 (37) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/index.d.mts +122 -70
  3. package/dist/index.d.ts +122 -70
  4. package/dist/index.global.js +686 -281
  5. package/dist/index.js +10 -10
  6. package/dist/index.mjs +10 -10
  7. package/package.json +1 -1
  8. package/src/assets/icons/ArrowsExchange.tsx +18 -0
  9. package/src/assets/icons/index.ts +1 -0
  10. package/src/components/Swap/Header/index.tsx +4 -2
  11. package/src/components/Swap/HistoryView/index.tsx +2 -2
  12. package/src/components/Swap/ReorderButton/ReorderButton.tsx +37 -0
  13. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/ArrowIcon.tsx +1 -1
  14. package/src/components/Swap/SwapView/SwapButton/index.tsx +3 -1
  15. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +55 -22
  16. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/index.tsx +71 -6
  17. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/index.tsx +68 -15
  18. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +10 -2
  19. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +31 -11
  20. package/src/components/Swap/SwapView/SwapPanel/index.tsx +8 -3
  21. package/src/components/Swap/SwapView/index.tsx +3 -3
  22. package/src/components/Swap/index.tsx +1 -1
  23. package/src/components/TxConfigForm/index.tsx +57 -4
  24. package/src/components/TxWidgetWC/index.tsx +47 -2
  25. package/src/components/TxWidgetWCWrapped/index.tsx +33 -1
  26. package/src/config/index.ts +1 -1
  27. package/src/context/SwapProvider.tsx +177 -27
  28. package/src/models/BridgeTokensDictionary.ts +7 -0
  29. package/src/models/TokenData.ts +3 -1
  30. package/src/models/index.ts +6 -5
  31. package/src/models/payloads/ModalIntegrationPayload.ts +15 -1
  32. package/src/models/payloads/WidgetIntegrationPayload.ts +15 -1
  33. package/src/services/api.ts +6 -1
  34. package/src/services/integrations/transactions.ts +20 -0
  35. package/src/types/global.d.ts +15 -0
  36. package/src/utils/validation.ts +383 -0
  37. package/tsconfig.json +3 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xswap-link/sdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "JavaScript SDK for XSwap platform",
5
5
  "homepage": "https://github.com/xswap-link/xswap-sdk",
6
6
  "repository": {
@@ -0,0 +1,18 @@
1
+ import { FC } from "react";
2
+
3
+ export const ArrowsExchange: FC = () => {
4
+ return (
5
+ <svg
6
+ width="14"
7
+ height="18"
8
+ viewBox="0 0 14 18"
9
+ fill="none"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ >
12
+ <path
13
+ d="M3.66665 9.83335V3.85419L1.52081 6.00002L0.333313 4.83335L4.49998 0.666687L8.66665 4.83335L7.47915 6.00002L5.33331 3.85419V9.83335H3.66665ZM9.49998 17.3334L5.33331 13.1667L6.52081 12L8.66665 14.1459V8.16669H10.3333V14.1459L12.4791 12L13.6666 13.1667L9.49998 17.3334Z"
14
+ fill="white"
15
+ />
16
+ </svg>
17
+ );
18
+ };
@@ -2,6 +2,7 @@ export * from "./ArrowDownIcon";
2
2
  export * from "./ArrowDownLongIcon";
3
3
  export * from "./ArrowLeftIcon";
4
4
  export * from "./ArrowRightIcon";
5
+ export * from "./ArrowsExchange";
5
6
  export * from "./ArrowUpIcon";
6
7
  export * from "./ArrowUpRightIcon";
7
8
  export * from "./CashbacklIcon";
@@ -5,11 +5,13 @@ type Props = {
5
5
  onClose: () => void;
6
6
  };
7
7
  export const Header = ({ onClose }: Props) => {
8
- const { tab } = useSwapContext();
8
+ const { tab, bridgeUI } = useSwapContext();
9
9
 
10
10
  return (
11
11
  <div className="flex justify-between">
12
- <div className="text-xl">{tab}</div>
12
+ <div className="text-xl">
13
+ {tab === "Swap" && bridgeUI ? "Bridge" : tab}
14
+ </div>
13
15
  <Controls onClose={onClose} />
14
16
  </div>
15
17
  );
@@ -22,7 +22,7 @@ export const HistoryView = () => {
22
22
  return (
23
23
  <Virtuoso
24
24
  data={history}
25
- style={{ height: "100%" }}
25
+ style={{ height: "532px" }}
26
26
  itemContent={(_, transaction) => {
27
27
  return <TxDataCard transaction={transaction} />;
28
28
  }}
@@ -30,5 +30,5 @@ export const HistoryView = () => {
30
30
  );
31
31
  }, [history, historyLoadedOnce]);
32
32
 
33
- return <div className="h-[40vh] overflow-hidden">{view}</div>;
33
+ return <div className="flex-1 overflow-hidden">{view}</div>;
34
34
  };
@@ -0,0 +1,37 @@
1
+ import { ArrowsExchange } from "@src/assets/icons";
2
+ import { useSwapContext } from "@src/context";
3
+ import { FC } from "react";
4
+
5
+ export const ReorderButton: FC = () => {
6
+ const {
7
+ dstChain,
8
+ dstToken,
9
+ srcChain,
10
+ srcToken,
11
+ setDstChain,
12
+ setDstToken,
13
+ setSrcChain,
14
+ setSrcToken,
15
+ } = useSwapContext();
16
+
17
+ const onReorderClick = () => {
18
+ setDstChain(srcChain);
19
+ setSrcChain(dstChain);
20
+ setDstToken(srcToken);
21
+ setSrcToken(dstToken);
22
+ };
23
+
24
+ return (
25
+ <button
26
+ onClick={onReorderClick}
27
+ aria-label="Switch target token with the source token"
28
+ className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-14 p-1 border border-solid border-t_text_primary border-opacity-10 rounded-xl bg-t_bg_secondary"
29
+ >
30
+ <div className="relative bg-gradient-to-r from-t_main_accent_light to-t_main_accent_dark w-8 h-8 rounded-lg">
31
+ <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 fill-white">
32
+ <ArrowsExchange />
33
+ </div>
34
+ </div>
35
+ </button>
36
+ );
37
+ };
@@ -2,7 +2,7 @@ import { ArrowDownLongIcon } from "@src/assets/icons";
2
2
 
3
3
  export const ArrowIcon = () => {
4
4
  return (
5
- <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 p-1 border border-solid border-t_text_primary border-opacity-10 rounded-xl bg-t_bg_secondary">
5
+ <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-14 p-1 border border-solid border-t_text_primary border-opacity-10 rounded-xl bg-t_bg_secondary">
6
6
  <div className="relative bg-gradient-to-r from-t_main_accent_light to-t_main_accent_dark w-8 h-8 rounded-xl">
7
7
  <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 fill-white">
8
8
  <ArrowDownLongIcon />
@@ -24,6 +24,7 @@ export const SwapButton = () => {
24
24
  const { integrationConfig } = useSwapContext();
25
25
 
26
26
  const {
27
+ bridgeUI,
27
28
  error,
28
29
  srcChain,
29
30
  dstChain,
@@ -199,13 +200,14 @@ export const SwapButton = () => {
199
200
  }
200
201
 
201
202
  return {
202
- text: "Swap",
203
+ text: bridgeUI ? "Bridge" : "Swap",
203
204
  fn: async () => {
204
205
  showSwapConfirmationView();
205
206
  },
206
207
  disabled: false,
207
208
  };
208
209
  }, [
210
+ bridgeUI,
209
211
  srcChainId,
210
212
  dstChainId,
211
213
  srcTokenAddress,
@@ -1,74 +1,107 @@
1
1
  import { DotGreenIcon, HelpIcon } from "@src/assets/icons";
2
- import { Tooltip } from "@src/components";
2
+ import { SwapPanelType } from "@src/components/Swap/SwapView";
3
+ import { Tooltip } from "@src/components/Tooltip";
3
4
  import { useSwapContext } from "@src/context";
5
+ import useNetworks from "@src/hooks/networkManagement/useNetworks";
4
6
  import { Chain } from "@src/models";
5
7
  import { useMemo, useRef } from "react";
6
- import { SwapPanelType } from "../../../../../SwapView";
7
8
 
8
9
  type Props = {
9
10
  chain: Chain;
10
11
  type: SwapPanelType;
11
- disabledChain?: string;
12
+ disabledChains: string[];
12
13
  onClick: (chainId: string) => void;
13
14
  };
14
- export const ChainItem = ({ chain, type, disabledChain, onClick }: Props) => {
15
- // todo check useModal, we could improve this part to have the react state here, right now this line throws an error
16
- // const { address, chainId } = useAccount();
15
+
16
+ export const ChainItem = ({ chain, type, onClick, disabledChains }: Props) => {
17
+ const {
18
+ networks: { evm },
19
+ } = useNetworks();
17
20
 
18
21
  const tooltipTriggerRef = useRef<SVGSVGElement>(null);
19
22
 
20
- const { srcChain, dstChain } = useSwapContext();
23
+ const { srcChain, dstChain, bridgeUI } = useSwapContext();
24
+
21
25
  const currentChainId = useMemo(
22
26
  () => (type === "source" ? srcChain?.chainId : dstChain?.chainId),
23
27
  [dstChain, srcChain, type],
24
28
  );
25
29
 
26
- const connected = false; //address && chainId?.toString() === chain.chainId;
30
+ const connected = useMemo(
31
+ () => evm.signer && evm.chainId === chain.chainId,
32
+ [chain.chainId, evm.chainId, evm.signer],
33
+ );
34
+
35
+ const sameChainId = useMemo(() => {
36
+ if (!bridgeUI) {
37
+ return undefined;
38
+ }
39
+
40
+ return type === "destination" ? srcChain?.chainId : dstChain?.chainId;
41
+ }, [bridgeUI, dstChain?.chainId, srcChain?.chainId, type]);
42
+
43
+ const isInDisabledChains = disabledChains.includes(chain.chainId);
27
44
 
28
45
  return (
29
46
  <div
30
47
  onClick={() => {
31
- if (chain.chainId === disabledChain) {
48
+ if (chain.chainId === sameChainId || isInDisabledChains) {
32
49
  return;
33
50
  }
34
51
  onClick(chain.chainId);
35
52
  }}
36
- className={`flex justify-between gap-3 mt-2 mb-2 border border-solid border-transparent cursor-pointer p-4 hover:selected-chain-item ${
37
- currentChainId === chain.chainId
38
- ? "selected-chain-item"
39
- : "bg-t_bg_primary"
53
+ 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
+ currentChainId === chain.chainId ? "selected-chain-item" : ""
40
55
  } ${
41
- chain.chainId === disabledChain
56
+ chain.chainId === sameChainId || isInDisabledChains
42
57
  ? "disabled-chain-item cursor-default"
43
58
  : ""
44
59
  }`}
45
60
  >
46
61
  <div className="flex gap-3 items-center w-full">
47
62
  <img src={chain.image} alt={chain.name} className="w-[34px] h-[34px]" />
48
- <p className="text-t_text_primary">{chain.displayName}</p>
63
+ <p>{chain.displayName}</p>
49
64
  </div>
50
65
 
51
66
  {connected && (
52
- <div className="flex items-center gap-1">
67
+ <div className="flex items-baseline gap-1">
53
68
  <DotGreenIcon />
54
69
  <div className="text-t_text_green">connected</div>
55
70
  </div>
56
71
  )}
57
- {chain.chainId === disabledChain && (
72
+
73
+ {isInDisabledChains && chain.chainId !== sameChainId && (
58
74
  <div className="flex items-center gap-1">
59
75
  <Tooltip
60
- text="Transfers between same chain not available"
76
+ text={
77
+ bridgeUI
78
+ ? "Token is not available on this chain"
79
+ : `Transfers from ${
80
+ srcChain?.name || "the source chain"
81
+ } to this chain are currently not supported.`
82
+ }
61
83
  position="BOTTOM"
62
84
  triggerRef={tooltipTriggerRef}
63
- id="transfers-between-same-chains-tooltip"
85
+ id="transfers-from-chain-not-supported-tooltip"
64
86
  />
87
+ </div>
88
+ )}
65
89
 
66
- <HelpIcon
67
- ref={tooltipTriggerRef}
68
- className="cursor-help fill-t_main_accent_light w-[18px] h-[18px]"
90
+ {chain.chainId === sameChainId && (
91
+ <div className="flex items-center gap-1">
92
+ <Tooltip
93
+ text="Transfers between the same chain are not available"
94
+ id="transfers-between-same-chains-tooltip"
95
+ position="BOTTOM"
96
+ triggerRef={tooltipTriggerRef}
69
97
  />
70
98
  </div>
71
99
  )}
100
+
101
+ <HelpIcon
102
+ ref={tooltipTriggerRef}
103
+ className="cursor-help fill-t_main_accent_light w-[18px] h-[18px] min-w-[18px] min-h-[18px] max-w-[18px] max-h-[18px]"
104
+ />
72
105
  </div>
73
106
  );
74
107
  };
@@ -1,4 +1,5 @@
1
1
  import { CloseIcon } from "@src/assets/icons";
2
+ import { useSwapContext } from "@src/context";
2
3
  import { Chain, Web3Environment } from "@src/models";
3
4
  import { useMemo } from "react";
4
5
  import { Virtuoso } from "react-virtuoso";
@@ -7,24 +8,33 @@ import { ChainItem } from "./ChainItem";
7
8
 
8
9
  type Props = {
9
10
  type: SwapPanelType;
10
- supportedChains: Chain[];
11
11
  setSrcChain: (chain: Chain) => void;
12
12
  setDstChain: (chain: Chain) => void;
13
13
  onClose: () => void;
14
14
  };
15
15
  export const ChainPicker = ({
16
16
  type,
17
- supportedChains,
18
17
  setSrcChain,
19
18
  setDstChain,
20
19
  onClose,
21
20
  }: Props) => {
21
+ const {
22
+ srcChain,
23
+ srcToken,
24
+ dstChain,
25
+ dstToken,
26
+ bridgeUI,
27
+ bridgeTokensDictionary,
28
+ supportedChains,
29
+ } = useSwapContext();
30
+
22
31
  const filteredChains = useMemo(() => {
23
32
  return supportedChains.filter(
24
- ({ web3Environment, swapSupported }) =>
25
- web3Environment === Web3Environment.MAINNET && swapSupported,
33
+ ({ web3Environment, swapSupported, bridgeSupported }) =>
34
+ web3Environment === Web3Environment.MAINNET &&
35
+ ((bridgeUI && bridgeSupported) || (!bridgeUI && swapSupported)),
26
36
  );
27
- }, [supportedChains]);
37
+ }, [bridgeUI, supportedChains]);
28
38
 
29
39
  const select = (selectedChainId: string) => {
30
40
  const chain = supportedChains.find(
@@ -41,6 +51,56 @@ export const ChainPicker = ({
41
51
  onClose();
42
52
  };
43
53
 
54
+ const disabledChains = useMemo(() => {
55
+ if (!srcChain?.chainId || !srcToken?.address) {
56
+ return [];
57
+ }
58
+
59
+ if (!bridgeUI) {
60
+ const disabledForDestination =
61
+ supportedChains.find((chain) => chain.chainId === srcChain.chainId)
62
+ ?.disabledForDestination || [];
63
+
64
+ if (type === "destination") {
65
+ return filteredChains
66
+ ?.filter((chain) => disabledForDestination.includes(chain.chainId))
67
+ .map(({ chainId }) => chainId);
68
+ }
69
+
70
+ return [];
71
+ }
72
+
73
+ if (type === "destination") {
74
+ const availableChains =
75
+ bridgeTokensDictionary?.[srcChain.chainId]?.[srcToken.address];
76
+ const disabledChains = filteredChains
77
+ ?.filter((chain) => !availableChains?.[chain.chainId])
78
+ .map(({ chainId }) => chainId);
79
+ return disabledChains;
80
+ } else {
81
+ if (!dstChain || !dstToken) {
82
+ return [];
83
+ }
84
+
85
+ const availableChains =
86
+ bridgeTokensDictionary?.[dstChain.chainId]?.[dstToken.address];
87
+ const disabledChains = filteredChains
88
+ ?.filter((chain) => !availableChains?.[chain.chainId])
89
+ .map(({ chainId }) => chainId);
90
+ return disabledChains;
91
+ }
92
+ }, [
93
+ bridgeTokensDictionary,
94
+ bridgeUI,
95
+ filteredChains,
96
+ srcChain,
97
+ srcToken,
98
+ dstChain,
99
+ dstToken,
100
+ type,
101
+ supportedChains,
102
+ ]);
103
+
44
104
  return (
45
105
  <div className="flex flex-col h-full">
46
106
  <div className="flex justify-between">
@@ -59,7 +119,12 @@ export const ChainPicker = ({
59
119
  style={{ height: "100%" }}
60
120
  data={filteredChains}
61
121
  itemContent={(_, chain) => (
62
- <ChainItem chain={chain} type={type} onClick={select} />
122
+ <ChainItem
123
+ chain={chain}
124
+ type={type}
125
+ disabledChains={disabledChains}
126
+ onClick={select}
127
+ />
63
128
  )}
64
129
  />
65
130
  ) : (
@@ -1,7 +1,7 @@
1
1
  import { ArrowDownIcon } from "@src/assets/icons";
2
2
  import { Modal } from "@src/components/Modal";
3
3
  import { useSwapContext } from "@src/context";
4
- import { Chain } from "@src/models";
4
+ import { Chain, Token } from "@src/models";
5
5
  import { useCallback, useEffect, useMemo, useState } from "react";
6
6
  import { useAccount } from "wagmi";
7
7
  import { SwapPanelType } from "../../../SwapView";
@@ -10,9 +10,10 @@ import { ChainPicker } from "./ChainPicker";
10
10
  type Props = {
11
11
  chain: Chain | undefined;
12
12
  type: SwapPanelType;
13
+ className?: string;
13
14
  };
14
15
 
15
- export const ChainPanel = ({ chain, type }: Props) => {
16
+ export const ChainPanel = ({ chain, type, className }: Props) => {
16
17
  const {
17
18
  supportedChains,
18
19
  srcChain,
@@ -22,20 +23,28 @@ export const ChainPanel = ({ chain, type }: Props) => {
22
23
  setSrcToken,
23
24
  dstToken,
24
25
  setDstToken,
25
- srcChainProvided,
26
- dstChainProvided,
26
+ srcChainLocked,
27
+ dstChainLocked,
28
+ integrationConfig,
29
+ bridgeUI,
30
+ bridgeTokensDictionary,
27
31
  } = useSwapContext();
28
32
 
29
33
  const { chainId } = useAccount();
30
34
 
31
- const projectSelectedDestinationChain = useMemo(
32
- () => type === "destination" && dstChainProvided,
33
- [type, dstChainProvided],
35
+ const projectLockedDestinationChain = useMemo(
36
+ () => type === "destination" && dstChainLocked,
37
+ [type, dstChainLocked],
38
+ );
39
+
40
+ const projectLockedSourceChain = useMemo(
41
+ () => type === "source" && srcChainLocked,
42
+ [type, srcChainLocked],
34
43
  );
35
44
 
36
45
  // select chain from user wallet
37
46
  useEffect(() => {
38
- if (!chainId || srcChainProvided) return;
47
+ if (!chainId || integrationConfig.srcChainId) return;
39
48
 
40
49
  const userChainFromWallet = supportedChains.find(
41
50
  (chain) => chain.chainId === String(chainId),
@@ -54,13 +63,57 @@ export const ChainPanel = ({ chain, type }: Props) => {
54
63
 
55
64
  const handleSrcChainChange = useCallback(
56
65
  (chain: Chain) => {
57
- const newSrcToken =
58
- chain.tokens.find((token) => token.symbol === srcToken?.symbol) ||
59
- chain.tokens.find((token) => token.symbol === chain.tokenSymbol);
66
+ let newSrcToken: Token | undefined = undefined;
67
+
68
+ if (bridgeUI) {
69
+ const bridgeableTokensAddresses = Object.keys(
70
+ bridgeTokensDictionary?.[chain.chainId] || {},
71
+ ).map((address) => address.toLowerCase());
72
+
73
+ // Attempt to use the same token on a new chain matching by symbol.
74
+ newSrcToken = chain.tokens.find(
75
+ (token) =>
76
+ token.symbol === srcToken?.symbol &&
77
+ bridgeableTokensAddresses.includes(token.address.toLowerCase()),
78
+ );
79
+
80
+ if (!newSrcToken) {
81
+ // The token from the previous chain is not bridgeable on the new chain.
82
+ // We will use the first available bridgeable token on the new chain.
83
+
84
+ const firstAvailableBridgeableTokenAddress =
85
+ bridgeableTokensAddresses[0];
86
+
87
+ if (!firstAvailableBridgeableTokenAddress) {
88
+ throw new Error(
89
+ `Couldn't find any available bridgeable token on the chain ${chain.chainId}, but bridging is available`,
90
+ );
91
+ }
92
+
93
+ newSrcToken = chain.tokens.find(
94
+ (token) =>
95
+ token.address.toLowerCase() ===
96
+ firstAvailableBridgeableTokenAddress.toLowerCase(),
97
+ );
98
+
99
+ if (!newSrcToken) {
100
+ throw new Error(
101
+ `Found bridgeable token (${firstAvailableBridgeableTokenAddress}) on the chain ${chain.chainId}, but token was not defined in chains collection`,
102
+ );
103
+ }
104
+ }
105
+ } else {
106
+ newSrcToken =
107
+ // Attempt to use the same token on a new chain.
108
+ chain.tokens.find((token) => token.symbol === srcToken?.symbol) ||
109
+ // Otherwise, use the default token on this chain.
110
+ chain.tokens.find((token) => token.symbol === chain.tokenSymbol);
111
+ }
112
+
60
113
  setSrcChain(chain);
61
114
  setSrcToken(newSrcToken);
62
115
  },
63
- [setSrcChain, setSrcToken, srcToken],
116
+ [setSrcChain, setSrcToken, srcToken, bridgeTokensDictionary, bridgeUI],
64
117
  );
65
118
 
66
119
  const handleDstChainChange = useCallback(
@@ -80,7 +133,6 @@ export const ChainPanel = ({ chain, type }: Props) => {
80
133
  <Modal onClose={() => setChainPickerModalOpen(false)}>
81
134
  <ChainPicker
82
135
  type={type}
83
- supportedChains={supportedChains}
84
136
  setSrcChain={handleSrcChainChange}
85
137
  setDstChain={handleDstChainChange}
86
138
  onClose={() => setChainPickerModalOpen(false)}
@@ -90,8 +142,9 @@ export const ChainPanel = ({ chain, type }: Props) => {
90
142
 
91
143
  <button
92
144
  type="button"
93
- disabled={projectSelectedDestinationChain}
145
+ disabled={projectLockedDestinationChain || projectLockedSourceChain}
94
146
  onClick={() => setChainPickerModalOpen(true)}
147
+ className={`${className || ""}`}
95
148
  >
96
149
  <div className="flex justify-between items-center p-4 gap-8">
97
150
  <div className="flex flex-col font-medium items-start">
@@ -102,7 +155,7 @@ export const ChainPanel = ({ chain, type }: Props) => {
102
155
  {chain ? chain.displayName : "Select"}
103
156
  </p>
104
157
  </div>
105
- {!projectSelectedDestinationChain && (
158
+ {!projectLockedDestinationChain && !projectLockedSourceChain && (
106
159
  <div className="w-4 h-2 fill-t_text_primary">
107
160
  <ArrowDownIcon />
108
161
  </div>
@@ -49,7 +49,8 @@ export const TokenPicker = ({
49
49
  const currentRouteRequestNonce = useRef<number>(0);
50
50
  const tokenSearchRef = useRef<HTMLInputElement | null>(null);
51
51
 
52
- const { supportedChains, findTokenDataByAddressAndChain } = useSwapContext();
52
+ const { bridgeUI, supportedChains, findTokenDataByAddressAndChain } =
53
+ useSwapContext();
53
54
  const evmContractApi = useEvmContractApi();
54
55
 
55
56
  useEffect(() => {
@@ -131,6 +132,7 @@ export const TokenPicker = ({
131
132
 
132
133
  useEffect(() => {
133
134
  if (
135
+ !bridgeUI &&
134
136
  isETHAddressValid(searchValue) &&
135
137
  !findTokenDataByAddressAndChain(searchValue.toLowerCase(), chain.chainId)
136
138
  ) {
@@ -139,7 +141,13 @@ export const TokenPicker = ({
139
141
  setLoadingCustomTokenData(false);
140
142
  setCustomTokenData(null);
141
143
  }
142
- }, [searchValue, getCustomTokenData, findTokenDataByAddressAndChain, chain]);
144
+ }, [
145
+ bridgeUI,
146
+ searchValue,
147
+ getCustomTokenData,
148
+ findTokenDataByAddressAndChain,
149
+ chain,
150
+ ]);
143
151
 
144
152
  useEffect(() => {
145
153
  setSearchValue("");
@@ -10,10 +10,12 @@ type Props = {
10
10
  chain: Chain | undefined;
11
11
  token: Token | undefined;
12
12
  type: SwapPanelType;
13
+ className?: string;
13
14
  };
14
15
 
15
- export const TokenPanel = ({ chain, token, type }: Props) => {
16
+ export const TokenPanel = ({ chain, token, type, className }: Props) => {
16
17
  const {
18
+ bridgeUI,
17
19
  srcChainTokensOptions,
18
20
  srcChainOtherTokensOptions,
19
21
  dstChainTokensOptions,
@@ -23,12 +25,18 @@ export const TokenPanel = ({ chain, token, type }: Props) => {
23
25
  supportedChains,
24
26
  setDstToken,
25
27
  setDstChain,
26
- dstTokenProvided,
28
+ dstTokenLocked,
29
+ srcTokenLocked,
27
30
  } = useSwapContext();
28
31
 
29
- const projectSelectedDestinationToken = useMemo(
30
- () => type === "destination" && dstTokenProvided,
31
- [type, dstTokenProvided],
32
+ const integratorLockedDestinationToken = useMemo(
33
+ () => type === "destination" && dstTokenLocked,
34
+ [type, dstTokenLocked],
35
+ );
36
+
37
+ const integratorLockedSourceToken = useMemo(
38
+ () => type === "source" && srcTokenLocked,
39
+ [type, srcTokenLocked],
32
40
  );
33
41
 
34
42
  const [tokenPickerModalOpen, setTokenPickerModalOpen] = useState(false);
@@ -38,6 +46,11 @@ export const TokenPanel = ({ chain, token, type }: Props) => {
38
46
  [dstChainTokensOptions, srcChainTokensOptions, type],
39
47
  );
40
48
 
49
+ const pickerDisabled = useMemo(
50
+ () => type === "destination" && bridgeUI,
51
+ [bridgeUI, type],
52
+ );
53
+
41
54
  const otherTokensWithBalances = useMemo(
42
55
  () =>
43
56
  type === "source"
@@ -80,10 +93,15 @@ export const TokenPanel = ({ chain, token, type }: Props) => {
80
93
 
81
94
  <button
82
95
  type="button"
83
- disabled={projectSelectedDestinationToken}
96
+ disabled={
97
+ pickerDisabled ||
98
+ integratorLockedDestinationToken ||
99
+ integratorLockedSourceToken
100
+ }
84
101
  onClick={() => {
85
102
  setTokenPickerModalOpen(true);
86
103
  }}
104
+ className={`${className || ""}`}
87
105
  >
88
106
  <div className="flex justify-between items-center p-4 gap-8 border-r border-solid border-t_text_primary border-opacity-10">
89
107
  <div className="flex gap-2 items-center">
@@ -99,11 +117,13 @@ export const TokenPanel = ({ chain, token, type }: Props) => {
99
117
  </p>
100
118
  </div>
101
119
  </div>
102
- {!projectSelectedDestinationToken && (
103
- <div className="w-4 h-2 fill-t_text_primary">
104
- <ArrowDownIcon />
105
- </div>
106
- )}
120
+ {!pickerDisabled &&
121
+ !integratorLockedDestinationToken &&
122
+ !integratorLockedSourceToken && (
123
+ <div className="w-4 h-2 fill-t_text_primary">
124
+ <ArrowDownIcon />
125
+ </div>
126
+ )}
107
127
  </div>
108
128
  </button>
109
129
  </>
@@ -24,9 +24,14 @@ export const SwapPanel = ({ type }: Props) => {
24
24
 
25
25
  return (
26
26
  <div className="flex flex-col x-border rounded-xl bg-t_bg_tertiary bg-opacity-5">
27
- <div className="grid grid-cols-2 border-b border-solid border-t_text_primary border-opacity-10">
28
- <TokenPanel chain={chain} token={token} type={type} />
29
- <ChainPanel chain={chain} type={type} />
27
+ <div className="flex border-b border-solid border-t_text_primary border-opacity-10">
28
+ <TokenPanel
29
+ chain={chain}
30
+ token={token}
31
+ type={type}
32
+ className="w-full flex-grow"
33
+ />
34
+ <ChainPanel chain={chain} type={type} className="w-full max-w-[36%]" />
30
35
  </div>
31
36
  <div
32
37
  className={`flex justify-between ${