@xswap-link/sdk 0.3.5 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +9 -5
- package/CHANGELOG.md +16 -0
- package/README.md +2 -1
- package/dist/index.d.mts +106 -20
- package/dist/index.d.ts +106 -20
- package/dist/index.global.js +856 -307
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +105 -3564
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +105 -3534
- package/dist/index.mjs.map +1 -0
- package/nodemon.json +1 -1
- package/package.json +16 -9
- package/postcss.config.js +8 -1
- package/src/{components → assets}/icons/ArrowDownIcon.tsx +1 -2
- package/src/assets/icons/ArrowDownLongIcon.tsx +12 -0
- package/src/{components → assets}/icons/ArrowLeftIcon.tsx +1 -5
- package/src/{components → assets}/icons/ArrowRightIcon.tsx +1 -5
- package/src/assets/icons/ArrowUpIcon.tsx +12 -0
- package/src/{components → assets}/icons/ArrowUpRightIcon.tsx +1 -5
- package/src/assets/icons/CashbacklIcon.tsx +28 -0
- package/src/{components → assets}/icons/CheckIcon.tsx +1 -5
- package/src/assets/icons/ChevronDownIcon.tsx +12 -0
- package/src/assets/icons/ChevronUpIcon.tsx +7 -0
- package/src/{components → assets}/icons/CircularProgressIcon.tsx +1 -2
- package/src/assets/icons/CloseIcon.tsx +15 -0
- package/src/{components → assets}/icons/CoinsIcon.tsx +0 -2
- package/src/assets/icons/CopyIcon.tsx +15 -0
- package/src/assets/icons/DotGreenIcon.tsx +12 -0
- package/src/{components → assets}/icons/DownArrorIcon.tsx +0 -2
- package/src/{components → assets}/icons/ErrorIcon.tsx +0 -1
- package/src/assets/icons/ErrorModalStatusIcon.tsx +31 -0
- package/src/assets/icons/HelpIcon.tsx +21 -0
- package/src/assets/icons/HistoryGradientBgIcon.tsx +32 -0
- package/src/assets/icons/HistoryIcon.tsx +12 -0
- package/src/{components → assets}/icons/HourGlassIcon.tsx +1 -5
- package/src/assets/icons/Icon.tsx +5 -0
- package/src/{components → assets}/icons/InfoIcon.tsx +0 -1
- package/src/assets/icons/LinkExternalIcon.tsx +15 -0
- package/src/{components → assets}/icons/PercentageIcon.tsx +0 -1
- package/src/assets/icons/RankIcon.tsx +28 -0
- package/src/assets/icons/SettingsGradientBgIcon.tsx +32 -0
- package/src/assets/icons/SettingsIcon.tsx +12 -0
- package/src/{components → assets}/icons/SignIcon.tsx +0 -2
- package/src/{components → assets}/icons/SuccessIcon.tsx +0 -1
- package/src/assets/icons/SuccessModalStatusIcon.tsx +31 -0
- package/src/assets/icons/ThumbUpRoundedIcon.tsx +15 -0
- package/src/{components → assets}/icons/TimerIcon.tsx +0 -1
- package/src/assets/icons/TokensIcon.tsx +16 -0
- package/src/assets/icons/WaitingIcon.tsx +15 -0
- package/src/{components → assets}/icons/XMarkIcon.tsx +1 -5
- package/src/assets/icons/XPowerIcon.tsx +28 -0
- package/src/{components → assets}/icons/index.ts +16 -0
- package/src/components/Alert/index.tsx +1 -1
- package/src/components/Button/index.tsx +57 -0
- package/src/components/MessageBar/SnackMessage.tsx +32 -0
- package/src/components/MessageBar/index.tsx +44 -0
- package/src/components/Modal/index.tsx +52 -0
- package/src/components/PoweredBy/index.tsx +12 -0
- package/src/components/SafeInput/index.tsx +51 -0
- package/src/components/Skeleton/index.tsx +5 -9
- package/src/components/Spinner/index.tsx +6 -8
- package/src/components/Swap/Header/Controls/index.tsx +57 -0
- package/src/components/Swap/Header/index.tsx +16 -0
- package/src/components/Swap/HistoryView/index.tsx +34 -0
- package/src/components/Swap/SettingsView/Delivery/index.tsx +42 -0
- package/src/components/Swap/SettingsView/Slippage/index.tsx +80 -0
- package/src/components/Swap/SettingsView/index.tsx +11 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/ArrowIcon.tsx +13 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Header/index.tsx +37 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Steps/index.tsx +145 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/SwapPanel/index.tsx +61 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +291 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxResult/TokenItem/index.tsx +24 -0
- package/src/components/Swap/SwapView/ConfirmationView/TxResult/index.tsx +84 -0
- package/src/components/Swap/SwapView/ConfirmationView/index.tsx +17 -0
- package/src/components/Swap/SwapView/FeesPanel/DeliveryInfo/index.tsx +25 -0
- package/src/components/Swap/SwapView/FeesPanel/Fee/index.tsx +18 -0
- package/src/components/Swap/SwapView/FeesPanel/Fees/index.tsx +71 -0
- package/src/components/Swap/SwapView/FeesPanel/index.tsx +67 -0
- package/src/components/Swap/SwapView/SwapButton/index.tsx +154 -0
- package/src/components/Swap/SwapView/SwapPanel/AmountPanel/Balance/index.tsx +23 -0
- package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +56 -0
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +70 -0
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/index.tsx +62 -0
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/index.tsx +74 -0
- package/src/components/Swap/SwapView/SwapPanel/MaxPanel/index.tsx +35 -0
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/QuickPickTokenItem/index.tsx +40 -0
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +62 -0
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +399 -0
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +104 -0
- package/src/components/Swap/SwapView/SwapPanel/index.tsx +42 -0
- package/src/components/Swap/SwapView/index.tsx +20 -0
- package/src/components/Swap/index.tsx +34 -0
- package/src/components/TextInput/index.tsx +67 -0
- package/src/components/Timer/index.tsx +57 -0
- package/src/components/ToggleButton/index.tsx +24 -0
- package/src/components/{UnknownTokenLogo/UnknownTokenLogo.tsx → TokenLogo/Unknown/index.tsx} +3 -4
- package/src/components/TokenLogo/WithChain/index.tsx +34 -0
- package/src/components/TokenLogo/index.tsx +22 -0
- package/src/components/Tooltip/index.tsx +108 -0
- package/src/components/TxConfigForm/index.tsx +63 -42
- package/src/components/TxDataCard/TokenDetails/index.tsx +30 -0
- package/src/components/TxDataCard/TxDataCardUI/index.tsx +133 -0
- package/src/components/TxDataCard/index.tsx +30 -0
- package/src/components/TxStatusButton/index.tsx +64 -108
- package/src/components/global.css +115 -4
- package/src/components/index.ts +18 -1
- package/src/config/fonts.ts +114 -0
- package/src/config/global.d.ts +4 -0
- package/src/config/init.tsx +35 -31
- package/src/constants/crosschainEstimationTimes.ts +52 -0
- package/src/constants/index.ts +8 -0
- package/src/context/GlobalDataProvider.tsx +165 -0
- package/src/context/HistoryProvider.tsx +339 -0
- package/src/context/ModalProvider.tsx +47 -0
- package/src/context/SwapProvider.tsx +664 -0
- package/src/context/TransactionProvider.tsx +86 -0
- package/src/context/TxUIWrapper.tsx +388 -0
- package/src/context/index.ts +5 -0
- package/src/contracts/abi/CustomXSwapRouter.json +953 -0
- package/src/contracts/abi/StakingStableAPR.json +1438 -0
- package/src/contracts/abi/XSwapRouter.json +1242 -0
- package/src/contracts/abi/XSwapTokenFeeDistributor.json +350 -0
- package/src/contracts/abi/index.ts +8 -0
- package/src/contracts/addresses.ts +24 -6
- package/src/fonts/Satoshi-Black.woff2 +0 -0
- package/src/fonts/Satoshi-BlackItalic.woff2 +0 -0
- package/src/fonts/Satoshi-Bold.woff2 +0 -0
- package/src/fonts/Satoshi-BoldItalic.woff2 +0 -0
- package/src/fonts/Satoshi-Light.woff2 +0 -0
- package/src/fonts/Satoshi-LightItalic.woff2 +0 -0
- package/src/fonts/Satoshi-Medium.woff2 +0 -0
- package/src/fonts/Satoshi-MediumItalic.woff2 +0 -0
- package/src/fonts/Satoshi-Regular.woff2 +0 -0
- package/src/fonts/Satoshi-RegularItalic.woff2 +0 -0
- package/src/fonts/Satoshi-Variable.woff2 +0 -0
- package/src/fonts/Satoshi-VariableItalic.woff2 +0 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/networkManagement/clientToSigner.tsx +17 -0
- package/src/hooks/networkManagement/useNetworks.ts +51 -0
- package/src/hooks/useBatchQuery.ts +46 -0
- package/src/hooks/useDebounce.tsx +28 -0
- package/src/hooks/useEvmContractApi.ts +85 -0
- package/src/hooks/usePortal.ts +79 -0
- package/src/hooks/useXPower.ts +58 -0
- package/src/models/Addresses.ts +8 -2
- package/src/models/TokenData.ts +13 -0
- package/src/models/TransactionHistory.ts +2 -0
- package/src/models/TxUIWrapper.ts +51 -0
- package/src/models/XSwapConfig.ts +1 -0
- package/src/models/index.ts +1 -0
- package/src/models/payloads/GetBalancesPayload.ts +3 -0
- package/src/models/payloads/GetFinishedLeaderboardQuestsPayload.ts +3 -0
- package/src/models/payloads/GetLeaderboardChangePayload.ts +4 -0
- package/src/models/payloads/{GetSwapTxPayload.ts → ModalIntegrationPayload.ts} +3 -1
- package/src/models/payloads/index.ts +5 -2
- package/src/services/api.ts +34 -5
- package/src/services/integrations/monitoring.ts +49 -37
- package/src/services/integrations/transactions.ts +50 -6
- package/src/utils/contracts.ts +29 -0
- package/src/utils/index.ts +3 -3
- package/src/utils/numbers.ts +14 -0
- package/src/utils/parseWeb3Error.ts +7 -3
- package/tailwind.config.js +15 -5
- package/tsconfig.json +7 -7
- package/tsup.config.ts +62 -0
- package/xswap.config.ts +1 -0
- package/dist/index.css +0 -1
- package/src/components/TxConfigForm/BalanceComponent.tsx +0 -52
- package/src/components/TxConfigForm/Button.tsx +0 -20
- package/src/components/TxConfigForm/ChainListElement.tsx +0 -36
- package/src/components/TxConfigForm/ConfirmationAmount.tsx +0 -91
- package/src/components/TxConfigForm/ConfirmationView/ErrorView.tsx +0 -38
- package/src/components/TxConfigForm/ConfirmationView/SuccessView.tsx +0 -159
- package/src/components/TxConfigForm/ConfirmationView/index.tsx +0 -149
- package/src/components/TxConfigForm/ConnectWalletPrompt.tsx +0 -21
- package/src/components/TxConfigForm/Description.tsx +0 -17
- package/src/components/TxConfigForm/ErrorField.tsx +0 -19
- package/src/components/TxConfigForm/FeesDetails.tsx +0 -117
- package/src/components/TxConfigForm/Form.tsx +0 -574
- package/src/components/TxConfigForm/History.tsx +0 -131
- package/src/components/TxConfigForm/HistoryCard.tsx +0 -182
- package/src/components/TxConfigForm/PoweredBy.tsx +0 -12
- package/src/components/TxConfigForm/Settings.tsx +0 -143
- package/src/components/TxConfigForm/Summary.tsx +0 -105
- package/src/components/TxConfigForm/SwapPanel.tsx +0 -185
- package/src/components/TxConfigForm/TokenPicker.tsx +0 -176
- package/src/components/TxConfigForm/TopBar.tsx +0 -58
- package/src/components/TxConfigForm/UsdPrice.tsx +0 -44
- package/src/components/icons/ChevronDownIcon.tsx +0 -15
- package/src/components/icons/ChevronUpIcon.tsx +0 -10
- package/src/components/icons/CloseIcon.tsx +0 -15
- package/src/components/icons/HistoryIcon.tsx +0 -16
- package/src/components/icons/SettingsIcon.tsx +0 -16
- /package/src/{components → assets}/icons/ChainlinkCCIPIcon.tsx +0 -0
- /package/src/{components → assets}/icons/SearchIcon.tsx +0 -0
- /package/src/{components → assets}/icons/XSwapBadgeIcon.tsx +0 -0
- /package/src/{components → assets}/icons/XSwapLogo.tsx +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrowDownLongIcon,
|
|
3
|
+
CloseIcon,
|
|
4
|
+
ErrorModalStatusIcon,
|
|
5
|
+
LinkExternalIcon,
|
|
6
|
+
SuccessModalStatusIcon,
|
|
7
|
+
} from "@src/assets/icons";
|
|
8
|
+
import { useSwapContext, useTxUIWrapper } from "@src/context";
|
|
9
|
+
import { TokenItem } from "./TokenItem";
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
onCloseClick: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const TxResult = ({ onCloseClick }: Props) => {
|
|
16
|
+
const { txError, txExplorerUrl, txMsg } = useTxUIWrapper();
|
|
17
|
+
const {
|
|
18
|
+
isExpressDeliveryActive,
|
|
19
|
+
srcChain,
|
|
20
|
+
srcToken,
|
|
21
|
+
dstChain,
|
|
22
|
+
dstToken,
|
|
23
|
+
srcValue,
|
|
24
|
+
dstValue,
|
|
25
|
+
} = useSwapContext();
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex flex-col gap-5 text-white justify-center items-center">
|
|
29
|
+
{txError ? (
|
|
30
|
+
<>
|
|
31
|
+
<div
|
|
32
|
+
className="flex justify-end fill-white cursor-pointer w-full"
|
|
33
|
+
onClick={onCloseClick}
|
|
34
|
+
>
|
|
35
|
+
<CloseIcon className="w-3.5 h-3.5" />
|
|
36
|
+
</div>
|
|
37
|
+
<ErrorModalStatusIcon />
|
|
38
|
+
<div className="text-2xl font-bold">Transaction failed</div>
|
|
39
|
+
<div className="text-center">{txError}</div>
|
|
40
|
+
</>
|
|
41
|
+
) : (
|
|
42
|
+
<>
|
|
43
|
+
<div
|
|
44
|
+
className="flex justify-end fill-white cursor-pointer w-full"
|
|
45
|
+
onClick={onCloseClick}
|
|
46
|
+
>
|
|
47
|
+
<CloseIcon className="w-3.5 h-3.5" />
|
|
48
|
+
</div>
|
|
49
|
+
<SuccessModalStatusIcon />
|
|
50
|
+
<div className="flex flex-col items-center">
|
|
51
|
+
<div className="text-2xl font-bold">Transaction success!</div>
|
|
52
|
+
{!txMsg && srcChain !== dstChain && (
|
|
53
|
+
<div className="text-sm font-normal opacity-60">
|
|
54
|
+
{`Approximated time of delivery: ${
|
|
55
|
+
isExpressDeliveryActive ? "30 sec" : "30 min"
|
|
56
|
+
}`}
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
{!txMsg && (
|
|
61
|
+
<div className="flex items-center gap-2">
|
|
62
|
+
<TokenItem chain={srcChain} token={srcToken} value={srcValue} />
|
|
63
|
+
<div className="-rotate-90 w-8 fill-white">
|
|
64
|
+
<ArrowDownLongIcon />
|
|
65
|
+
</div>
|
|
66
|
+
<TokenItem chain={dstChain} token={dstToken} value={dstValue} />
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
<a
|
|
70
|
+
href={txExplorerUrl}
|
|
71
|
+
target="_blank"
|
|
72
|
+
rel="noreferrer"
|
|
73
|
+
className="flex gap-4 items-center"
|
|
74
|
+
>
|
|
75
|
+
<p className="text-x_blue_300 underline">
|
|
76
|
+
View details on Explorer
|
|
77
|
+
</p>
|
|
78
|
+
<LinkExternalIcon />
|
|
79
|
+
</a>
|
|
80
|
+
</>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useTxUIWrapper } from "@src/context";
|
|
2
|
+
import { TxStatus } from "@src/models";
|
|
3
|
+
import { TxOverview } from "./TxOverview";
|
|
4
|
+
import { TxResult } from "./TxResult";
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
onCloseClick: () => void;
|
|
8
|
+
};
|
|
9
|
+
export const ConfirmationView = ({ onCloseClick }: Props) => {
|
|
10
|
+
const { txStatus, txError } = useTxUIWrapper();
|
|
11
|
+
|
|
12
|
+
return txStatus === TxStatus.COMPLETED || txError ? (
|
|
13
|
+
<TxResult onCloseClick={onCloseClick} />
|
|
14
|
+
) : (
|
|
15
|
+
<TxOverview onCloseClick={onCloseClick} />
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { TimerIcon } from "@src/assets/icons";
|
|
2
|
+
import { useSwapContext } from "@src/context";
|
|
3
|
+
|
|
4
|
+
export const DeliveryInfo = () => {
|
|
5
|
+
const { isExpressDeliveryActive } = useSwapContext();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex items-center">
|
|
9
|
+
<div
|
|
10
|
+
className={`w-4 h-4 mr-1 ${
|
|
11
|
+
isExpressDeliveryActive ? "fill-x_green_300" : "fill-white opacity-50"
|
|
12
|
+
} `}
|
|
13
|
+
>
|
|
14
|
+
<TimerIcon />
|
|
15
|
+
</div>
|
|
16
|
+
<span
|
|
17
|
+
className={`text-xs font-medium sm:text-sm ${
|
|
18
|
+
isExpressDeliveryActive ? "text-x_green_300" : "opacity-50"
|
|
19
|
+
}`}
|
|
20
|
+
>
|
|
21
|
+
{isExpressDeliveryActive ? "Fast ~ 30sec " : "Normal ~ 30min"}
|
|
22
|
+
</span>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Skeleton } from "@src/components";
|
|
2
|
+
import { useSwapContext } from "@src/context";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
name: string;
|
|
6
|
+
value: string;
|
|
7
|
+
};
|
|
8
|
+
export const Fee = ({ name, value }: Props) => {
|
|
9
|
+
const { isFetchingRoute } = useSwapContext();
|
|
10
|
+
return (
|
|
11
|
+
<div className="flex gap-x-1 justify-between text-xs font-medium">
|
|
12
|
+
<div className="opacity-60">{name}</div>
|
|
13
|
+
<div>
|
|
14
|
+
{isFetchingRoute ? <Skeleton className="w-[60px] h-full" /> : value}
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useSwapContext } from "@src/context";
|
|
2
|
+
import { weiToHumanReadable } from "@src/utils";
|
|
3
|
+
import { FC } from "react";
|
|
4
|
+
import { Fee } from "../Fee";
|
|
5
|
+
|
|
6
|
+
export const Fees: FC = () => {
|
|
7
|
+
const { route, slippage, feeToken, dstToken, expressDelivery } =
|
|
8
|
+
useSwapContext();
|
|
9
|
+
|
|
10
|
+
const fees = [
|
|
11
|
+
{
|
|
12
|
+
name: "CCIP Fee: ",
|
|
13
|
+
value: `${
|
|
14
|
+
weiToHumanReadable({
|
|
15
|
+
amount: route?.xSwapFees.ccipFee || "0",
|
|
16
|
+
decimals: feeToken?.decimals || 18,
|
|
17
|
+
precisionFractionalPlaces: 5,
|
|
18
|
+
}) || 0
|
|
19
|
+
} ${feeToken?.symbol}`,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "Native Fee: ",
|
|
23
|
+
value: `${
|
|
24
|
+
weiToHumanReadable({
|
|
25
|
+
amount: route?.xSwapFees.xSwapFee.nativeFee || "0",
|
|
26
|
+
decimals: feeToken?.decimals || 18,
|
|
27
|
+
precisionFractionalPlaces: 5,
|
|
28
|
+
}) || 0
|
|
29
|
+
} ${feeToken?.symbol}`,
|
|
30
|
+
},
|
|
31
|
+
...(expressDelivery
|
|
32
|
+
? [
|
|
33
|
+
{
|
|
34
|
+
name: "Express delivery Fee: ",
|
|
35
|
+
value: `${
|
|
36
|
+
weiToHumanReadable({
|
|
37
|
+
amount: route?.xSwapFees.expressDeliveryFee || "0",
|
|
38
|
+
decimals: feeToken?.decimals || 18,
|
|
39
|
+
precisionFractionalPlaces: 5,
|
|
40
|
+
}) || 0
|
|
41
|
+
} ${feeToken?.symbol}`,
|
|
42
|
+
},
|
|
43
|
+
]
|
|
44
|
+
: []),
|
|
45
|
+
{ name: "Slippage: ", value: `${slippage}%` },
|
|
46
|
+
{
|
|
47
|
+
name: "Min out: ",
|
|
48
|
+
value: `${
|
|
49
|
+
weiToHumanReadable({
|
|
50
|
+
amount: route?.minAmountOut || "0",
|
|
51
|
+
decimals: dstToken?.decimals || 18,
|
|
52
|
+
precisionFractionalPlaces: 5,
|
|
53
|
+
}) || 0
|
|
54
|
+
} ${dstToken?.symbol}`,
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className="border-t border-solid border-white border-opacity-10 pt-3 mt-3">
|
|
60
|
+
<div
|
|
61
|
+
className={`flex flex-col gap-y-2 gap-x-10 sm:grid ${
|
|
62
|
+
fees.length === 4 ? "sm:grid-rows-2" : "sm:grid-rows-3"
|
|
63
|
+
} sm:grid-flow-col`}
|
|
64
|
+
>
|
|
65
|
+
{fees.map(({ name, value }) => (
|
|
66
|
+
<Fee key={name} name={name} value={value} />
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ArrowDownIcon, TokensIcon } from "@src/assets/icons";
|
|
2
|
+
import { Skeleton } from "@src/components";
|
|
3
|
+
import { useSwapContext } from "@src/context";
|
|
4
|
+
import { safeBigNumberFrom, weiToHumanReadable } from "@src/utils";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { DeliveryInfo } from "./DeliveryInfo";
|
|
7
|
+
import { Fees } from "./Fees";
|
|
8
|
+
|
|
9
|
+
export const FeesPanel = () => {
|
|
10
|
+
const [expanded, setExpanded] = useState(false);
|
|
11
|
+
|
|
12
|
+
const { route, feeToken, isFetchingRoute, srcChain, dstChain } =
|
|
13
|
+
useSwapContext();
|
|
14
|
+
|
|
15
|
+
const totalFee = useMemo(() => {
|
|
16
|
+
if (!route) {
|
|
17
|
+
return "0";
|
|
18
|
+
}
|
|
19
|
+
const totalWei = safeBigNumberFrom(route.xSwapFees.ccipFee || "0")
|
|
20
|
+
.add(safeBigNumberFrom(route.xSwapFees.xSwapFee.nativeFee || "0"))
|
|
21
|
+
.add(safeBigNumberFrom(route.xSwapFees.expressDeliveryFee || "0"))
|
|
22
|
+
.toString();
|
|
23
|
+
|
|
24
|
+
return weiToHumanReadable({
|
|
25
|
+
amount: totalWei,
|
|
26
|
+
decimals: feeToken?.decimals || 18,
|
|
27
|
+
precisionFractionalPlaces: 5,
|
|
28
|
+
});
|
|
29
|
+
}, [feeToken?.decimals, route]);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="flex flex-col x-border rounded-xl py-2 px-4 bg-x_gray_500">
|
|
33
|
+
<button
|
|
34
|
+
aria-label={`${expanded ? "Collapse" : "Expand"} fees panel`}
|
|
35
|
+
type="button"
|
|
36
|
+
onClick={() => setExpanded((e) => !e)}
|
|
37
|
+
>
|
|
38
|
+
<div className="flex justify-between">
|
|
39
|
+
<div className="flex gap-2 items-center text-xs font-medium sm:text-sm">
|
|
40
|
+
<TokensIcon />
|
|
41
|
+
<p className="opacity-60">Fee: </p>
|
|
42
|
+
{isFetchingRoute ? (
|
|
43
|
+
<Skeleton className="w-[100px] h-full" />
|
|
44
|
+
) : (
|
|
45
|
+
<p className="opacity-60">{`${totalFee} ${feeToken?.symbol}`}</p>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
<div className="flex gap-2 items-center">
|
|
49
|
+
{route &&
|
|
50
|
+
!isFetchingRoute &&
|
|
51
|
+
srcChain?.chainId !== dstChain?.chainId && <DeliveryInfo />}
|
|
52
|
+
{expanded ? (
|
|
53
|
+
<div className="w-4 h-2 rotate-180">
|
|
54
|
+
<ArrowDownIcon />
|
|
55
|
+
</div>
|
|
56
|
+
) : (
|
|
57
|
+
<div className="w-4 h-2">
|
|
58
|
+
<ArrowDownIcon />
|
|
59
|
+
</div>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
{expanded && <Fees />}
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { useSwapContext, useTxUIWrapper } from "@src/context";
|
|
2
|
+
import { TxStatus } from "@src/models";
|
|
3
|
+
import { safeBigNumberFrom } from "@src/utils";
|
|
4
|
+
import { useCallback, useMemo } from "react";
|
|
5
|
+
import { useAccount, useSwitchChain } from "wagmi";
|
|
6
|
+
|
|
7
|
+
type Button = {
|
|
8
|
+
text: string;
|
|
9
|
+
fn: () => void;
|
|
10
|
+
disabled: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const SwapButton = () => {
|
|
14
|
+
const { address, chainId } = useAccount();
|
|
15
|
+
const { switchChainAsync } = useSwitchChain();
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
error,
|
|
19
|
+
srcChain,
|
|
20
|
+
dstChain,
|
|
21
|
+
srcToken,
|
|
22
|
+
dstToken,
|
|
23
|
+
srcValueWei,
|
|
24
|
+
srcTokenBalanceWei,
|
|
25
|
+
isFetchingRoute,
|
|
26
|
+
} = useSwapContext();
|
|
27
|
+
|
|
28
|
+
const { setTxStatus, setTxMsg, setTxModalOpen } = useTxUIWrapper();
|
|
29
|
+
|
|
30
|
+
const srcChainId = useMemo(() => srcChain?.chainId, [srcChain?.chainId]);
|
|
31
|
+
const dstChainId = useMemo(() => dstChain?.chainId, [dstChain?.chainId]);
|
|
32
|
+
const srcTokenAddress = useMemo(() => srcToken?.address, [srcToken?.address]);
|
|
33
|
+
const dstTokenAddress = useMemo(() => dstToken?.address, [dstToken?.address]);
|
|
34
|
+
|
|
35
|
+
const showSwapConfirmationView = useCallback(() => {
|
|
36
|
+
setTxStatus(TxStatus.SWAP_INIT);
|
|
37
|
+
setTxMsg("");
|
|
38
|
+
setTxModalOpen(true);
|
|
39
|
+
}, [setTxModalOpen, setTxMsg, setTxStatus]);
|
|
40
|
+
|
|
41
|
+
const button: Button = useMemo(() => {
|
|
42
|
+
const disabled = false;
|
|
43
|
+
|
|
44
|
+
if (!srcChainId || !dstChainId || !srcTokenAddress || !dstTokenAddress) {
|
|
45
|
+
return {
|
|
46
|
+
text: "Loading XPay...",
|
|
47
|
+
fn: () => {},
|
|
48
|
+
disabled: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (error) {
|
|
53
|
+
return {
|
|
54
|
+
text: error,
|
|
55
|
+
fn: () => {},
|
|
56
|
+
disabled: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!address) {
|
|
61
|
+
return {
|
|
62
|
+
text: "Connect wallet first",
|
|
63
|
+
fn: () => {},
|
|
64
|
+
disabled: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (srcChainId === dstChainId && srcTokenAddress === dstTokenAddress) {
|
|
69
|
+
return {
|
|
70
|
+
text: "Change token",
|
|
71
|
+
fn: () => {},
|
|
72
|
+
disabled: true,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (chainId?.toString() !== srcChainId) {
|
|
77
|
+
return {
|
|
78
|
+
text: "Change chain",
|
|
79
|
+
fn: async () => await switchChainAsync({ chainId: Number(srcChainId) }),
|
|
80
|
+
disabled,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (
|
|
85
|
+
srcValueWei &&
|
|
86
|
+
srcTokenBalanceWei &&
|
|
87
|
+
safeBigNumberFrom(srcValueWei).gt(safeBigNumberFrom(srcTokenBalanceWei))
|
|
88
|
+
) {
|
|
89
|
+
return {
|
|
90
|
+
text: "Insufficient balance",
|
|
91
|
+
fn: () => {},
|
|
92
|
+
disabled: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!srcTokenAddress || !dstTokenAddress) {
|
|
97
|
+
return {
|
|
98
|
+
text: `Select token`,
|
|
99
|
+
fn: () => {},
|
|
100
|
+
disabled: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (safeBigNumberFrom(srcValueWei || "0").lte(safeBigNumberFrom("0"))) {
|
|
105
|
+
return {
|
|
106
|
+
text: `Enter token amount`,
|
|
107
|
+
fn: () => {},
|
|
108
|
+
disabled: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (isFetchingRoute) {
|
|
113
|
+
return {
|
|
114
|
+
text: `Fetching route`,
|
|
115
|
+
fn: () => {},
|
|
116
|
+
disabled: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
text: "Swap",
|
|
122
|
+
fn: async () => {
|
|
123
|
+
showSwapConfirmationView();
|
|
124
|
+
},
|
|
125
|
+
disabled: false,
|
|
126
|
+
};
|
|
127
|
+
}, [
|
|
128
|
+
dstChainId,
|
|
129
|
+
dstTokenAddress,
|
|
130
|
+
error,
|
|
131
|
+
address,
|
|
132
|
+
chainId,
|
|
133
|
+
isFetchingRoute,
|
|
134
|
+
showSwapConfirmationView,
|
|
135
|
+
srcChainId,
|
|
136
|
+
srcTokenAddress,
|
|
137
|
+
srcTokenBalanceWei,
|
|
138
|
+
srcValueWei,
|
|
139
|
+
switchChainAsync,
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<button
|
|
144
|
+
type="button"
|
|
145
|
+
disabled={button.disabled}
|
|
146
|
+
onClick={button.fn}
|
|
147
|
+
className={`text-white border-none text-xl w-full py-5 cursor-pointer bg-gradient-to-r from-x_blue_300 to-x_blue_400 rounded-2xl ${
|
|
148
|
+
button.disabled && "bg-none bg-x_gray_500"
|
|
149
|
+
}`}
|
|
150
|
+
>
|
|
151
|
+
{button.text}
|
|
152
|
+
</button>
|
|
153
|
+
);
|
|
154
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Skeleton } from "@src/components";
|
|
2
|
+
import { useSwapContext } from "@src/context";
|
|
3
|
+
|
|
4
|
+
export const Balance = () => {
|
|
5
|
+
const { srcTokenBalanceInfo, isFetchingBalance } = useSwapContext();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex flex-col items-end">
|
|
9
|
+
<p className="text-xs font-medium opacity-60">Balance:</p>
|
|
10
|
+
{isFetchingBalance ? (
|
|
11
|
+
<Skeleton className="w-[50px] h-[16px]" />
|
|
12
|
+
) : (
|
|
13
|
+
<p
|
|
14
|
+
className={`text-xs font-medium ${
|
|
15
|
+
!srcTokenBalanceInfo && "opacity-60"
|
|
16
|
+
}`}
|
|
17
|
+
>
|
|
18
|
+
{srcTokenBalanceInfo || "-"}
|
|
19
|
+
</p>
|
|
20
|
+
)}
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { SafeInput, Skeleton } from "@src/components";
|
|
2
|
+
import { NUMBER_INPUT_REGEX } from "@src/constants";
|
|
3
|
+
import { useSwapContext } from "@src/context";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
import { SwapPanelType } from "../../../SwapView";
|
|
6
|
+
import { Balance } from "./Balance";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
type: SwapPanelType;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const AmountPanel = ({ type }: Props) => {
|
|
13
|
+
const {
|
|
14
|
+
srcValue,
|
|
15
|
+
srcValueUsd,
|
|
16
|
+
dstValue,
|
|
17
|
+
dstValueUsd,
|
|
18
|
+
setSrcValue,
|
|
19
|
+
isFetchingRoute,
|
|
20
|
+
} = useSwapContext();
|
|
21
|
+
|
|
22
|
+
const valueUsd = useMemo(
|
|
23
|
+
() => (type === "source" ? srcValueUsd : dstValueUsd),
|
|
24
|
+
[dstValueUsd, srcValueUsd, type],
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex justify-between items-center w-full p-4 gap-8 overflow-hidden">
|
|
29
|
+
<div className="flex flex-col font-medium w-full overflow-hidden">
|
|
30
|
+
{type === "source" ? (
|
|
31
|
+
<>
|
|
32
|
+
<SafeInput
|
|
33
|
+
id="swap-amount-input"
|
|
34
|
+
className={`bg-transparent border-none p-0 text-xl outline-0`}
|
|
35
|
+
regex={NUMBER_INPUT_REGEX}
|
|
36
|
+
value={srcValue}
|
|
37
|
+
onChange={(value) => setSrcValue(value)}
|
|
38
|
+
/>
|
|
39
|
+
<div className={`text-sx opacity-60`}>{`$${valueUsd || 0}`}</div>
|
|
40
|
+
</>
|
|
41
|
+
) : isFetchingRoute ? (
|
|
42
|
+
<div className="flex flex-col gap-1">
|
|
43
|
+
<Skeleton className="w-[150px] h-[30px]" />
|
|
44
|
+
<Skeleton className="w-[100px] h-[20px]" />
|
|
45
|
+
</div>
|
|
46
|
+
) : (
|
|
47
|
+
<>
|
|
48
|
+
<div className={`text-xl text-x_blue_300`}>{dstValue || 0}</div>
|
|
49
|
+
<div className={`text-sx opacity-60`}>{`$${valueUsd || 0}`}</div>
|
|
50
|
+
</>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
{type === "source" && <Balance />}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { DotGreenIcon, HelpIcon } from "@src/assets/icons";
|
|
2
|
+
import { Tooltip } from "@src/components";
|
|
3
|
+
import { useSwapContext } from "@src/context";
|
|
4
|
+
import { Chain } from "@src/models";
|
|
5
|
+
import { useMemo, useRef } from "react";
|
|
6
|
+
import { SwapPanelType } from "../../../../../SwapView";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
chain: Chain;
|
|
10
|
+
type: SwapPanelType;
|
|
11
|
+
disabledChain?: string;
|
|
12
|
+
onClick: (chainId: string) => void;
|
|
13
|
+
};
|
|
14
|
+
export const ChainItem = ({ chain, type, disabledChain, onClick }: Props) => {
|
|
15
|
+
// todo check useModal, we could improve this part to have the react state here, right now this line throws an error
|
|
16
|
+
// const { address, chainId } = useAccount();
|
|
17
|
+
|
|
18
|
+
const tooltipTriggerRef = useRef<SVGSVGElement>(null);
|
|
19
|
+
|
|
20
|
+
const { srcChain, dstChain } = useSwapContext();
|
|
21
|
+
const currentChainId = useMemo(
|
|
22
|
+
() => (type === "source" ? srcChain?.chainId : dstChain?.chainId),
|
|
23
|
+
[dstChain, srcChain, type],
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const connected = false; //address && chainId?.toString() === chain.chainId;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
onClick={() => {
|
|
31
|
+
if (chain.chainId === disabledChain) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
onClick(chain.chainId);
|
|
35
|
+
}}
|
|
36
|
+
className={`flex justify-between gap-3 mt-2 mb-2 cursor-pointer p-4 border border-solid border-white border-opacity-0 hover:selected-chain-item ${
|
|
37
|
+
currentChainId === chain.chainId && "selected-chain-item"
|
|
38
|
+
} ${
|
|
39
|
+
chain.chainId === disabledChain && "disabled-chain-item cursor-default"
|
|
40
|
+
}`}
|
|
41
|
+
>
|
|
42
|
+
<div className="flex gap-3 items-center w-full">
|
|
43
|
+
<img src={chain.image} alt={chain.name} className="w-[34px] h-[34px]" />
|
|
44
|
+
<p>{chain.displayName}</p>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{connected && (
|
|
48
|
+
<div className="flex items-center gap-1">
|
|
49
|
+
<DotGreenIcon />
|
|
50
|
+
<div className="text-x_green_300">connected</div>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
{chain.chainId === disabledChain && (
|
|
54
|
+
<div className="flex items-center gap-1">
|
|
55
|
+
<Tooltip
|
|
56
|
+
text="Transfers between same chain not available"
|
|
57
|
+
position="BOTTOM"
|
|
58
|
+
triggerRef={tooltipTriggerRef}
|
|
59
|
+
id="transfers-between-same-chains-tooltip"
|
|
60
|
+
/>
|
|
61
|
+
|
|
62
|
+
<HelpIcon
|
|
63
|
+
ref={tooltipTriggerRef}
|
|
64
|
+
className="cursor-help fill-x_blue_300 w-[18px] h-[18px]"
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CloseIcon } from "@src/assets/icons";
|
|
2
|
+
import { useModal } from "@src/context/ModalProvider";
|
|
3
|
+
import { Chain, Web3Environment } from "@src/models";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
import { Virtuoso } from "react-virtuoso";
|
|
6
|
+
import { SwapPanelType } from "../../../../SwapView";
|
|
7
|
+
import { ChainItem } from "./ChainItem";
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
type: SwapPanelType;
|
|
11
|
+
supportedChains: Chain[];
|
|
12
|
+
setSrcChain: (chain: Chain | undefined) => void;
|
|
13
|
+
setDstChain: (chain: Chain | undefined) => void;
|
|
14
|
+
};
|
|
15
|
+
export const ChainPicker = ({
|
|
16
|
+
type,
|
|
17
|
+
supportedChains,
|
|
18
|
+
setSrcChain,
|
|
19
|
+
setDstChain,
|
|
20
|
+
}: Props) => {
|
|
21
|
+
const { closeModal } = useModal();
|
|
22
|
+
|
|
23
|
+
const filteredChains = useMemo(() => {
|
|
24
|
+
return supportedChains.filter(
|
|
25
|
+
({ web3Environment, swapSupported }) =>
|
|
26
|
+
web3Environment === Web3Environment.MAINNET && swapSupported,
|
|
27
|
+
);
|
|
28
|
+
}, [supportedChains]);
|
|
29
|
+
|
|
30
|
+
const select = (selectedChainId: string) => {
|
|
31
|
+
const chain = supportedChains.find(
|
|
32
|
+
({ chainId }) => chainId === selectedChainId,
|
|
33
|
+
);
|
|
34
|
+
type === "source" ? setSrcChain(chain) : setDstChain(chain);
|
|
35
|
+
closeModal();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="flex flex-col h-full">
|
|
40
|
+
<div className="flex justify-between">
|
|
41
|
+
<div className="text-base/4 mb-4">{`Pick ${type} chain`}</div>
|
|
42
|
+
<div className="w-4 h-4 fill-white cursor-pointer" onClick={closeModal}>
|
|
43
|
+
<CloseIcon />
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
<div className="horizontal-separator" />
|
|
47
|
+
<div className="h-full pt-4">
|
|
48
|
+
{filteredChains.length ? (
|
|
49
|
+
<Virtuoso
|
|
50
|
+
style={{ height: "400px" }}
|
|
51
|
+
data={filteredChains}
|
|
52
|
+
itemContent={(_, chain) => (
|
|
53
|
+
<ChainItem chain={chain} type={type} onClick={select} />
|
|
54
|
+
)}
|
|
55
|
+
/>
|
|
56
|
+
) : (
|
|
57
|
+
<div className="flex justify-center mt-4">"No chain found."</div>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
};
|