@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,80 @@
|
|
|
1
|
+
import { SafeInput } from "@src/components";
|
|
2
|
+
import { NUMBER_INPUT_REGEX } from "@src/constants";
|
|
3
|
+
import { useSwapContext } from "@src/context";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
|
|
6
|
+
export const Slippage = () => {
|
|
7
|
+
const { slippage, setSlippage } = useSwapContext();
|
|
8
|
+
const [customSlippage, setCustomSlippage] = useState(slippage);
|
|
9
|
+
|
|
10
|
+
const onInputChange = (value: string) => {
|
|
11
|
+
if (value.length === 0) {
|
|
12
|
+
setCustomSlippage("");
|
|
13
|
+
setSlippage(defaultPreset);
|
|
14
|
+
} else {
|
|
15
|
+
if (value.length >= 5) {
|
|
16
|
+
value = value.substring(0, 5);
|
|
17
|
+
}
|
|
18
|
+
if (Number(value) <= 100) {
|
|
19
|
+
setCustomSlippage(value);
|
|
20
|
+
setSlippage(value);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const defaultPreset = "1.5";
|
|
26
|
+
const presets = ["0.5", "1.5", "3.0"];
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex flex-col gap-5 pt-5 border-t border-solid border-white border-opacity-10">
|
|
29
|
+
<h1 className="text-sm font-medium">Slippage</h1>
|
|
30
|
+
<div className="flex items-center gap-2 w-full">
|
|
31
|
+
<div className="flex items-center bg-x_gray_500 p-1 border border-solid border-white border-opacity-10 rounded-xl gap-2">
|
|
32
|
+
{presets.map((preset) => (
|
|
33
|
+
<button
|
|
34
|
+
key={preset}
|
|
35
|
+
type="button"
|
|
36
|
+
onClick={() => onInputChange(preset)}
|
|
37
|
+
className={`block rounded-lg text-sm bg-transparent text-white h-7 px-1 hover:bg-gradient-to-r hover:from-x_blue_300 hover:to-x_blue_400 hover:opacity-100 ${
|
|
38
|
+
slippage === preset
|
|
39
|
+
? "bg-gradient-to-r from-x_blue_300 to-x_blue_400 opacity-100"
|
|
40
|
+
: "opacity-20"
|
|
41
|
+
}`}
|
|
42
|
+
>
|
|
43
|
+
{`${preset}%`}
|
|
44
|
+
</button>
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
<div
|
|
48
|
+
className={`${
|
|
49
|
+
customSlippage ? "gradient-border-container-blue rounded-xl" : ""
|
|
50
|
+
}`}
|
|
51
|
+
>
|
|
52
|
+
<div
|
|
53
|
+
className={`bg-x_gray_500 p-1 border border-solid border-white border-opacity-10 rounded-xl`}
|
|
54
|
+
>
|
|
55
|
+
<SafeInput
|
|
56
|
+
id="slippage-input"
|
|
57
|
+
placeholder="1.5"
|
|
58
|
+
regex={NUMBER_INPUT_REGEX}
|
|
59
|
+
value={customSlippage}
|
|
60
|
+
onChange={onInputChange}
|
|
61
|
+
className="text-sm leading-none bg-transparent px-0.5 text-white placeholder:text-white/20 focus:outline-none"
|
|
62
|
+
/>{" "}
|
|
63
|
+
%
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="text-sm opacity-60">
|
|
68
|
+
<p>
|
|
69
|
+
Slippage is the price variation you are willing to accept in the event
|
|
70
|
+
that the price of the trade changes while it is processing.
|
|
71
|
+
</p>
|
|
72
|
+
<br />
|
|
73
|
+
<p>
|
|
74
|
+
If the trade fails due to too low slippage, you will receive USDC on
|
|
75
|
+
the destination chain.
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ArrowDownLongIcon } from "@src/assets/icons";
|
|
2
|
+
|
|
3
|
+
export const ArrowIcon = () => {
|
|
4
|
+
return (
|
|
5
|
+
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 p-1 border border-solid border-white border-opacity-10 rounded-xl bg-x_gray_500">
|
|
6
|
+
<div className="relative bg-gradient-to-r from-x_blue_300 to-x_blue_400 w-8 h-8 rounded-xl">
|
|
7
|
+
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 fill-white">
|
|
8
|
+
<ArrowDownLongIcon />
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CloseIcon, HelpIcon } from "@src/assets/icons";
|
|
2
|
+
import { Tooltip } from "@src/components/Tooltip/index";
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
onCloseClick: () => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const Header = ({ onCloseClick }: Props) => {
|
|
10
|
+
const tooltipTriggerRef = useRef<SVGSVGElement>(null);
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="flex justify-between items-center text-base font-medium">
|
|
14
|
+
<div>Confirmation</div>
|
|
15
|
+
<div className="flex gap-4 items-center">
|
|
16
|
+
<Tooltip
|
|
17
|
+
text="To sign the transaction open the wallet you have used to connect with XSwap."
|
|
18
|
+
position="BOTTOM"
|
|
19
|
+
triggerRef={tooltipTriggerRef}
|
|
20
|
+
id="connect-wallet-to-sign-tx-tooltip"
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
<HelpIcon
|
|
24
|
+
ref={tooltipTriggerRef}
|
|
25
|
+
className="cursor-help fill-x_blue_300 w-[18px] h-[18px]"
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<div
|
|
29
|
+
className="w-4 h-4 fill-white cursor-pointer"
|
|
30
|
+
onClick={onCloseClick}
|
|
31
|
+
>
|
|
32
|
+
<CloseIcon />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CheckIcon,
|
|
3
|
+
SignIcon,
|
|
4
|
+
ThumbUpRoundedIcon,
|
|
5
|
+
WaitingIcon,
|
|
6
|
+
} from "@src/assets/icons";
|
|
7
|
+
import { useTxUIWrapper } from "@src/context";
|
|
8
|
+
import { TxStatus } from "@src/models";
|
|
9
|
+
import { ReactElement, useMemo } from "react";
|
|
10
|
+
|
|
11
|
+
type Step = {
|
|
12
|
+
icon: (className: string) => ReactElement;
|
|
13
|
+
title: string;
|
|
14
|
+
state: "none" | "triggered" | "completed";
|
|
15
|
+
show: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// TODO item's 'show' logic is a bit complicated/weird. Think about an improvement...
|
|
19
|
+
export const Steps = () => {
|
|
20
|
+
const { txStatus, txNeedsApproval, txMsg } = useTxUIWrapper();
|
|
21
|
+
|
|
22
|
+
const items: Step[] = useMemo(
|
|
23
|
+
() => [
|
|
24
|
+
{
|
|
25
|
+
icon: (className) => (
|
|
26
|
+
<div className={`fill-white ${className}`}>
|
|
27
|
+
<SignIcon />
|
|
28
|
+
</div>
|
|
29
|
+
),
|
|
30
|
+
title: "sign approval transaction",
|
|
31
|
+
state:
|
|
32
|
+
txStatus === TxStatus.SIGNING_APPROVAL
|
|
33
|
+
? "triggered"
|
|
34
|
+
: txStatus > TxStatus.SIGNING_APPROVAL
|
|
35
|
+
? "completed"
|
|
36
|
+
: "none",
|
|
37
|
+
show: txMsg
|
|
38
|
+
? txStatus === TxStatus.SIGNING_APPROVAL ||
|
|
39
|
+
txStatus === TxStatus.CONFIRMING_APPROVAL
|
|
40
|
+
: txNeedsApproval,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
icon: (className) => (
|
|
44
|
+
<div className={`fill-white ${className}`}>
|
|
45
|
+
<ThumbUpRoundedIcon />
|
|
46
|
+
</div>
|
|
47
|
+
),
|
|
48
|
+
title: "confirming approval transaction...",
|
|
49
|
+
state:
|
|
50
|
+
txStatus === TxStatus.CONFIRMING_APPROVAL
|
|
51
|
+
? "triggered"
|
|
52
|
+
: txStatus > TxStatus.CONFIRMING_APPROVAL
|
|
53
|
+
? "completed"
|
|
54
|
+
: "none",
|
|
55
|
+
show: txMsg
|
|
56
|
+
? txStatus === TxStatus.SIGNING_APPROVAL ||
|
|
57
|
+
txStatus === TxStatus.CONFIRMING_APPROVAL
|
|
58
|
+
: txNeedsApproval,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
icon: (className) => (
|
|
62
|
+
<div className={`fill-white ${className}`}>
|
|
63
|
+
<SignIcon />
|
|
64
|
+
</div>
|
|
65
|
+
),
|
|
66
|
+
title: "sign transaction",
|
|
67
|
+
state:
|
|
68
|
+
txStatus === TxStatus.SIGNING_TX
|
|
69
|
+
? "triggered"
|
|
70
|
+
: txStatus > TxStatus.SIGNING_TX
|
|
71
|
+
? "completed"
|
|
72
|
+
: "none",
|
|
73
|
+
show: txMsg
|
|
74
|
+
? txStatus === TxStatus.SIGNING_TX ||
|
|
75
|
+
txStatus === TxStatus.CONFIRMING_TX
|
|
76
|
+
: true,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
icon: (className) => (
|
|
80
|
+
<div className={`fill-white ${className}`}>
|
|
81
|
+
<ThumbUpRoundedIcon />
|
|
82
|
+
</div>
|
|
83
|
+
),
|
|
84
|
+
title: "confirming transaction...",
|
|
85
|
+
state:
|
|
86
|
+
txStatus === TxStatus.CONFIRMING_TX
|
|
87
|
+
? "triggered"
|
|
88
|
+
: txStatus > TxStatus.CONFIRMING_TX
|
|
89
|
+
? "completed"
|
|
90
|
+
: "none",
|
|
91
|
+
show: txMsg
|
|
92
|
+
? txStatus === TxStatus.SIGNING_TX ||
|
|
93
|
+
txStatus === TxStatus.CONFIRMING_TX
|
|
94
|
+
: true,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
[txMsg, txNeedsApproval, txStatus],
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const filteredItems = useMemo(
|
|
101
|
+
() => items.filter(({ show }) => show),
|
|
102
|
+
[items],
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div className="flex flex-col gap-1">
|
|
107
|
+
{filteredItems.map(({ icon, title, state }, index) => (
|
|
108
|
+
<div key={title} className="flex flex-col px-4 gap-1">
|
|
109
|
+
<div className="flex justify-between items-center">
|
|
110
|
+
<div className="flex items-center gap-2">
|
|
111
|
+
{icon(
|
|
112
|
+
`fill-white ${
|
|
113
|
+
state === "triggered" ? "fill-x_blue_300" : "opacity-60"
|
|
114
|
+
}`,
|
|
115
|
+
)}
|
|
116
|
+
<div
|
|
117
|
+
className={`${
|
|
118
|
+
state !== "triggered"
|
|
119
|
+
? "opacity-60"
|
|
120
|
+
: state === "triggered" && "text-x_blue_300"
|
|
121
|
+
}`}
|
|
122
|
+
>
|
|
123
|
+
{title}
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
<div>
|
|
127
|
+
{state === "completed" ? (
|
|
128
|
+
<div className="fill-x_green_400">
|
|
129
|
+
<CheckIcon />
|
|
130
|
+
</div>
|
|
131
|
+
) : state === "triggered" ? (
|
|
132
|
+
<div className="animate-spin-slow fill-x_blue_300">
|
|
133
|
+
<WaitingIcon />
|
|
134
|
+
</div>
|
|
135
|
+
) : null}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
{index !== filteredItems.length - 1 && (
|
|
139
|
+
<div className="vertical-separator py-3 ml-2" />
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
))}
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TokenLogo } from "@src/components";
|
|
2
|
+
import { useSwapContext } from "@src/context";
|
|
3
|
+
import { SwapPanelType } from "../../../index";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
type: SwapPanelType;
|
|
8
|
+
};
|
|
9
|
+
export const SwapPanel = ({ type }: Props) => {
|
|
10
|
+
const {
|
|
11
|
+
srcToken,
|
|
12
|
+
srcChain,
|
|
13
|
+
dstToken,
|
|
14
|
+
dstChain,
|
|
15
|
+
srcValue,
|
|
16
|
+
srcValueUsd,
|
|
17
|
+
dstValue,
|
|
18
|
+
dstValueUsd,
|
|
19
|
+
} = useSwapContext();
|
|
20
|
+
|
|
21
|
+
const token = useMemo(
|
|
22
|
+
() => (type === "source" ? srcToken : dstToken),
|
|
23
|
+
[dstToken, srcToken, type],
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const chain = useMemo(
|
|
27
|
+
() => (type === "source" ? srcChain : dstChain),
|
|
28
|
+
[dstChain, srcChain, type],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const value = useMemo(
|
|
32
|
+
() => (type === "source" ? srcValue : dstValue),
|
|
33
|
+
[dstValue, srcValue, type],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const valueUsd = useMemo(
|
|
37
|
+
() => (type === "source" ? srcValueUsd : dstValueUsd),
|
|
38
|
+
[dstValueUsd, srcValueUsd, type],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="flex justify-between bg-gradient-to-r from-x_blue_300_20 to-x_blue_400_20 p-5 rounded-xl">
|
|
43
|
+
<div className="flex flex-col">
|
|
44
|
+
<div className="opacity-60 text-sm mb-2">{`You ${
|
|
45
|
+
type === "source" ? "pay" : "receive"
|
|
46
|
+
}`}</div>
|
|
47
|
+
<div className={`${type === "destination" && "text-x_blue_300"}`}>
|
|
48
|
+
{value}
|
|
49
|
+
</div>
|
|
50
|
+
<div className="opacity-60">${valueUsd}</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div className="flex items-center gap-3">
|
|
53
|
+
<div className="flex flex-col text-right">
|
|
54
|
+
<div>{token?.symbol}</div>
|
|
55
|
+
<div className="opacity-60">{chain?.displayName}</div>
|
|
56
|
+
</div>
|
|
57
|
+
<TokenLogo token={token} className="w-[34px]" />
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { Button } from "@src/components";
|
|
2
|
+
import {
|
|
3
|
+
FALLBACK_CROSSCHAIN_ESTIMATION_TIME,
|
|
4
|
+
crosschainEstimationTimes,
|
|
5
|
+
} from "@src/constants";
|
|
6
|
+
import { useHistory, useSwapContext, useTxUIWrapper } from "@src/context";
|
|
7
|
+
import {
|
|
8
|
+
ADDRESSES,
|
|
9
|
+
CustomXSwapRouterAbi,
|
|
10
|
+
XSwapRouterAbi,
|
|
11
|
+
} from "@src/contracts";
|
|
12
|
+
import { useEvmContractApi } from "@src/hooks";
|
|
13
|
+
import useNetworks from "@src/hooks/networkManagement/useNetworks";
|
|
14
|
+
import { Ecosystem, Transaction, TxStatus } from "@src/models";
|
|
15
|
+
import { renderTxStatus } from "@src/services";
|
|
16
|
+
import { ContractReceipt, constants, ethers } from "ethers";
|
|
17
|
+
import { useCallback } from "react";
|
|
18
|
+
import { FeesPanel } from "../../FeesPanel";
|
|
19
|
+
import { ArrowIcon } from "./ArrowIcon";
|
|
20
|
+
import { Header } from "./Header";
|
|
21
|
+
import { Steps } from "./Steps";
|
|
22
|
+
import { SwapPanel } from "./SwapPanel";
|
|
23
|
+
|
|
24
|
+
type Props = {
|
|
25
|
+
onCloseClick: () => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const TxOverview = ({ onCloseClick }: Props) => {
|
|
29
|
+
const {
|
|
30
|
+
supportedChains,
|
|
31
|
+
srcChain,
|
|
32
|
+
srcToken,
|
|
33
|
+
dstChain,
|
|
34
|
+
dstToken,
|
|
35
|
+
srcValueWei,
|
|
36
|
+
dstValueWei,
|
|
37
|
+
route,
|
|
38
|
+
refreshBalance,
|
|
39
|
+
} = useSwapContext();
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
txStatus,
|
|
43
|
+
setTxStatus,
|
|
44
|
+
txMsg,
|
|
45
|
+
enqueueTransaction,
|
|
46
|
+
setTxNeedsApproval,
|
|
47
|
+
} = useTxUIWrapper();
|
|
48
|
+
|
|
49
|
+
const { addTransactionToLocalHistory, updateTransactionInLocalHistory } =
|
|
50
|
+
useHistory();
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
networks: {
|
|
54
|
+
evm: { signer },
|
|
55
|
+
},
|
|
56
|
+
} = useNetworks();
|
|
57
|
+
|
|
58
|
+
const evmContractApi = useEvmContractApi();
|
|
59
|
+
|
|
60
|
+
const trackTransaction = useCallback(
|
|
61
|
+
(txReceipt: ContractReceipt) => {
|
|
62
|
+
if (!srcChain || !dstChain || !srcToken || !dstToken || !route) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const transactionData: Transaction = {
|
|
67
|
+
hash: txReceipt.transactionHash,
|
|
68
|
+
timestamp: Date.now() / 1000,
|
|
69
|
+
sourceChainId: srcChain.chainId,
|
|
70
|
+
targetChainId: dstChain.chainId,
|
|
71
|
+
amountWei: srcValueWei,
|
|
72
|
+
tokenAddress: srcToken.address,
|
|
73
|
+
tokenOutAddress: dstToken?.address,
|
|
74
|
+
tokenOutAmount: dstValueWei,
|
|
75
|
+
|
|
76
|
+
estimatedDeliveryTimestamp:
|
|
77
|
+
Number(route.xSwapFees.expressDeliveryFee || "0") > 0 // express delivery fee TODO
|
|
78
|
+
? (Date.now() / 1000) * 1000 + 30 * 1000
|
|
79
|
+
: (Date.now() / 1000) * 1000 +
|
|
80
|
+
crosschainEstimationTimes?.[srcChain?.chainId]?.[
|
|
81
|
+
dstChain?.chainId
|
|
82
|
+
] || FALLBACK_CROSSCHAIN_ESTIMATION_TIME,
|
|
83
|
+
status: "IN_PROGRESS",
|
|
84
|
+
};
|
|
85
|
+
if (srcChain.chainId === dstChain.chainId) {
|
|
86
|
+
// single chain swap
|
|
87
|
+
transactionData.estimatedDeliveryTimestamp = transactionData.timestamp;
|
|
88
|
+
transactionData.status = "DONE";
|
|
89
|
+
} else {
|
|
90
|
+
// cross-chain swap - listening for destination chain event
|
|
91
|
+
const messageSentEventSignature =
|
|
92
|
+
"MessageSent(bytes32,uint64,address,bytes,address,uint256,uint256,address,uint256)"; // TODO think of getting this signature in a different way becase its easy to forgot about it when we will change event on smartcontract
|
|
93
|
+
const messageReceivedEventSignature =
|
|
94
|
+
"MessageReceived(bytes32,uint64,address,bytes,address,uint256)"; // TODO think of getting this signature in a different way because its easy to forgot about it when we will change event on smartcontract
|
|
95
|
+
// cross-chain bridge via custom xswap router
|
|
96
|
+
const messageSentXSwapCustomRouterEventSignature =
|
|
97
|
+
"MessageSent(bytes32,uint64,address,address,address,address,uint256)"; // TODO think of getting this signature in a different way becase its easy to forgot about it when we will change event on smartcontract
|
|
98
|
+
const messageReceivedXSwapCustomRouterEventSignature =
|
|
99
|
+
"MessageReceived(bytes32,uint64,address,bytes)"; // TODO think of getting this signature in a different way because its easy to forgot about it when we will change event on smartcontract
|
|
100
|
+
|
|
101
|
+
const messageSentEvent = txReceipt.logs?.find((log) => {
|
|
102
|
+
return log.topics[0] === ethers.utils.id(messageSentEventSignature);
|
|
103
|
+
});
|
|
104
|
+
const messageSentXSwapCustomRouterEvent = txReceipt.logs?.find(
|
|
105
|
+
(log) => {
|
|
106
|
+
return (
|
|
107
|
+
log.topics[0] ===
|
|
108
|
+
ethers.utils.id(messageSentXSwapCustomRouterEventSignature)
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const destinationChainProvider = ethers.getDefaultProvider(
|
|
114
|
+
supportedChains.find(
|
|
115
|
+
(chain) =>
|
|
116
|
+
chain.ecosystem === Ecosystem.EVM &&
|
|
117
|
+
chain.chainId === dstChain.chainId,
|
|
118
|
+
)?.publicRpcUrls[0],
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (messageSentEvent) {
|
|
122
|
+
// subscription for xswap router event
|
|
123
|
+
const messageId = messageSentEvent?.topics[1] as string;
|
|
124
|
+
transactionData.messageId = messageId;
|
|
125
|
+
|
|
126
|
+
const xSwapRouterOnDestination = new ethers.Contract(
|
|
127
|
+
// @ts-ignore
|
|
128
|
+
ADDRESSES[dstChain.chainId].XSwapRouter,
|
|
129
|
+
XSwapRouterAbi,
|
|
130
|
+
destinationChainProvider,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const eventFilter = {
|
|
134
|
+
// @ts-ignore
|
|
135
|
+
address: ADDRESSES[dstChain.chainId].XSwapRouter,
|
|
136
|
+
topics: [ethers.utils.id(messageReceivedEventSignature), messageId],
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
xSwapRouterOnDestination.once(eventFilter, () => {
|
|
140
|
+
transactionData.status = "DONE";
|
|
141
|
+
updateTransactionInLocalHistory(transactionData, txReceipt.from);
|
|
142
|
+
});
|
|
143
|
+
} else if (messageSentXSwapCustomRouterEvent) {
|
|
144
|
+
// subscription for custom router event
|
|
145
|
+
const messageId = messageSentXSwapCustomRouterEvent
|
|
146
|
+
?.topics[1] as string;
|
|
147
|
+
transactionData.messageId = messageId;
|
|
148
|
+
|
|
149
|
+
const xSwapRouterOnDestination = new ethers.Contract(
|
|
150
|
+
// @ts-ignore
|
|
151
|
+
ADDRESSES[dstChain.chainId].CustomXSwapRouter,
|
|
152
|
+
CustomXSwapRouterAbi,
|
|
153
|
+
destinationChainProvider,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const eventFilter = {
|
|
157
|
+
address: ADDRESSES[dstChain.chainId]?.CustomXSwapRouter,
|
|
158
|
+
topics: [
|
|
159
|
+
ethers.utils.id(messageReceivedXSwapCustomRouterEventSignature),
|
|
160
|
+
messageId,
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
xSwapRouterOnDestination.once(eventFilter, () => {
|
|
165
|
+
transactionData.status = "DONE";
|
|
166
|
+
updateTransactionInLocalHistory(transactionData, txReceipt.from);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
addTransactionToLocalHistory(transactionData, txReceipt.from);
|
|
171
|
+
},
|
|
172
|
+
[
|
|
173
|
+
addTransactionToLocalHistory,
|
|
174
|
+
updateTransactionInLocalHistory,
|
|
175
|
+
srcChain,
|
|
176
|
+
dstChain,
|
|
177
|
+
srcToken,
|
|
178
|
+
dstToken,
|
|
179
|
+
srcValueWei,
|
|
180
|
+
dstValueWei,
|
|
181
|
+
supportedChains,
|
|
182
|
+
route,
|
|
183
|
+
],
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const enqueueSwapTx = useCallback(async () => {
|
|
187
|
+
if (srcToken && route) {
|
|
188
|
+
const enqueueSwap = () => {
|
|
189
|
+
enqueueTransaction({
|
|
190
|
+
executeTransaction: () => {
|
|
191
|
+
if (!route.transactions.swap || !signer) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return evmContractApi.executeCalldata(
|
|
196
|
+
signer,
|
|
197
|
+
route.transactions.swap,
|
|
198
|
+
);
|
|
199
|
+
},
|
|
200
|
+
txStatus: TxStatus.SIGNING_TX,
|
|
201
|
+
handlers: {
|
|
202
|
+
onSuccess: (data) => {
|
|
203
|
+
setTxStatus(TxStatus.COMPLETED);
|
|
204
|
+
refreshBalance();
|
|
205
|
+
trackTransaction(data);
|
|
206
|
+
if (
|
|
207
|
+
srcChain?.chainId &&
|
|
208
|
+
dstChain?.chainId &&
|
|
209
|
+
srcChain?.chainId !== dstChain?.chainId
|
|
210
|
+
) {
|
|
211
|
+
renderTxStatus(
|
|
212
|
+
srcChain?.chainId,
|
|
213
|
+
data.transactionHash,
|
|
214
|
+
dstToken?.address,
|
|
215
|
+
dstValueWei,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
if (
|
|
224
|
+
srcToken.address !== constants.AddressZero &&
|
|
225
|
+
route.transactions.approve
|
|
226
|
+
) {
|
|
227
|
+
setTxNeedsApproval(true);
|
|
228
|
+
enqueueTransaction({
|
|
229
|
+
executeTransaction: () => {
|
|
230
|
+
if (!route.transactions.approve || !signer) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
return evmContractApi.executeCalldata(
|
|
234
|
+
signer,
|
|
235
|
+
route.transactions.approve,
|
|
236
|
+
);
|
|
237
|
+
},
|
|
238
|
+
txStatus: TxStatus.SIGNING_APPROVAL,
|
|
239
|
+
handlers: {
|
|
240
|
+
onSuccess: () => enqueueSwap(),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
setTxNeedsApproval(false);
|
|
245
|
+
enqueueSwap();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}, [
|
|
249
|
+
evmContractApi,
|
|
250
|
+
signer,
|
|
251
|
+
enqueueTransaction,
|
|
252
|
+
refreshBalance,
|
|
253
|
+
route,
|
|
254
|
+
setTxNeedsApproval,
|
|
255
|
+
setTxStatus,
|
|
256
|
+
srcToken,
|
|
257
|
+
trackTransaction,
|
|
258
|
+
]);
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<div className="flex flex-col text-white gap-5">
|
|
262
|
+
<Header onCloseClick={onCloseClick} />
|
|
263
|
+
{txMsg ? (
|
|
264
|
+
<div>{txMsg}</div>
|
|
265
|
+
) : (
|
|
266
|
+
<>
|
|
267
|
+
<div className="relative flex flex-col gap-5 fill-white">
|
|
268
|
+
<SwapPanel type="source" />
|
|
269
|
+
<ArrowIcon />
|
|
270
|
+
<SwapPanel type="destination" />
|
|
271
|
+
</div>
|
|
272
|
+
<FeesPanel />
|
|
273
|
+
</>
|
|
274
|
+
)}
|
|
275
|
+
{txStatus === TxStatus.SWAP_INIT ? (
|
|
276
|
+
<Button
|
|
277
|
+
variant="filled-squared"
|
|
278
|
+
onClick={() => {
|
|
279
|
+
enqueueSwapTx();
|
|
280
|
+
}}
|
|
281
|
+
type="button"
|
|
282
|
+
className="text-white border-none h-14 text-sm font-bold w-full py-5 cursor-pointer bg-gradient-to-r from-x_blue_300 to-x_blue_400 rounded-lg"
|
|
283
|
+
>
|
|
284
|
+
Sign and swap
|
|
285
|
+
</Button>
|
|
286
|
+
) : (
|
|
287
|
+
<Steps />
|
|
288
|
+
)}
|
|
289
|
+
</div>
|
|
290
|
+
);
|
|
291
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TokenLogoWithChain } from "@src/components";
|
|
2
|
+
import { Chain, Token } from "@src/models";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
chain: Chain | undefined;
|
|
6
|
+
token: Token | undefined;
|
|
7
|
+
value: string;
|
|
8
|
+
};
|
|
9
|
+
export const TokenItem = ({ chain, token, value }: Props) => {
|
|
10
|
+
return (
|
|
11
|
+
<div className="flex items-center gap-2">
|
|
12
|
+
<TokenLogoWithChain
|
|
13
|
+
chain={chain}
|
|
14
|
+
token={token}
|
|
15
|
+
chainLogoClassName="w-5"
|
|
16
|
+
tokenLogoClassName="w-[34px]"
|
|
17
|
+
/>
|
|
18
|
+
<div className="flex flex-col w-min sm:w-auto">
|
|
19
|
+
<div>{`${value} ${token?.symbol}`}</div>
|
|
20
|
+
<div className="opacity-60">{chain?.displayName}</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
};
|