@xswap-link/sdk 0.14.0 → 0.15.0

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 (78) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/index.d.mts +4 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.global.js +92 -92
  5. package/dist/index.js +3208 -2862
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +3083 -2740
  8. package/dist/index.mjs.map +1 -1
  9. package/localTheme.ts +6 -4
  10. package/package.json +1 -1
  11. package/src/assets/icons/AdjustmentsIcon.tsx +5 -7
  12. package/src/assets/icons/ChainlinkMarkIcon.tsx +4 -5
  13. package/src/assets/icons/CheckCircleThinIcon.tsx +22 -0
  14. package/src/assets/icons/ChevronDownThinIcon.tsx +39 -0
  15. package/src/assets/icons/ChevronDownWideThinIcon.tsx +18 -0
  16. package/src/assets/icons/ChevronsUpDownThinIcon.tsx +21 -0
  17. package/src/assets/icons/ClockIcon.tsx +3 -7
  18. package/src/assets/icons/CloseIcon.tsx +9 -4
  19. package/src/assets/icons/ErrorCircleThinIcon.tsx +22 -0
  20. package/src/assets/icons/LinkExternalThinIcon.tsx +19 -0
  21. package/src/assets/icons/LoadingRingIcon.tsx +22 -0
  22. package/src/assets/icons/ProgressRingThinIcon.tsx +29 -0
  23. package/src/assets/icons/SearchThinIcon.tsx +15 -0
  24. package/src/assets/icons/SignThinIcon.tsx +15 -0
  25. package/src/assets/icons/WalletCheckThinIcon.tsx +24 -0
  26. package/src/assets/icons/WarningIcon.tsx +8 -5
  27. package/src/assets/icons/index.ts +11 -0
  28. package/src/components/Modal/index.tsx +14 -9
  29. package/src/components/PoweredBy/index.tsx +6 -8
  30. package/src/components/Swap/Header/index.tsx +2 -4
  31. package/src/components/Swap/HistoryView/ActivityCard/index.tsx +110 -115
  32. package/src/components/Swap/HistoryView/index.tsx +10 -7
  33. package/src/components/Swap/PickerView/index.tsx +151 -0
  34. package/src/components/Swap/ReorderButton/ReorderButton.tsx +4 -4
  35. package/src/components/Swap/SettingsView/Delivery/index.tsx +19 -17
  36. package/src/components/Swap/SettingsView/InfiniteApproval/index.tsx +19 -17
  37. package/src/components/Swap/SettingsView/Slippage/index.tsx +55 -49
  38. package/src/components/Swap/SwapView/ConfirmationView/TokenTiles/index.tsx +69 -0
  39. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Header/index.tsx +13 -25
  40. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Steps/index.tsx +65 -113
  41. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +68 -26
  42. package/src/components/Swap/SwapView/ConfirmationView/TxResult/index.tsx +51 -74
  43. package/src/components/Swap/SwapView/FeesPanel/DeliveryInfo/index.tsx +1 -1
  44. package/src/components/Swap/SwapView/FeesPanel/index.tsx +5 -5
  45. package/src/components/Swap/SwapView/SwapButton/index.tsx +2 -31
  46. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/Balance/index.tsx +2 -2
  47. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +2 -2
  48. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/ChainPicker/index.tsx +143 -0
  49. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/NetworkFilter/index.tsx +19 -16
  50. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +11 -4
  51. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +185 -105
  52. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +54 -120
  53. package/src/components/Swap/SwapView/SwapPanel/UsdValue/index.tsx +2 -2
  54. package/src/components/Swap/SwapView/SwapPanel/WalletPanel/index.tsx +10 -16
  55. package/src/components/Swap/SwapView/SwapPanel/index.tsx +4 -4
  56. package/src/components/Swap/SwapView/WalletPicker/index.tsx +131 -139
  57. package/src/components/Swap/SwapView/index.tsx +11 -7
  58. package/src/components/Swap/WalletPickerView/index.tsx +65 -0
  59. package/src/components/Swap/index.tsx +39 -8
  60. package/src/components/ToggleButton/index.tsx +5 -7
  61. package/src/components/TokenLogo/index.tsx +1 -1
  62. package/src/components/TxConfigForm/index.tsx +2 -2
  63. package/src/components/TxModal/index.tsx +14 -4
  64. package/src/components/global.css +18 -1
  65. package/src/constants/index.ts +4 -0
  66. package/src/context/HistoryProvider.tsx +1 -1
  67. package/src/context/SwapProvider.tsx +193 -81
  68. package/src/context/TxUIWrapper.tsx +3 -3
  69. package/src/hooks/useEvmContractApi.ts +28 -4
  70. package/src/models/payloads/GetBalancesPayload.ts +1 -0
  71. package/src/services/api.ts +37 -0
  72. package/src/utils/index.ts +27 -1
  73. package/src/utils/tokens.ts +20 -1
  74. package/src/utils/validation.ts +9 -4
  75. package/tailwind.config.js +4 -1
  76. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/ArrowIcon.tsx +0 -13
  77. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/SwapPanel/index.tsx +0 -72
  78. package/src/components/Swap/SwapView/ConfirmationView/TxResult/TokenItem/index.tsx +0 -25
@@ -1,14 +1,10 @@
1
1
  import {
2
- ArrowDownLongIcon,
3
- CloseIcon,
4
- ErrorModalStatusIcon,
5
- LinkExternalIcon,
6
- SuccessModalStatusIcon,
2
+ CheckCircleThinIcon,
3
+ ErrorCircleThinIcon,
4
+ LinkExternalThinIcon,
7
5
  } from "@src/assets/icons";
8
6
  import { useSwapContext, useTxUIWrapper } from "@src/context";
9
- import { TokenItem } from "./TokenItem";
10
- import { useMemo } from "react";
11
- import { Ecosystem } from "@src/models";
7
+ import { TokenTiles } from "../TokenTiles";
12
8
 
13
9
  type Props = {
14
10
  onCloseClick: () => void;
@@ -16,79 +12,60 @@ type Props = {
16
12
 
17
13
  export const TxResult = ({ onCloseClick }: Props) => {
18
14
  const { txError, txExplorerUrl, txMsg } = useTxUIWrapper();
19
- const {
20
- isExpressDeliveryActive,
21
- srcChain,
22
- srcToken,
23
- dstChain,
24
- dstToken,
25
- srcValue,
26
- dstValue,
27
- bridgeUI,
28
- } = useSwapContext();
29
-
30
- const value = useMemo(() => {
31
- if (bridgeUI && srcChain?.ecosystem === Ecosystem.SOLANA) {
32
- return srcValue;
33
- }
34
- return dstValue;
35
- }, [bridgeUI, srcChain?.ecosystem, srcValue, dstValue]);
15
+ const { isExpressDeliveryActive, srcChain, dstChain } = useSwapContext();
36
16
 
37
17
  return (
38
- <div className="flex flex-col gap-5 text-t_text_primary justify-center items-center">
39
- {txError ? (
40
- <>
41
- <div
42
- className="flex justify-end fill-t_text_primary cursor-pointer w-full"
43
- onClick={onCloseClick}
44
- >
45
- <CloseIcon className="w-3.5 h-3.5" />
46
- </div>
47
- <ErrorModalStatusIcon />
48
- <div className="text-2xl font-bold">Transaction failed</div>
49
- <div className="text-center">{txError}</div>
50
- </>
51
- ) : (
52
- <>
53
- <div
54
- className="flex justify-end fill-t_text_primary cursor-pointer w-full"
55
- onClick={onCloseClick}
56
- >
57
- <CloseIcon className="w-3.5 h-3.5" />
18
+ <div className="flex flex-col gap-8 text-t_text_primary">
19
+ <div className="flex flex-col gap-2">
20
+ <div className="flex items-center gap-3">
21
+ <div className="text-2xl leading-8 tracking-[0.01em]">
22
+ {txError ? "Transaction Error" : "Transaction Success"}
58
23
  </div>
59
- <SuccessModalStatusIcon />
60
- <div className="flex flex-col items-center">
61
- <div className="text-2xl font-bold">Transaction success!</div>
62
- {!txMsg && srcChain !== dstChain && (
63
- <div className="text-sm font-normal opacity-60">
64
- {`Approximated time of delivery: ${
65
- isExpressDeliveryActive ? "30 sec" : "30 min"
66
- }`}
67
- </div>
68
- )}
24
+ {txError ? (
25
+ <ErrorCircleThinIcon className="w-6 h-6 shrink-0 text-[#F44336]" />
26
+ ) : (
27
+ <CheckCircleThinIcon className="w-6 h-6 shrink-0 text-[#6AE89B]" />
28
+ )}
29
+ </div>
30
+ {txError ? (
31
+ <div className="text-sm leading-5 tracking-[0.01em] text-t_text_primary text-opacity-50">
32
+ {txError}
69
33
  </div>
70
- {!txMsg && (
71
- <div className="flex items-center gap-2">
72
- <TokenItem chain={srcChain} token={srcToken} value={srcValue} />
73
- <div className="-rotate-90 flex justify-center w-8 fill-t_text_primary opacity-50">
74
- <ArrowDownLongIcon />
75
- </div>
76
- <TokenItem chain={dstChain} token={dstToken} value={value} />
34
+ ) : (
35
+ !txMsg &&
36
+ srcChain !== dstChain && (
37
+ <div className="text-sm leading-5 tracking-[0.01em] text-t_text_primary text-opacity-50">
38
+ {`Approximated time of delivery: ${
39
+ isExpressDeliveryActive ? "30 sec" : "30 min"
40
+ }`}
77
41
  </div>
78
- )}
79
- <a
80
- href={txExplorerUrl}
81
- target="_blank"
82
- rel="noreferrer"
83
- className="flex gap-4 items-center mb-2 fill-t_main_accent_light"
84
- >
85
- <p className="text-t_text_secondary underline">
42
+ )
43
+ )}
44
+ </div>
45
+ {!txMsg && (
46
+ <div className="flex flex-col gap-4">
47
+ <TokenTiles />
48
+ {!txError && (
49
+ <a
50
+ href={txExplorerUrl}
51
+ target="_blank"
52
+ rel="noreferrer"
53
+ className="flex items-center gap-2 self-start text-sm leading-5 tracking-[0.01em] text-t_text_primary text-opacity-50 hover:text-opacity-80 transition-colors"
54
+ >
86
55
  View details on Explorer
87
- </p>
88
- <LinkExternalIcon />
89
- </a>
90
- </>
56
+ <LinkExternalThinIcon className="w-3 h-3" />
57
+ </a>
58
+ )}
59
+ </div>
91
60
  )}
61
+ <div className="h-px w-full gradient-sections-separator-container" />
62
+ <button
63
+ type="button"
64
+ onClick={onCloseClick}
65
+ className="w-full px-6 py-3 rounded-lg border-none cursor-pointer bg-t_bg_tertiary bg-opacity-5 text-sm font-medium leading-6 tracking-[0.01em] text-t_text_primary text-opacity-80 hover:bg-opacity-10 transition-colors"
66
+ >
67
+ Continue
68
+ </button>
92
69
  </div>
93
70
  );
94
71
  };
@@ -11,7 +11,7 @@ export const DeliveryInfo = () => {
11
11
  : "text-t_text_primary opacity-50"
12
12
  }`}
13
13
  >
14
- {isExpressDeliveryActive ? "Max. Time 30 Sec." : "Max. Time 30 Min."}
14
+ {isExpressDeliveryActive ? "Est. Time 30 Sec." : "Est. Time 30 Min."}
15
15
  </span>
16
16
  );
17
17
  };
@@ -1,4 +1,4 @@
1
- import { CaretDownIcon } from "@src/assets/icons";
1
+ import { ChevronDownThinIcon } from "@src/assets/icons";
2
2
  import { Skeleton } from "@src/components";
3
3
  import { useSwapContext } from "@src/context";
4
4
  import { safeBigNumberFrom, weiToHumanReadable } from "@src/utils";
@@ -176,7 +176,7 @@ export const FeesPanel = () => {
176
176
  isFetchingSolanaFee);
177
177
 
178
178
  return (
179
- <div className="flex flex-col rounded-xl py-2 px-4 sm:px-6 bg-gradient-to-r from-t_bg_tertiary/[0.02] to-t_bg_tertiary/0">
179
+ <div className="flex flex-col rounded-xl py-3 px-4 sm:px-6 bg-gradient-to-r from-t_bg_tertiary/[0.02] to-t_bg_tertiary/0">
180
180
  <button
181
181
  aria-label={`${expanded ? "Collapse" : "Expand"} fees panel`}
182
182
  type="button"
@@ -195,17 +195,17 @@ export const FeesPanel = () => {
195
195
  )}
196
196
  {route && !isLoading && srcChain?.chainId !== dstChain?.chainId && (
197
197
  <>
198
- <div className="w-0.5 h-0.5 bg-t_text_primary opacity-25" />
198
+ <span className="text-t_text_primary opacity-50">/</span>
199
199
  <DeliveryInfo />
200
200
  </>
201
201
  )}
202
202
  </div>
203
203
  <div
204
- className={`flex items-center justify-center w-6 h-6 fill-t_text_primary opacity-75 transition-transform [&_svg]:w-2.5 [&_svg]:h-[5px] ${
204
+ className={`flex items-center justify-center w-6 h-6 text-t_text_primary opacity-50 transition-transform ${
205
205
  expanded ? "rotate-180" : ""
206
206
  }`}
207
207
  >
208
- <CaretDownIcon />
208
+ <ChevronDownThinIcon />
209
209
  </div>
210
210
  </div>
211
211
  {expanded && <Fees solanaBridgeFee={solanaBridgeFee} />}
@@ -1,4 +1,3 @@
1
- import { Modal } from "@src/components/Modal";
2
1
  import { useSwapContext, useTxUIWrapper } from "@src/context";
3
2
  import { useWallet } from "@src/context/WalletProvider";
4
3
  import { ADDRESSES } from "@src/contracts";
@@ -10,7 +9,6 @@ import { safeBigNumberFrom } from "@src/utils";
10
9
  import { BigNumber } from "ethers";
11
10
  import { useCallback, useEffect, useMemo, useState } from "react";
12
11
  import { useAccount, useSwitchChain } from "wagmi";
13
- import { WalletPicker } from "../WalletPicker";
14
12
 
15
13
  type Button = {
16
14
  text: string;
@@ -53,11 +51,8 @@ export const SwapButton = () => {
53
51
  } = useNetworks();
54
52
 
55
53
  const {
56
- openEVMWalletModal,
57
54
  setOpenEVMWalletModal,
58
- openSolanaWalletModal,
59
55
  setOpenSolanaWalletModal,
60
- openSuiWalletModal,
61
56
  setOpenSuiWalletModal,
62
57
  solana,
63
58
  sui,
@@ -314,38 +309,14 @@ export const SwapButton = () => {
314
309
 
315
310
  return (
316
311
  <>
317
- {openEVMWalletModal && (
318
- <Modal onClose={() => setOpenEVMWalletModal(false)}>
319
- <WalletPicker
320
- ecosystem={Ecosystem.EVM}
321
- onClose={() => setOpenEVMWalletModal(false)}
322
- />
323
- </Modal>
324
- )}
325
- {openSolanaWalletModal && (
326
- <Modal onClose={() => setOpenSolanaWalletModal(false)}>
327
- <WalletPicker
328
- ecosystem={Ecosystem.SOLANA}
329
- onClose={() => setOpenSolanaWalletModal(false)}
330
- />
331
- </Modal>
332
- )}
333
- {openSuiWalletModal && (
334
- <Modal onClose={() => setOpenSuiWalletModal(false)}>
335
- <WalletPicker
336
- ecosystem={Ecosystem.SUI}
337
- onClose={() => setOpenSuiWalletModal(false)}
338
- />
339
- </Modal>
340
- )}
341
312
  <button
342
313
  type="button"
343
314
  disabled={button.disabled}
344
315
  onClick={button.fn}
345
- className={`text-sm font-medium leading-6 tracking-[0.01em] w-full px-6 py-3 cursor-pointer disabled:cursor-not-allowed rounded-[10px] border-none transition-opacity ${
316
+ className={`text-base font-medium leading-6 tracking-[0.01em] w-full px-6 py-3 cursor-pointer disabled:cursor-not-allowed rounded-lg border-none transition-[filter] ${
346
317
  button.disabled
347
318
  ? "bg-t_button_pr_off_bg text-t_button_pr_off_text"
348
- : "bg-gradient-to-br from-t_main_accent_light to-t_main_accent_dark text-t_button_pr_text hover:opacity-90"
319
+ : "bg-gradient-to-b from-t_main_accent_light from-50% to-t_main_accent_dark text-t_button_pr_text hover:brightness-105"
349
320
  }`}
350
321
  >
351
322
  {button.text}
@@ -55,9 +55,9 @@ export const Balance = ({ type }: Props) => {
55
55
  ) : (
56
56
  <p className="text-[10px] leading-4 text-right whitespace-nowrap">
57
57
  <span className="text-t_text_primary text-opacity-50">
58
- {`Balance `}
58
+ {`Balance. `}
59
59
  </span>
60
- <span className="text-t_text_primary text-opacity-75">
60
+ <span className="text-t_text_primary text-opacity-75 tracking-[0.032em]">
61
61
  {`${balanceInfo} ${token.symbol}`}
62
62
  </span>
63
63
  </p>
@@ -25,7 +25,7 @@ export const AmountPanel = ({ type }: Props) => {
25
25
  } = useSwapContext();
26
26
 
27
27
  const onMaxClick = () => {
28
- if (!srcTokenBalanceWei) {
28
+ if (!srcToken || !srcTokenBalanceWei) {
29
29
  return;
30
30
  }
31
31
  setSrcValue(
@@ -69,7 +69,7 @@ export const AmountPanel = ({ type }: Props) => {
69
69
  value={srcValue}
70
70
  onChange={(value) => setSrcValue(value)}
71
71
  />
72
- {!!srcTokenBalanceWei && (
72
+ {!!srcToken && !!srcTokenBalanceWei && (
73
73
  <button
74
74
  type="button"
75
75
  onClick={onMaxClick}
@@ -0,0 +1,143 @@
1
+ import { CloseIcon, SearchThinIcon, WarningIcon } from "@src/assets/icons";
2
+ import { Chain } from "@src/models";
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
+ import { Virtuoso } from "react-virtuoso";
5
+
6
+ type ChainOption = Chain & {
7
+ disabled?: boolean;
8
+ };
9
+
10
+ type Props = {
11
+ chains: ChainOption[];
12
+ selectedChainId?: string;
13
+ /**
14
+ * Chain occupied by the other side of the bridge — picking it requires
15
+ * resetting the source, same as picking an unreachable chain.
16
+ */
17
+ sameChainId?: string;
18
+ onSelect: (chain: ChainOption) => void;
19
+ onClose: () => void;
20
+ };
21
+
22
+ /**
23
+ * Network picker used in bridge mode, where the destination token is
24
+ * derived from the source token and only the chain gets picked by the user.
25
+ */
26
+ export const ChainPicker = ({
27
+ chains,
28
+ selectedChainId,
29
+ sameChainId,
30
+ onSelect,
31
+ onClose,
32
+ }: Props) => {
33
+ const [searchValue, setSearchValue] = useState("");
34
+ const chainSearchRef = useRef<HTMLInputElement | null>(null);
35
+
36
+ useEffect(() => {
37
+ setTimeout(() => {
38
+ chainSearchRef.current?.focus();
39
+ }, 10); // delay focus a tiny bit to ensure it works
40
+ }, []);
41
+
42
+ const filteredChains = useMemo(() => {
43
+ if (!searchValue) return chains;
44
+ const searchLower = searchValue.toLowerCase();
45
+ return chains.filter(
46
+ (chain) =>
47
+ chain.displayName.toLowerCase().includes(searchLower) ||
48
+ chain.chainId.toLowerCase().includes(searchLower),
49
+ );
50
+ }, [chains, searchValue]);
51
+
52
+ // The current source cannot reach this chain — picking it resets the source.
53
+ const requiresSourceReset = (chain: ChainOption) =>
54
+ !!chain.disabled || chain.chainId === sameChainId;
55
+
56
+ return (
57
+ <>
58
+ <div className="flex justify-between items-center mb-6">
59
+ <div className="text-2xl leading-8 tracking-[0.01em] text-t_text_primary">
60
+ Select Network
61
+ </div>
62
+ <button
63
+ type="button"
64
+ aria-label="Close"
65
+ className="flex items-center justify-center w-8 h-8 rounded-md bg-t_bg_tertiary bg-opacity-5 hover:bg-opacity-10 transition-colors"
66
+ onClick={onClose}
67
+ >
68
+ <div className="w-4 h-4 opacity-50 fill-t_text_primary flex items-center justify-center">
69
+ <CloseIcon />
70
+ </div>
71
+ </button>
72
+ </div>
73
+
74
+ <div className="flex items-center gap-2 h-14 px-4 rounded-xl bg-t_bg_tertiary bg-opacity-[0.03] border border-solid border-t_text_primary border-opacity-5 mb-6">
75
+ <div className="text-t_text_primary opacity-60 flex items-center shrink-0">
76
+ <SearchThinIcon />
77
+ </div>
78
+ <input
79
+ ref={chainSearchRef}
80
+ value={searchValue}
81
+ onChange={(e) => setSearchValue(e.target.value)}
82
+ placeholder="Search network name"
83
+ className="bg-transparent border-none outline-none w-full text-sm leading-6 text-t_text_primary placeholder:text-t_text_primary placeholder:text-opacity-40"
84
+ />
85
+ </div>
86
+
87
+ <div className="flex flex-col mx-0 h-full min-h-0 flex-1">
88
+ {filteredChains.length ? (
89
+ <Virtuoso
90
+ style={{ maxHeight: "700px", height: "100%" }}
91
+ data={filteredChains}
92
+ itemContent={(_, chain) => {
93
+ const resetWarning = requiresSourceReset(chain);
94
+ const isSelected = chain.chainId === selectedChainId;
95
+ return (
96
+ <div className="pb-2">
97
+ <div
98
+ onClick={() => onSelect(chain)}
99
+ className={`flex gap-3 items-center p-3 sm:p-4 rounded-xl cursor-pointer bg-gradient-to-r from-t_bg_tertiary/[0.02] to-t_bg_tertiary/[0.02] bg-[rgba(40,40,40,0.15)] hover:bg-t_bg_tertiary hover:bg-opacity-10 transition-colors ${
100
+ isSelected ? "!bg-t_bg_tertiary !bg-opacity-10" : ""
101
+ }`}
102
+ >
103
+ <img
104
+ src={chain.image}
105
+ alt={chain.displayName}
106
+ className={`w-10 h-10 rounded-full shrink-0 ${
107
+ resetWarning ? "opacity-40" : ""
108
+ }`}
109
+ />
110
+ <div className="flex justify-between items-center gap-2 w-full min-w-0">
111
+ <p
112
+ className={`text-sm font-medium leading-5 tracking-[0.01em] text-t_text_primary ${
113
+ resetWarning ? "opacity-40" : ""
114
+ }`}
115
+ >
116
+ {chain.displayName}
117
+ </p>
118
+ {resetWarning && (
119
+ <div className="flex items-center justify-end gap-1.5 text-[#D77F02]">
120
+ <WarningIcon className="w-3.5 h-3.5 shrink-0" />
121
+ <span className="text-xs leading-[18px] tracking-[0.01em] whitespace-nowrap">
122
+ <span className="sm:hidden">Source will reset.</span>
123
+ <span className="hidden sm:inline">
124
+ Source will reset after selection.
125
+ </span>
126
+ </span>
127
+ </div>
128
+ )}
129
+ </div>
130
+ </div>
131
+ </div>
132
+ );
133
+ }}
134
+ />
135
+ ) : (
136
+ <div className="text-center py-1 text-sm text-t_text_primary text-opacity-50">
137
+ No network found
138
+ </div>
139
+ )}
140
+ </div>
141
+ </>
142
+ );
143
+ };
@@ -1,4 +1,4 @@
1
- import { ChevronDownIcon, SearchIcon } from "@src/assets/icons";
1
+ import { ChevronDownThinIcon, SearchThinIcon } from "@src/assets/icons";
2
2
  import { useSwapContext } from "@src/context";
3
3
  import { SwapPanelType } from "@src/components/Swap/SwapView";
4
4
  import { Chain } from "@src/models";
@@ -56,14 +56,15 @@ export const NetworkFilter = ({ type, selectedChain, onSelect }: Props) => {
56
56
  return () => document.removeEventListener("mousedown", onPointerDown);
57
57
  }, []);
58
58
 
59
- const previewChains = chains.slice(0, 4);
59
+ // 2x2 icon cluster shown when no network is picked
60
+ const clusterChains = chains.slice(0, 4);
60
61
 
61
62
  return (
62
63
  <div className="relative" ref={containerRef}>
63
64
  <button
64
65
  type="button"
65
66
  onClick={() => setOpen((state) => !state)}
66
- className="flex items-center gap-2 h-12 px-3 py-2 rounded-lg bg-t_bg_tertiary bg-opacity-[0.04] hover:bg-opacity-10 transition-colors"
67
+ className="flex items-center gap-2.5 h-12 px-3 py-2 rounded-lg bg-t_bg_tertiary bg-opacity-[0.04] hover:bg-opacity-10 transition-colors"
67
68
  >
68
69
  {selectedChain ? (
69
70
  <img
@@ -72,8 +73,8 @@ export const NetworkFilter = ({ type, selectedChain, onSelect }: Props) => {
72
73
  className="w-5 h-5 rounded-full"
73
74
  />
74
75
  ) : (
75
- <div className="grid grid-cols-2 gap-0.5 w-6 shrink-0">
76
- {previewChains.map((chain) => (
76
+ <div className="grid grid-cols-2 gap-0.5 shrink-0">
77
+ {clusterChains.map((chain) => (
77
78
  <img
78
79
  key={chain.chainId}
79
80
  src={chain.image}
@@ -84,22 +85,24 @@ export const NetworkFilter = ({ type, selectedChain, onSelect }: Props) => {
84
85
  </div>
85
86
  )}
86
87
  <p className="text-sm leading-6 tracking-[0.01em] text-t_text_primary whitespace-nowrap max-w-[120px] truncate">
87
- {selectedChain ? selectedChain.displayName : "All Networks"}
88
+ {selectedChain
89
+ ? selectedChain.displayName
90
+ : `${chains.length} Networks`}
88
91
  </p>
89
92
  <div
90
- className={`flex items-center justify-center w-6 h-6 fill-t_text_primary opacity-50 transition-transform [&_svg]:w-3 [&_svg]:h-3 ${
93
+ className={`flex items-center justify-center w-6 h-6 text-t_text_primary opacity-50 transition-transform ${
91
94
  open ? "rotate-180" : ""
92
95
  }`}
93
96
  >
94
- <ChevronDownIcon />
97
+ <ChevronDownThinIcon />
95
98
  </div>
96
99
  </button>
97
100
 
98
101
  {open && (
99
102
  <div className="absolute right-0 top-[calc(100%+8px)] z-20 flex flex-col w-[220px] max-h-[280px] rounded-xl bg-t_bg_primary border border-solid border-t_text_primary border-opacity-10 backdrop-blur-[24px] overflow-hidden shadow-lg">
100
103
  <div className="flex items-center gap-2 px-4 py-3 border-b border-solid border-t_text_primary border-opacity-10">
101
- <div className="fill-t_text_primary opacity-50 [&_svg]:w-3.5 [&_svg]:h-3.5 flex items-center">
102
- <SearchIcon />
104
+ <div className="text-t_text_primary opacity-60 [&_svg]:w-3.5 [&_svg]:h-3.5 flex items-center">
105
+ <SearchThinIcon />
103
106
  </div>
104
107
  <input
105
108
  value={searchValue}
@@ -132,13 +135,13 @@ export const NetworkFilter = ({ type, selectedChain, onSelect }: Props) => {
132
135
  className="w-5 h-5 rounded-full"
133
136
  />
134
137
  ) : (
135
- <div className="grid grid-cols-2 gap-px w-5 shrink-0">
136
- {previewChains.map((previewChain) => (
138
+ <div className="grid grid-cols-2 gap-0.5 shrink-0">
139
+ {clusterChains.map((clusterChain) => (
137
140
  <img
138
- key={previewChain.chainId}
139
- src={previewChain.image}
140
- alt={previewChain.displayName}
141
- className="w-[9px] h-[9px] rounded-full"
141
+ key={clusterChain.chainId}
142
+ src={clusterChain.image}
143
+ alt={clusterChain.displayName}
144
+ className="w-[11px] h-[11px] rounded-full"
142
145
  />
143
146
  ))}
144
147
  </div>
@@ -69,7 +69,7 @@ export const TokenItem = ({
69
69
  return (
70
70
  <div className="pb-2">
71
71
  <div
72
- className={`flex gap-3 items-center p-3 sm:p-4 rounded-xl cursor-pointer bg-gradient-to-r from-t_bg_tertiary/[0.02] to-t_bg_tertiary/[0.02] bg-t_bg_secondary bg-opacity-15 hover:bg-t_bg_tertiary hover:bg-opacity-10 transition-colors ${
72
+ className={`flex gap-3 items-center p-3 sm:p-4 rounded-xl cursor-pointer bg-gradient-to-r from-t_bg_tertiary/[0.02] to-t_bg_tertiary/[0.02] bg-[rgba(40,40,40,0.15)] hover:bg-t_bg_tertiary hover:bg-opacity-10 transition-colors ${
73
73
  isSelected ? "!bg-t_bg_tertiary !bg-opacity-10" : ""
74
74
  }`}
75
75
  onClick={onClick}
@@ -142,9 +142,16 @@ export const TokenItem = ({
142
142
  <div className="flex items-center justify-end gap-1.5 text-[#D77F02]">
143
143
  <WarningIcon className="w-3.5 h-3.5 shrink-0" />
144
144
  <span className="text-xs leading-[18px] tracking-[0.01em] whitespace-nowrap">
145
- {resetWarning === "source"
146
- ? "Source will reset after selection."
147
- : "Destination will reset after selection."}
145
+ <span className="sm:hidden">
146
+ {resetWarning === "source"
147
+ ? "Source will reset."
148
+ : "Destination will reset."}
149
+ </span>
150
+ <span className="hidden sm:inline">
151
+ {resetWarning === "source"
152
+ ? "Source will reset after selection."
153
+ : "Destination will reset after selection."}
154
+ </span>
148
155
  </span>
149
156
  </div>
150
157
  ) : (