@xswap-link/sdk 0.6.10 → 0.8.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 +16 -0
- package/dist/index.d.mts +124 -72
- package/dist/index.d.ts +124 -72
- package/dist/index.global.js +686 -281
- package/dist/index.js +10 -10
- package/dist/index.mjs +10 -10
- package/package.json +1 -1
- package/src/assets/icons/ArrowsExchange.tsx +18 -0
- package/src/assets/icons/index.ts +1 -0
- package/src/components/Modal/index.tsx +48 -41
- package/src/components/Swap/Header/index.tsx +4 -2
- package/src/components/Swap/HistoryView/index.tsx +2 -2
- package/src/components/Swap/ReorderButton/ReorderButton.tsx +37 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/ArrowIcon.tsx +1 -1
- package/src/components/Swap/SwapView/SwapButton/index.tsx +31 -22
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +55 -22
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/index.tsx +74 -10
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/index.tsx +83 -28
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +9 -5
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +12 -6
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +50 -29
- package/src/components/Swap/SwapView/SwapPanel/index.tsx +8 -3
- package/src/components/Swap/SwapView/WalletPicker/index.tsx +7 -6
- package/src/components/Swap/SwapView/index.tsx +3 -3
- package/src/components/Swap/index.tsx +1 -1
- package/src/components/TxConfigForm/index.tsx +69 -19
- package/src/components/TxModal/index.tsx +59 -0
- package/src/components/TxWidgetWC/index.tsx +47 -2
- package/src/components/TxWidgetWCWrapped/index.tsx +33 -1
- package/src/components/index.ts +1 -0
- package/src/config/index.ts +1 -1
- package/src/context/SwapProvider.tsx +177 -27
- package/src/context/TxUIWrapper.tsx +5 -5
- package/src/hooks/index.ts +0 -1
- package/src/models/BridgeTokensDictionary.ts +7 -0
- package/src/models/TokenData.ts +3 -1
- package/src/models/index.ts +6 -5
- package/src/models/payloads/ModalIntegrationPayload.ts +17 -3
- package/src/models/payloads/WidgetIntegrationPayload.ts +15 -1
- package/src/services/api.ts +6 -1
- package/src/services/integrations/transactions.ts +40 -17
- package/src/types/global.d.ts +15 -0
- package/src/utils/validation.ts +367 -0
- package/tsconfig.json +3 -2
- package/src/context/ModalProvider.tsx +0 -63
- package/src/hooks/usePortal.ts +0 -79
package/package.json
CHANGED
|
@@ -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";
|
|
@@ -1,52 +1,59 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
1
|
+
import { useSwapContext } from "@src/context";
|
|
2
|
+
import { FC, ReactNode } from "react";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
/**
|
|
5
|
+
* This component was created to meet the following requirements:
|
|
6
|
+
* - Modals needs to be inside the shadow root,
|
|
7
|
+
* - Modals needs to be inside all app's main contexts,
|
|
8
|
+
* - Modals' need to rerender on props change.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```
|
|
12
|
+
* const [open, setOpen] = useState(false);
|
|
13
|
+
*
|
|
14
|
+
* return (
|
|
15
|
+
* <>
|
|
16
|
+
* { open && <Modal onClose={() => setOpen(false)}>...</Modal>}
|
|
17
|
+
*
|
|
18
|
+
* <button onClick={() => setOpen(true)}>Open modal</button>
|
|
19
|
+
* </>
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const Modal: FC<{
|
|
11
24
|
className?: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
onCloseClick,
|
|
17
|
-
type = "waiting",
|
|
18
|
-
className,
|
|
19
|
-
}: Props) => {
|
|
20
|
-
const onBackdropClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
21
|
-
e.stopPropagation();
|
|
22
|
-
e.nativeEvent.stopImmediatePropagation();
|
|
23
|
-
if (e.target === e.currentTarget) {
|
|
24
|
-
onCloseClick();
|
|
25
|
-
}
|
|
26
|
-
};
|
|
25
|
+
children: ReactNode;
|
|
26
|
+
onClose: () => void;
|
|
27
|
+
}> = ({ className, children, onClose }) => {
|
|
28
|
+
const { overlay } = useSwapContext();
|
|
27
29
|
|
|
28
|
-
return
|
|
30
|
+
return (
|
|
29
31
|
<div
|
|
30
|
-
className=
|
|
31
|
-
|
|
32
|
+
className={`${
|
|
33
|
+
overlay ? "fixed bg-[rgba(0,0,0,0.8)]" : "absolute"
|
|
34
|
+
} top-0 left-0 right-0 bottom-0 flex items-center justify-center z-10 ${
|
|
35
|
+
className || ""
|
|
36
|
+
}`}
|
|
37
|
+
onMouseDown={(e) => {
|
|
38
|
+
e.stopPropagation();
|
|
39
|
+
e.nativeEvent.stopImmediatePropagation();
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
|
|
42
|
+
if (e.target === e.currentTarget && overlay) {
|
|
43
|
+
onClose();
|
|
44
|
+
}
|
|
45
|
+
}}
|
|
32
46
|
>
|
|
33
47
|
<div
|
|
34
|
-
className=
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
? "border-t_warning_dark"
|
|
41
|
-
: type === "waiting"
|
|
42
|
-
? "border-t_main_accent_dark"
|
|
43
|
-
: "border-t_text_primary border-opacity-10"
|
|
44
|
-
} ${className}`}
|
|
48
|
+
className="flex flex-col bg-t_bg_primary x-border rounded-3xl p-4 w-full min-w-[340px]"
|
|
49
|
+
style={{
|
|
50
|
+
height: overlay ? "600px" : "100%",
|
|
51
|
+
maxWidth: "480px",
|
|
52
|
+
maxHeight: "100%",
|
|
53
|
+
}}
|
|
45
54
|
>
|
|
46
55
|
{children}
|
|
47
56
|
</div>
|
|
48
|
-
</div
|
|
49
|
-
xpayShadowRoot!,
|
|
50
|
-
id,
|
|
57
|
+
</div>
|
|
51
58
|
);
|
|
52
59
|
};
|
|
@@ -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">
|
|
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: "
|
|
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="
|
|
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-
|
|
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 />
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import { Modal } from "@src/components/Modal";
|
|
1
2
|
import { useSwapContext, useTxUIWrapper } from "@src/context";
|
|
2
|
-
import {
|
|
3
|
+
import { ADDRESSES } from "@src/contracts";
|
|
4
|
+
import { specialApprovalTokens } from "@src/contracts/specialApprovalTokens";
|
|
5
|
+
import useNetworks from "@src/hooks/networkManagement/useNetworks";
|
|
6
|
+
import { useERC20Token } from "@src/hooks/useERC20Token";
|
|
3
7
|
import { TxStatus } from "@src/models";
|
|
4
8
|
import { safeBigNumberFrom } from "@src/utils";
|
|
9
|
+
import { BigNumber } from "ethers";
|
|
5
10
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
6
11
|
import { useAccount, useSwitchChain } from "wagmi";
|
|
7
12
|
import { WalletPicker } from "../WalletPicker";
|
|
8
|
-
import { BigNumber } from "ethers";
|
|
9
|
-
import useNetworks from "@src/hooks/networkManagement/useNetworks";
|
|
10
|
-
import { useERC20Token } from "@src/hooks/useERC20Token";
|
|
11
|
-
import { ADDRESSES } from "@src/contracts";
|
|
12
|
-
import { specialApprovalTokens } from "@src/contracts/specialApprovalTokens";
|
|
13
13
|
|
|
14
14
|
type Button = {
|
|
15
15
|
text: string;
|
|
@@ -20,10 +20,11 @@ type Button = {
|
|
|
20
20
|
export const SwapButton = () => {
|
|
21
21
|
const { address, chainId } = useAccount();
|
|
22
22
|
const { switchChainAsync } = useSwitchChain();
|
|
23
|
-
const
|
|
23
|
+
const [connectWalletModalOpen, setConnectWalletModalOpen] = useState(false);
|
|
24
24
|
const { integrationConfig } = useSwapContext();
|
|
25
25
|
|
|
26
26
|
const {
|
|
27
|
+
bridgeUI,
|
|
27
28
|
error,
|
|
28
29
|
srcChain,
|
|
29
30
|
dstChain,
|
|
@@ -113,7 +114,7 @@ export const SwapButton = () => {
|
|
|
113
114
|
? "Connect wallet"
|
|
114
115
|
: "Connect wallet first",
|
|
115
116
|
fn: () => {
|
|
116
|
-
|
|
117
|
+
setConnectWalletModalOpen(true);
|
|
117
118
|
},
|
|
118
119
|
disabled: !integrationConfig.defaultWalletPicker,
|
|
119
120
|
};
|
|
@@ -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,
|
|
@@ -218,7 +220,6 @@ export const SwapButton = () => {
|
|
|
218
220
|
isFetchingRoute,
|
|
219
221
|
currentTokenAllowance,
|
|
220
222
|
integrationConfig.defaultWalletPicker,
|
|
221
|
-
openModal,
|
|
222
223
|
switchChainAsync,
|
|
223
224
|
enqueueTransaction,
|
|
224
225
|
approve,
|
|
@@ -229,17 +230,25 @@ export const SwapButton = () => {
|
|
|
229
230
|
]);
|
|
230
231
|
|
|
231
232
|
return (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
233
|
+
<>
|
|
234
|
+
{connectWalletModalOpen && (
|
|
235
|
+
<Modal onClose={() => setConnectWalletModalOpen(false)}>
|
|
236
|
+
<WalletPicker onClose={() => setConnectWalletModalOpen(false)} />
|
|
237
|
+
</Modal>
|
|
238
|
+
)}
|
|
239
|
+
|
|
240
|
+
<button
|
|
241
|
+
type="button"
|
|
242
|
+
disabled={button.disabled}
|
|
243
|
+
onClick={button.fn}
|
|
244
|
+
className={`text-xl w-full py-5 cursor-pointer disabled:cursor-not-allowed rounded-2xl border-none ${
|
|
245
|
+
button.disabled
|
|
246
|
+
? "bg-t_button_pr_off_bg text-t_button_pr_off_text"
|
|
247
|
+
: "bg-gradient-to-r from-t_main_accent_light to-t_main_accent_dark text-t_button_pr_text"
|
|
248
|
+
}`}
|
|
249
|
+
>
|
|
250
|
+
{button.text}
|
|
251
|
+
</button>
|
|
252
|
+
</>
|
|
244
253
|
);
|
|
245
254
|
};
|
|
@@ -1,74 +1,107 @@
|
|
|
1
1
|
import { DotGreenIcon, HelpIcon } from "@src/assets/icons";
|
|
2
|
-
import {
|
|
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
|
-
|
|
12
|
+
disabledChains: string[];
|
|
12
13
|
onClick: (chainId: string) => void;
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 =
|
|
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 ===
|
|
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-
|
|
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 ===
|
|
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
|
|
63
|
+
<p>{chain.displayName}</p>
|
|
49
64
|
</div>
|
|
50
65
|
|
|
51
66
|
{connected && (
|
|
52
|
-
<div className="flex items-
|
|
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
|
-
|
|
72
|
+
|
|
73
|
+
{isInDisabledChains && chain.chainId !== sameChainId && (
|
|
58
74
|
<div className="flex items-center gap-1">
|
|
59
75
|
<Tooltip
|
|
60
|
-
text=
|
|
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-
|
|
85
|
+
id="transfers-from-chain-not-supported-tooltip"
|
|
64
86
|
/>
|
|
87
|
+
</div>
|
|
88
|
+
)}
|
|
65
89
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import { CloseIcon } from "@src/assets/icons";
|
|
2
|
-
import {
|
|
2
|
+
import { useSwapContext } from "@src/context";
|
|
3
3
|
import { Chain, Web3Environment } from "@src/models";
|
|
4
4
|
import { useMemo } from "react";
|
|
5
5
|
import { Virtuoso } from "react-virtuoso";
|
|
@@ -8,24 +8,33 @@ import { ChainItem } from "./ChainItem";
|
|
|
8
8
|
|
|
9
9
|
type Props = {
|
|
10
10
|
type: SwapPanelType;
|
|
11
|
-
supportedChains: Chain[];
|
|
12
11
|
setSrcChain: (chain: Chain) => void;
|
|
13
12
|
setDstChain: (chain: Chain) => void;
|
|
13
|
+
onClose: () => void;
|
|
14
14
|
};
|
|
15
15
|
export const ChainPicker = ({
|
|
16
16
|
type,
|
|
17
|
-
supportedChains,
|
|
18
17
|
setSrcChain,
|
|
19
18
|
setDstChain,
|
|
19
|
+
onClose,
|
|
20
20
|
}: Props) => {
|
|
21
|
-
const {
|
|
21
|
+
const {
|
|
22
|
+
srcChain,
|
|
23
|
+
srcToken,
|
|
24
|
+
dstChain,
|
|
25
|
+
dstToken,
|
|
26
|
+
bridgeUI,
|
|
27
|
+
bridgeTokensDictionary,
|
|
28
|
+
supportedChains,
|
|
29
|
+
} = useSwapContext();
|
|
22
30
|
|
|
23
31
|
const filteredChains = useMemo(() => {
|
|
24
32
|
return supportedChains.filter(
|
|
25
|
-
({ web3Environment, swapSupported }) =>
|
|
26
|
-
web3Environment === Web3Environment.MAINNET &&
|
|
33
|
+
({ web3Environment, swapSupported, bridgeSupported }) =>
|
|
34
|
+
web3Environment === Web3Environment.MAINNET &&
|
|
35
|
+
((bridgeUI && bridgeSupported) || (!bridgeUI && swapSupported)),
|
|
27
36
|
);
|
|
28
|
-
}, [supportedChains]);
|
|
37
|
+
}, [bridgeUI, supportedChains]);
|
|
29
38
|
|
|
30
39
|
const select = (selectedChainId: string) => {
|
|
31
40
|
const chain = supportedChains.find(
|
|
@@ -39,16 +48,66 @@ export const ChainPicker = ({
|
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
type === "source" ? setSrcChain(chain) : setDstChain(chain);
|
|
42
|
-
|
|
51
|
+
onClose();
|
|
43
52
|
};
|
|
44
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
|
+
|
|
45
104
|
return (
|
|
46
105
|
<div className="flex flex-col h-full">
|
|
47
106
|
<div className="flex justify-between">
|
|
48
107
|
<div className="text-base/4 text-t_text_primary mb-4">{`Pick ${type} chain`}</div>
|
|
49
108
|
<div
|
|
50
109
|
className="w-4 h-4 fill-t_text_primary cursor-pointer"
|
|
51
|
-
onClick={
|
|
110
|
+
onClick={onClose}
|
|
52
111
|
>
|
|
53
112
|
<CloseIcon />
|
|
54
113
|
</div>
|
|
@@ -60,7 +119,12 @@ export const ChainPicker = ({
|
|
|
60
119
|
style={{ height: "100%" }}
|
|
61
120
|
data={filteredChains}
|
|
62
121
|
itemContent={(_, chain) => (
|
|
63
|
-
<ChainItem
|
|
122
|
+
<ChainItem
|
|
123
|
+
chain={chain}
|
|
124
|
+
type={type}
|
|
125
|
+
disabledChains={disabledChains}
|
|
126
|
+
onClick={select}
|
|
127
|
+
/>
|
|
64
128
|
)}
|
|
65
129
|
/>
|
|
66
130
|
) : (
|