@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,11 +1,9 @@
1
- import { ChevronDownIcon } from "@src/assets/icons";
2
- import { Modal, TokenLogoWithChain } from "@src/components";
1
+ import { ChevronDownThinIcon } from "@src/assets/icons";
2
+ import { TokenLogoWithChain } from "@src/components";
3
3
  import { useSwapContext } from "@src/context";
4
- import { Chain, Token, TokenOption } from "@src/models";
5
- import { isChainConnectionSupported } from "@src/utils";
6
- import { useMemo, useState } from "react";
4
+ import { Chain, Token } from "@src/models";
5
+ import { useMemo } from "react";
7
6
  import { SwapPanelType } from "../../index";
8
- import { TokenPicker } from "./TokenPicker";
9
7
 
10
8
  type Props = {
11
9
  chain: Chain | undefined;
@@ -17,19 +15,10 @@ type Props = {
17
15
  export const TokenPanel = ({ chain, token, type, className }: Props) => {
18
16
  const {
19
17
  bridgeUI,
20
- srcChain,
21
- dstChain,
22
- srcChainTokensOptions,
23
- srcChainOtherTokensOptions,
24
- dstChainTokensOptions,
25
- dstChainOtherTokensOptions,
26
- setSrcToken,
27
- setSrcChain,
28
- supportedChains,
29
- setDstToken,
30
- setDstChain,
31
18
  dstTokenLocked,
32
19
  srcTokenLocked,
20
+ dstChainLocked,
21
+ setActivePicker,
33
22
  } = useSwapContext();
34
23
 
35
24
  const integratorLockedDestinationToken = useMemo(
@@ -42,120 +31,65 @@ export const TokenPanel = ({ chain, token, type, className }: Props) => {
42
31
  [type, srcTokenLocked],
43
32
  );
44
33
 
45
- const [tokenPickerModalOpen, setTokenPickerModalOpen] = useState(false);
46
-
47
- const tokensWithBalances = useMemo(
48
- () => (type === "source" ? srcChainTokensOptions : dstChainTokensOptions),
49
- [dstChainTokensOptions, srcChainTokensOptions, type],
50
- );
51
-
52
- const pickerDisabled = useMemo(
34
+ // In bridge mode the destination token is derived from the source token,
35
+ // so the destination panel picks a chain instead of a token.
36
+ const bridgeDestination = useMemo(
53
37
  () => type === "destination" && bridgeUI,
54
38
  [bridgeUI, type],
55
39
  );
56
40
 
57
- const otherTokensWithBalances = useMemo(
58
- () =>
59
- type === "source"
60
- ? srcChainOtherTokensOptions
61
- : dstChainOtherTokensOptions,
62
- [dstChainOtherTokensOptions, srcChainOtherTokensOptions, type],
63
- );
64
-
65
- const onTokenSelect = (token: TokenOption) => {
66
- const newChain = supportedChains.find(
67
- (chain) => chain.chainId === token.chainId,
68
- );
69
-
70
- if (type === "source") {
71
- setSrcToken(token);
72
- setSrcChain(newChain);
73
- // The new source chain cannot reach the current destination —
74
- // reset the destination so the user picks a reachable one.
75
- if (
76
- dstChain &&
77
- !isChainConnectionSupported(newChain, dstChain.chainId, bridgeUI)
78
- ) {
79
- setDstChain(undefined);
80
- setDstToken(undefined);
81
- }
82
- } else {
83
- setDstToken(token);
84
- setDstChain(newChain);
85
- // The current source cannot reach the new destination chain —
86
- // reset the source so the user picks a reachable one.
87
- if (
88
- srcChain &&
89
- newChain &&
90
- !isChainConnectionSupported(srcChain, newChain.chainId, bridgeUI)
91
- ) {
92
- setSrcChain(undefined);
93
- setSrcToken(undefined);
94
- }
95
- }
96
- setTokenPickerModalOpen(false);
97
- };
98
-
99
41
  const locked =
100
- pickerDisabled ||
42
+ (bridgeDestination && dstChainLocked) ||
101
43
  integratorLockedDestinationToken ||
102
44
  integratorLockedSourceToken;
103
45
 
104
46
  return (
105
- <>
106
- {tokenPickerModalOpen && (
107
- <Modal onClose={() => setTokenPickerModalOpen(false)} className="z-[3]">
108
- <TokenPicker
109
- type={type}
110
- chain={chain!}
111
- tokens={tokensWithBalances}
112
- otherTokens={otherTokensWithBalances}
113
- selectedTokenAddress={token?.address}
114
- onSelect={onTokenSelect}
115
- isOpen={tokenPickerModalOpen}
116
- setModalOpen={setTokenPickerModalOpen}
47
+ <button
48
+ type="button"
49
+ disabled={locked}
50
+ onClick={() => {
51
+ setActivePicker(type);
52
+ }}
53
+ className={`flex items-center shrink-0 min-h-11 rounded-lg ${
54
+ locked ? "" : "hover:opacity-80 transition-opacity"
55
+ } ${className || ""}`}
56
+ >
57
+ <div className="flex items-center gap-3">
58
+ <p
59
+ className={`text-sm font-medium leading-6 tracking-[0.01em] text-t_text_primary ${
60
+ token ? "" : "opacity-60"
61
+ }`}
62
+ >
63
+ {token
64
+ ? token.symbol
65
+ : bridgeDestination && chain
66
+ ? chain.displayName
67
+ : "Select"}
68
+ </p>
69
+ {token ? (
70
+ <TokenLogoWithChain
71
+ token={token}
72
+ chain={chain}
73
+ tokenLogoClassName="w-10 h-10"
74
+ generatedLogoClassName="w-10 h-10 text-[16px]"
75
+ chainLogoClassName="w-[18px]"
117
76
  />
118
- </Modal>
119
- )}
120
-
121
- <button
122
- type="button"
123
- disabled={locked}
124
- onClick={() => {
125
- setTokenPickerModalOpen(true);
126
- }}
127
- className={`flex items-center gap-1 shrink-0 rounded-lg ${
128
- locked ? "" : "hover:opacity-80 transition-opacity"
129
- } ${className || ""}`}
130
- >
131
- <div className="flex items-center gap-3">
132
- <p
133
- className={`text-sm font-medium leading-6 tracking-[0.01em] text-t_text_primary ${
134
- token ? "" : "opacity-60"
135
- }`}
136
- >
137
- {token
138
- ? token.symbol
139
- : type === "destination" && bridgeUI
140
- ? "-"
141
- : "Select"}
142
- </p>
143
- {token && (
144
- <TokenLogoWithChain
145
- token={token}
146
- chain={chain}
147
- tokenLogoClassName="w-10 h-10"
148
- generatedLogoClassName="w-10 h-10 text-[16px]"
149
- chainLogoClassName="w-[18px]"
77
+ ) : (
78
+ bridgeDestination &&
79
+ chain && (
80
+ <img
81
+ src={chain.image}
82
+ alt={chain.displayName}
83
+ className="w-10 h-10 rounded-full"
150
84
  />
151
- )}
152
- </div>
153
- {!locked && (
154
- <div className="flex items-center justify-center w-6 h-6 fill-t_text_primary opacity-50 [&_svg]:w-3 [&_svg]:h-3">
155
- <ChevronDownIcon />
156
- </div>
85
+ )
157
86
  )}
158
- </button>
159
- </>
87
+ </div>
88
+ {!locked && (
89
+ <div className="flex items-center justify-center w-6 h-6 text-t_text_primary opacity-50">
90
+ <ChevronDownThinIcon />
91
+ </div>
92
+ )}
93
+ </button>
160
94
  );
161
95
  };
@@ -16,11 +16,11 @@ export const UsdValue = ({ type }: Props) => {
16
16
  );
17
17
 
18
18
  if (type === "destination" && isFetchingRoute) {
19
- return <Skeleton className="w-[80px] h-[16px]" />;
19
+ return <Skeleton className="w-[80px] h-[16px] my-1" />;
20
20
  }
21
21
 
22
22
  return (
23
- <p className="text-[10px] leading-4 tracking-[0.01em] text-t_text_primary text-opacity-50 whitespace-nowrap">
23
+ <p className="py-1 text-[10px] leading-4 tracking-[0.01em] text-t_text_primary text-opacity-50 whitespace-nowrap">
24
24
  {valueUsd ? `${valueUsd} USD` : ""}
25
25
  </p>
26
26
  );
@@ -1,4 +1,4 @@
1
- import { ChevronDownIcon } from "@src/assets/icons";
1
+ import { ChevronDownThinSmallIcon } from "@src/assets/icons";
2
2
  import { useWallet } from "@src/context/WalletProvider";
3
3
  import { useSwapContext } from "@src/context";
4
4
  import { SwapPanelType } from "../..";
@@ -27,19 +27,18 @@ export const WalletPanel = ({ chain, className }: Props) => {
27
27
  const openEvmWalletModal = useCallback(() => {
28
28
  // Mirror SwapButton's contract: when the integrator supplied their own
29
29
  // connect handler and did not opt into the built-in picker, defer to it
30
- // (e.g. the host dapp's RainbowKit modal) instead of opening the SDK picker.
30
+ // in every state — the host decides whether to show its connect modal or
31
+ // its account modal (e.g. RainbowKit) for an already-connected wallet.
31
32
  if (!integrationConfig.defaultWalletPicker && hasOnConnectWalletCallback) {
32
33
  onConnectWallet();
33
34
  return;
34
35
  }
35
36
  setOpenEVMWalletModal(true);
36
- evm.connectWallet();
37
37
  }, [
38
38
  integrationConfig.defaultWalletPicker,
39
39
  hasOnConnectWalletCallback,
40
40
  onConnectWallet,
41
41
  setOpenEVMWalletModal,
42
- evm,
43
42
  ]);
44
43
 
45
44
  const openWalletModal = useCallback(() => {
@@ -50,17 +49,10 @@ export const WalletPanel = ({ chain, className }: Props) => {
50
49
  openEvmWalletModal();
51
50
  } else if (chain.ecosystem === "solana") {
52
51
  setOpenSolanaWalletModal(true);
53
- solana.connectWallet();
54
52
  } else if (chain.ecosystem === "sui") {
55
53
  setOpenSuiWalletModal(true);
56
54
  }
57
- }, [
58
- chain,
59
- openEvmWalletModal,
60
- solana,
61
- setOpenSolanaWalletModal,
62
- setOpenSuiWalletModal,
63
- ]);
55
+ }, [chain, openEvmWalletModal, setOpenSolanaWalletModal, setOpenSuiWalletModal]);
64
56
 
65
57
  const address = useMemo(() => {
66
58
  if (!chain) {
@@ -76,23 +68,25 @@ export const WalletPanel = ({ chain, className }: Props) => {
76
68
  return "";
77
69
  }, [chain, evm, solana, sui]);
78
70
 
71
+ // Reserve the button's height before the chain loads so the card
72
+ // doesn't grow when the wallet button appears.
79
73
  if (!chain) {
80
- return null;
74
+ return <div className="h-6" aria-hidden />;
81
75
  }
82
76
 
83
77
  return (
84
78
  <button
85
79
  type="button"
86
80
  onClick={openWalletModal}
87
- className={`flex items-center justify-center gap-0.5 px-2 py-1 rounded bg-t_bg_tertiary bg-opacity-5 hover:bg-opacity-10 transition-colors ${
81
+ className={`flex items-center justify-center px-2 py-1 rounded bg-t_bg_tertiary bg-opacity-5 hover:bg-opacity-10 transition-colors ${
88
82
  className || ""
89
83
  }`}
90
84
  >
91
85
  <p className="text-[10px] leading-4 tracking-[0.01em] text-t_text_primary whitespace-nowrap">
92
86
  {address ? shortAddress(address) : "Connect"}
93
87
  </p>
94
- <div className="flex items-center justify-center w-4 h-4 fill-t_text_primary opacity-50 [&_svg]:w-2.5 [&_svg]:h-2.5">
95
- <ChevronDownIcon />
88
+ <div className="flex items-center justify-center w-4 h-4 text-t_text_primary opacity-50">
89
+ <ChevronDownThinSmallIcon />
96
90
  </div>
97
91
  </button>
98
92
  );
@@ -25,13 +25,13 @@ export const SwapPanel = ({ type }: Props) => {
25
25
 
26
26
  return (
27
27
  <div
28
- className={`flex flex-col gap-2 px-4 sm:px-6 py-4 rounded-2xl bg-t_bg_secondary bg-opacity-60 ${
28
+ className={`flex flex-col gap-2 px-4 sm:px-6 py-4 rounded-2xl backdrop-blur-[12px] ${
29
29
  type === "source"
30
- ? "bg-gradient-to-r from-t_bg_tertiary/5 to-t_bg_tertiary/5 border border-solid border-t_text_primary border-opacity-10"
30
+ ? "bg-gradient-to-r from-t_bg_tertiary/5 to-t_bg_tertiary/5 border border-solid border-t_text_primary border-opacity-25"
31
31
  : "bg-gradient-to-r from-t_bg_tertiary/[0.03] to-t_bg_tertiary/[0.03]"
32
32
  }`}
33
33
  >
34
- <div className="flex gap-2 items-center w-full">
34
+ <div className="flex gap-2 items-center w-full min-h-6">
35
35
  <p className="text-[10px] leading-4 tracking-[0.01em] text-t_text_primary text-opacity-50">
36
36
  {type === "source" ? "Source" : "Destination"}
37
37
  </p>
@@ -41,7 +41,7 @@ export const SwapPanel = ({ type }: Props) => {
41
41
  <AmountPanel type={type} />
42
42
  <TokenPanel chain={chain} token={token} type={type} />
43
43
  </div>
44
- <div className="flex items-center justify-between w-full gap-4">
44
+ <div className="flex items-center justify-between w-full gap-4 min-h-6">
45
45
  <UsdValue type={type} />
46
46
  <Balance type={type} />
47
47
  </div>
@@ -4,7 +4,7 @@ import {
4
4
  DotRedIcon,
5
5
  WalletConnectIcon,
6
6
  } from "@src/assets/icons";
7
- import { FC, useCallback } from "react";
7
+ import { FC, ReactNode, useCallback } from "react";
8
8
  import { Connector, useAccount, useConnect, useDisconnect } from "wagmi";
9
9
  import { useWallet as useSolanaWallet } from "@solana/wallet-adapter-react";
10
10
  import { Ecosystem } from "@src/models";
@@ -24,7 +24,7 @@ const ConnectorIcon: FC<{ connector: Connector; className?: string }> = ({
24
24
  case "WalletConnect": {
25
25
  return (
26
26
  <WalletConnectIcon
27
- className={`w-[34px] h-[34px] rounded-md ${className || ""}`}
27
+ className={`w-10 h-10 rounded-lg shrink-0 ${className || ""}`}
28
28
  />
29
29
  );
30
30
  }
@@ -33,13 +33,50 @@ const ConnectorIcon: FC<{ connector: Connector; className?: string }> = ({
33
33
  <img
34
34
  src={connector.icon}
35
35
  alt={connector.name}
36
- className={`w-[34px] h-[34px] rounded-md ${className || ""}`}
36
+ className={`w-10 h-10 rounded-lg shrink-0 ${className || ""}`}
37
37
  />
38
38
  );
39
39
  }
40
40
  }
41
41
  };
42
42
 
43
+ /** Wallet list row styled after the ChainPicker/TokenItem rows. */
44
+ const WalletRow: FC<{
45
+ icon: ReactNode;
46
+ name: string;
47
+ isConnected: boolean;
48
+ onClick: () => void;
49
+ }> = ({ icon, name, isConnected, onClick }) => (
50
+ <div className="pb-2">
51
+ <button
52
+ type="button"
53
+ onClick={onClick}
54
+ className={`group flex gap-3 items-center w-full 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 ${
55
+ isConnected ? "!bg-t_bg_tertiary !bg-opacity-10" : ""
56
+ }`}
57
+ >
58
+ {icon}
59
+ <div className="flex justify-between items-center gap-2 w-full min-w-0">
60
+ <p className="text-sm font-medium leading-5 tracking-[0.01em] text-t_text_primary text-left">
61
+ {name}
62
+ </p>
63
+ {isConnected && (
64
+ <span className="flex items-center gap-1.5 text-xs leading-[18px] tracking-[0.01em] whitespace-nowrap">
65
+ <span className="group-hover:hidden flex items-center gap-1.5 text-t_text_green">
66
+ <DotGreenIcon />
67
+ Connected
68
+ </span>
69
+ <span className="hidden group-hover:flex items-center gap-1.5 text-t_text_red">
70
+ <DotRedIcon />
71
+ Disconnect
72
+ </span>
73
+ </span>
74
+ )}
75
+ </div>
76
+ </button>
77
+ </div>
78
+ );
79
+
43
80
  export const WalletPicker: FC<{
44
81
  ecosystem: Ecosystem;
45
82
  className?: string;
@@ -114,167 +151,122 @@ export const WalletPicker: FC<{
114
151
  }
115
152
  }, [disconnectSuiWalletMut, onClose]);
116
153
 
154
+ // The host's wagmi config may register the same connector more than once
155
+ // (e.g. WalletConnect via several RainbowKit wallets) — show each name once.
156
+ const visibleConnectors = connectors.filter(
157
+ (connector, index) =>
158
+ connectors.findIndex(({ name }) => name === connector.name) === index,
159
+ );
160
+
161
+ const visibleSolanaWallets = wallets
162
+ // MetaMask registers as a Wallet Standard Solana wallet (in more
163
+ // than one variant) but doesn't actually work here, so keep it out
164
+ // of the Solana picker entirely.
165
+ .filter((wallet) => !/metamask/i.test(wallet.adapter.name));
166
+
117
167
  return (
118
- <div className={`flex flex-col h-full ${className || ""}`}>
119
- <div className="flex justify-between">
120
- <div className="text-base/4 mb-4 text-t_text_primary">Pick wallet</div>
121
- <div
122
- className="w-4 h-4 fill-t_text_primary cursor-pointer"
168
+ <div className={`flex flex-col h-full min-h-0 ${className || ""}`}>
169
+ <div className="flex justify-between items-center mb-6">
170
+ <div className="text-2xl leading-8 tracking-[0.01em] text-t_text_primary">
171
+ Select Wallet
172
+ </div>
173
+ <button
174
+ type="button"
175
+ aria-label="Close"
176
+ 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"
123
177
  onClick={onClose}
124
178
  >
125
- <CloseIcon />
126
- </div>
179
+ <div className="w-4 h-4 opacity-50 fill-t_text_primary flex items-center justify-center">
180
+ <CloseIcon />
181
+ </div>
182
+ </button>
127
183
  </div>
128
- <div className="horizontal-separator" />
129
- <div className="h-full pt-4">
130
- {ecosystem === Ecosystem.EVM && (
131
- <>
132
- {connectors.map((connector) => {
133
- const isConnected = activeConnector?.id === connector.id;
134
- return (
135
- <button
136
- key={connector.id}
137
- onClick={async () => {
138
- isConnected
139
- ? disconnect()
140
- : await handleEvmWalletClick(connector);
141
- onClose();
142
- }}
143
- className="group flex items-center w-full gap-3 mt-2 mb-2 cursor-pointer p-4 border border-solid border-white border-opacity-0 hover:selected-wallet-item"
144
- >
145
- <ConnectorIcon connector={connector} />
146
- <div className="text-t_text_primary">{connector.name}</div>
147
- {isConnected && (
148
- <>
149
- <div className="flex w-full justify-end items-baseline gap-1">
150
- <div className="group-hover:hidden">
151
- <DotGreenIcon />
152
- </div>
153
- <div className="hidden group-hover:block">
154
- <DotRedIcon />
155
- </div>
156
- <div className="text-t_text_green group-hover:hidden">
157
- connected
158
- </div>
159
- <div className="text-t_text_red hidden group-hover:block">
160
- disconnect
161
- </div>
162
- </div>
163
- </>
164
- )}
165
- </button>
166
- );
167
- })}
168
- </>
169
- )}
170
- {ecosystem === Ecosystem.SOLANA && (
171
- <>
172
- {wallets
173
- // MetaMask registers as a Wallet Standard Solana wallet (in more
174
- // than one variant) but doesn't actually work here, so keep it out
175
- // of the Solana picker entirely.
176
- .filter((wallet) => !/metamask/i.test(wallet.adapter.name))
177
- .map((wallet) => {
184
+ <div className="flex flex-col flex-1 min-h-0 overflow-y-auto">
185
+ {ecosystem === Ecosystem.EVM &&
186
+ visibleConnectors.map((connector) => {
187
+ const isConnected = activeConnector?.id === connector.id;
188
+ return (
189
+ <WalletRow
190
+ key={connector.id}
191
+ icon={<ConnectorIcon connector={connector} />}
192
+ name={connector.name}
193
+ isConnected={isConnected}
194
+ onClick={async () => {
195
+ isConnected
196
+ ? disconnect()
197
+ : await handleEvmWalletClick(connector);
198
+ onClose();
199
+ }}
200
+ />
201
+ );
202
+ })}
203
+ {ecosystem === Ecosystem.SOLANA &&
204
+ (visibleSolanaWallets.length ? (
205
+ visibleSolanaWallets.map((wallet) => {
178
206
  const isConnected =
179
- publicKey && activeWallet?.adapter.name === wallet.adapter.name;
207
+ !!publicKey &&
208
+ activeWallet?.adapter.name === wallet.adapter.name;
180
209
  return (
181
- <button
210
+ <WalletRow
182
211
  key={wallet.adapter.name}
212
+ icon={
213
+ <img
214
+ src={wallet.adapter.icon}
215
+ alt={wallet.adapter.name}
216
+ className="w-10 h-10 rounded-lg shrink-0"
217
+ />
218
+ }
219
+ name={wallet.adapter.name}
220
+ isConnected={isConnected}
183
221
  onClick={async () => {
184
222
  isConnected
185
223
  ? await disconnectSolana()
186
224
  : await handleConnectSolana(wallet.adapter.name);
187
225
  onClose();
188
226
  }}
189
- className="group flex items-center w-full gap-3 mt-2 mb-2 cursor-pointer p-4 border border-solid border-white border-opacity-0 hover:selected-wallet-item"
190
- >
191
- <img
192
- src={wallet.adapter.icon}
193
- alt={wallet.adapter.name}
194
- className={`w-[34px] h-[34px] rounded-md ${
195
- className || ""
196
- }`}
197
- />
198
- <div className="text-t_text_primary">
199
- {wallet.adapter.name}
200
- </div>
201
- {isConnected && (
202
- <>
203
- <div className="flex w-full justify-end items-baseline gap-1">
204
- <div className="group-hover:hidden">
205
- <DotGreenIcon />
206
- </div>
207
- <div className="hidden group-hover:block">
208
- <DotRedIcon />
209
- </div>
210
- <div className="text-t_text_green group-hover:hidden">
211
- connected
212
- </div>
213
- <div className="text-t_text_red hidden group-hover:block">
214
- disconnect
215
- </div>
216
- </div>
217
- </>
218
- )}
219
- </button>
227
+ />
220
228
  );
221
- })}
222
- </>
223
- )}
224
- {ecosystem === Ecosystem.SUI && (
225
- <>
226
- {suiWallets.map((wallet) => {
229
+ })
230
+ ) : (
231
+ <div className="text-center py-1 text-sm text-t_text_primary text-opacity-50">
232
+ No wallet found
233
+ </div>
234
+ ))}
235
+ {ecosystem === Ecosystem.SUI &&
236
+ (suiWallets.length ? (
237
+ suiWallets.map((wallet) => {
227
238
  const isConnected =
228
239
  !!suiAccount?.address &&
229
240
  suiCurrentWallet?.currentWallet?.name === wallet.name;
230
-
231
- const icon = wallet.icon;
232
-
233
241
  return (
234
- <button
242
+ <WalletRow
235
243
  key={wallet.name}
244
+ icon={
245
+ wallet.icon ? (
246
+ <img
247
+ src={wallet.icon}
248
+ alt={wallet.name}
249
+ className="w-10 h-10 rounded-lg shrink-0"
250
+ />
251
+ ) : (
252
+ <div className="w-10 h-10 rounded-lg shrink-0 bg-t_bg_tertiary bg-opacity-10" />
253
+ )
254
+ }
255
+ name={wallet.name}
256
+ isConnected={isConnected}
236
257
  onClick={async () => {
237
258
  isConnected
238
259
  ? await handleDisconnectSui()
239
260
  : await handleConnectSui(wallet);
240
261
  }}
241
- className="group flex items-center w-full gap-3 mt-2 mb-2 cursor-pointer p-4 border border-solid border-white border-opacity-0 hover:selected-wallet-item"
242
- >
243
- {icon ? (
244
- <img
245
- src={icon}
246
- alt={wallet.name}
247
- className={`w-[34px] h-[34px] rounded-md ${
248
- className || ""
249
- }`}
250
- />
251
- ) : (
252
- <div className="w-[34px] h-[34px] rounded-md bg-white/10" />
253
- )}
254
-
255
- <div className="text-t_text_primary">{wallet.name}</div>
256
-
257
- {isConnected && (
258
- <div className="flex w-full justify-end items-baseline gap-1">
259
- <div className="group-hover:hidden">
260
- <DotGreenIcon />
261
- </div>
262
- <div className="hidden group-hover:block">
263
- <DotRedIcon />
264
- </div>
265
- <div className="text-t_text_green group-hover:hidden">
266
- connected
267
- </div>
268
- <div className="text-t_text_red hidden group-hover:block">
269
- disconnect
270
- </div>
271
- </div>
272
- )}
273
- </button>
262
+ />
274
263
  );
275
- })}
276
- </>
277
- )}
264
+ })
265
+ ) : (
266
+ <div className="text-center py-1 text-sm text-t_text_primary text-opacity-50">
267
+ No wallet found
268
+ </div>
269
+ ))}
278
270
  </div>
279
271
  </div>
280
272
  );