@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.
- package/CHANGELOG.md +21 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.global.js +92 -92
- package/dist/index.js +3208 -2862
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3083 -2740
- package/dist/index.mjs.map +1 -1
- package/localTheme.ts +6 -4
- package/package.json +1 -1
- package/src/assets/icons/AdjustmentsIcon.tsx +5 -7
- package/src/assets/icons/ChainlinkMarkIcon.tsx +4 -5
- package/src/assets/icons/CheckCircleThinIcon.tsx +22 -0
- package/src/assets/icons/ChevronDownThinIcon.tsx +39 -0
- package/src/assets/icons/ChevronDownWideThinIcon.tsx +18 -0
- package/src/assets/icons/ChevronsUpDownThinIcon.tsx +21 -0
- package/src/assets/icons/ClockIcon.tsx +3 -7
- package/src/assets/icons/CloseIcon.tsx +9 -4
- package/src/assets/icons/ErrorCircleThinIcon.tsx +22 -0
- package/src/assets/icons/LinkExternalThinIcon.tsx +19 -0
- package/src/assets/icons/LoadingRingIcon.tsx +22 -0
- package/src/assets/icons/ProgressRingThinIcon.tsx +29 -0
- package/src/assets/icons/SearchThinIcon.tsx +15 -0
- package/src/assets/icons/SignThinIcon.tsx +15 -0
- package/src/assets/icons/WalletCheckThinIcon.tsx +24 -0
- package/src/assets/icons/WarningIcon.tsx +8 -5
- package/src/assets/icons/index.ts +11 -0
- package/src/components/Modal/index.tsx +14 -9
- package/src/components/PoweredBy/index.tsx +6 -8
- package/src/components/Swap/Header/index.tsx +2 -4
- package/src/components/Swap/HistoryView/ActivityCard/index.tsx +110 -115
- package/src/components/Swap/HistoryView/index.tsx +10 -7
- package/src/components/Swap/PickerView/index.tsx +151 -0
- package/src/components/Swap/ReorderButton/ReorderButton.tsx +4 -4
- package/src/components/Swap/SettingsView/Delivery/index.tsx +19 -17
- package/src/components/Swap/SettingsView/InfiniteApproval/index.tsx +19 -17
- package/src/components/Swap/SettingsView/Slippage/index.tsx +55 -49
- package/src/components/Swap/SwapView/ConfirmationView/TokenTiles/index.tsx +69 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Header/index.tsx +13 -25
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Steps/index.tsx +65 -113
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +68 -26
- package/src/components/Swap/SwapView/ConfirmationView/TxResult/index.tsx +51 -74
- package/src/components/Swap/SwapView/FeesPanel/DeliveryInfo/index.tsx +1 -1
- package/src/components/Swap/SwapView/FeesPanel/index.tsx +5 -5
- package/src/components/Swap/SwapView/SwapButton/index.tsx +2 -31
- package/src/components/Swap/SwapView/SwapPanel/AmountPanel/Balance/index.tsx +2 -2
- package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +2 -2
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/ChainPicker/index.tsx +143 -0
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/NetworkFilter/index.tsx +19 -16
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +11 -4
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +185 -105
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +54 -120
- package/src/components/Swap/SwapView/SwapPanel/UsdValue/index.tsx +2 -2
- package/src/components/Swap/SwapView/SwapPanel/WalletPanel/index.tsx +10 -16
- package/src/components/Swap/SwapView/SwapPanel/index.tsx +4 -4
- package/src/components/Swap/SwapView/WalletPicker/index.tsx +131 -139
- package/src/components/Swap/SwapView/index.tsx +11 -7
- package/src/components/Swap/WalletPickerView/index.tsx +65 -0
- package/src/components/Swap/index.tsx +39 -8
- package/src/components/ToggleButton/index.tsx +5 -7
- package/src/components/TokenLogo/index.tsx +1 -1
- package/src/components/TxConfigForm/index.tsx +2 -2
- package/src/components/TxModal/index.tsx +14 -4
- package/src/components/global.css +18 -1
- package/src/constants/index.ts +4 -0
- package/src/context/HistoryProvider.tsx +1 -1
- package/src/context/SwapProvider.tsx +193 -81
- package/src/context/TxUIWrapper.tsx +3 -3
- package/src/hooks/useEvmContractApi.ts +28 -4
- package/src/models/payloads/GetBalancesPayload.ts +1 -0
- package/src/services/api.ts +37 -0
- package/src/utils/index.ts +27 -1
- package/src/utils/tokens.ts +20 -1
- package/src/utils/validation.ts +9 -4
- package/tailwind.config.js +4 -1
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/ArrowIcon.tsx +0 -13
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/SwapPanel/index.tsx +0 -72
- package/src/components/Swap/SwapView/ConfirmationView/TxResult/TokenItem/index.tsx +0 -25
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ChevronDownThinIcon } from "@src/assets/icons";
|
|
2
|
+
import { TokenLogoWithChain } from "@src/components";
|
|
3
3
|
import { useSwapContext } from "@src/context";
|
|
4
|
-
import { Chain, Token
|
|
5
|
-
import {
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
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
|
-
|
|
42
|
+
(bridgeDestination && dstChainLocked) ||
|
|
101
43
|
integratorLockedDestinationToken ||
|
|
102
44
|
integratorLockedSourceToken;
|
|
103
45
|
|
|
104
46
|
return (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
</
|
|
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 {
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
95
|
-
<
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
<
|
|
126
|
-
|
|
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="
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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 &&
|
|
207
|
+
!!publicKey &&
|
|
208
|
+
activeWallet?.adapter.name === wallet.adapter.name;
|
|
180
209
|
return (
|
|
181
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
);
|