@xswap-link/sdk 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/.eslintrc.json +9 -5
  2. package/CHANGELOG.md +10 -0
  3. package/README.md +2 -1
  4. package/dist/index.d.mts +106 -20
  5. package/dist/index.d.ts +106 -20
  6. package/dist/index.global.js +963 -164
  7. package/dist/index.global.js.map +1 -0
  8. package/dist/index.js +105 -3564
  9. package/dist/index.js.map +1 -0
  10. package/dist/index.mjs +105 -3534
  11. package/dist/index.mjs.map +1 -0
  12. package/nodemon.json +1 -1
  13. package/package.json +16 -9
  14. package/postcss.config.js +8 -1
  15. package/src/{components → assets}/icons/ArrowDownIcon.tsx +1 -2
  16. package/src/assets/icons/ArrowDownLongIcon.tsx +12 -0
  17. package/src/{components → assets}/icons/ArrowLeftIcon.tsx +1 -5
  18. package/src/{components → assets}/icons/ArrowRightIcon.tsx +1 -5
  19. package/src/assets/icons/ArrowUpIcon.tsx +12 -0
  20. package/src/{components → assets}/icons/ArrowUpRightIcon.tsx +1 -5
  21. package/src/assets/icons/CashbacklIcon.tsx +28 -0
  22. package/src/{components → assets}/icons/CheckIcon.tsx +1 -5
  23. package/src/assets/icons/ChevronDownIcon.tsx +12 -0
  24. package/src/assets/icons/ChevronUpIcon.tsx +7 -0
  25. package/src/{components → assets}/icons/CircularProgressIcon.tsx +1 -2
  26. package/src/assets/icons/CloseIcon.tsx +15 -0
  27. package/src/{components → assets}/icons/CoinsIcon.tsx +0 -2
  28. package/src/assets/icons/CopyIcon.tsx +15 -0
  29. package/src/assets/icons/DotGreenIcon.tsx +12 -0
  30. package/src/{components → assets}/icons/DownArrorIcon.tsx +0 -2
  31. package/src/{components → assets}/icons/ErrorIcon.tsx +0 -1
  32. package/src/assets/icons/ErrorModalStatusIcon.tsx +31 -0
  33. package/src/assets/icons/HelpIcon.tsx +21 -0
  34. package/src/assets/icons/HistoryGradientBgIcon.tsx +32 -0
  35. package/src/assets/icons/HistoryIcon.tsx +12 -0
  36. package/src/{components → assets}/icons/HourGlassIcon.tsx +1 -5
  37. package/src/assets/icons/Icon.tsx +5 -0
  38. package/src/{components → assets}/icons/InfoIcon.tsx +0 -1
  39. package/src/assets/icons/LinkExternalIcon.tsx +15 -0
  40. package/src/{components → assets}/icons/PercentageIcon.tsx +0 -1
  41. package/src/assets/icons/RankIcon.tsx +28 -0
  42. package/src/assets/icons/SettingsGradientBgIcon.tsx +32 -0
  43. package/src/assets/icons/SettingsIcon.tsx +12 -0
  44. package/src/{components → assets}/icons/SignIcon.tsx +0 -2
  45. package/src/{components → assets}/icons/SuccessIcon.tsx +0 -1
  46. package/src/assets/icons/SuccessModalStatusIcon.tsx +31 -0
  47. package/src/assets/icons/ThumbUpRoundedIcon.tsx +15 -0
  48. package/src/{components → assets}/icons/TimerIcon.tsx +0 -1
  49. package/src/assets/icons/TokensIcon.tsx +16 -0
  50. package/src/assets/icons/WaitingIcon.tsx +15 -0
  51. package/src/{components → assets}/icons/XMarkIcon.tsx +1 -5
  52. package/src/assets/icons/XPowerIcon.tsx +28 -0
  53. package/src/{components → assets}/icons/index.ts +16 -0
  54. package/src/components/Alert/index.tsx +1 -1
  55. package/src/components/Button/index.tsx +57 -0
  56. package/src/components/MessageBar/SnackMessage.tsx +32 -0
  57. package/src/components/MessageBar/index.tsx +44 -0
  58. package/src/components/Modal/index.tsx +52 -0
  59. package/src/components/PoweredBy/index.tsx +12 -0
  60. package/src/components/SafeInput/index.tsx +51 -0
  61. package/src/components/Skeleton/index.tsx +5 -9
  62. package/src/components/Spinner/index.tsx +6 -8
  63. package/src/components/Swap/Header/Controls/index.tsx +57 -0
  64. package/src/components/Swap/Header/index.tsx +16 -0
  65. package/src/components/Swap/HistoryView/index.tsx +34 -0
  66. package/src/components/Swap/SettingsView/Delivery/index.tsx +42 -0
  67. package/src/components/Swap/SettingsView/Slippage/index.tsx +80 -0
  68. package/src/components/Swap/SettingsView/index.tsx +11 -0
  69. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/ArrowIcon.tsx +13 -0
  70. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Header/index.tsx +37 -0
  71. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Steps/index.tsx +145 -0
  72. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/SwapPanel/index.tsx +61 -0
  73. package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +291 -0
  74. package/src/components/Swap/SwapView/ConfirmationView/TxResult/TokenItem/index.tsx +24 -0
  75. package/src/components/Swap/SwapView/ConfirmationView/TxResult/index.tsx +84 -0
  76. package/src/components/Swap/SwapView/ConfirmationView/index.tsx +17 -0
  77. package/src/components/Swap/SwapView/FeesPanel/DeliveryInfo/index.tsx +25 -0
  78. package/src/components/Swap/SwapView/FeesPanel/Fee/index.tsx +18 -0
  79. package/src/components/Swap/SwapView/FeesPanel/Fees/index.tsx +71 -0
  80. package/src/components/Swap/SwapView/FeesPanel/index.tsx +67 -0
  81. package/src/components/Swap/SwapView/SwapButton/index.tsx +154 -0
  82. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/Balance/index.tsx +23 -0
  83. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +56 -0
  84. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +70 -0
  85. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/index.tsx +62 -0
  86. package/src/components/Swap/SwapView/SwapPanel/ChainPanel/index.tsx +74 -0
  87. package/src/components/Swap/SwapView/SwapPanel/MaxPanel/index.tsx +35 -0
  88. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/QuickPickTokenItem/index.tsx +40 -0
  89. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +62 -0
  90. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +399 -0
  91. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +104 -0
  92. package/src/components/Swap/SwapView/SwapPanel/index.tsx +42 -0
  93. package/src/components/Swap/SwapView/index.tsx +20 -0
  94. package/src/components/Swap/index.tsx +34 -0
  95. package/src/components/TextInput/index.tsx +67 -0
  96. package/src/components/Timer/index.tsx +57 -0
  97. package/src/components/ToggleButton/index.tsx +24 -0
  98. package/src/components/{UnknownTokenLogo/UnknownTokenLogo.tsx → TokenLogo/Unknown/index.tsx} +3 -4
  99. package/src/components/TokenLogo/WithChain/index.tsx +34 -0
  100. package/src/components/TokenLogo/index.tsx +22 -0
  101. package/src/components/Tooltip/index.tsx +108 -0
  102. package/src/components/TxConfigForm/index.tsx +63 -42
  103. package/src/components/TxDataCard/TokenDetails/index.tsx +30 -0
  104. package/src/components/TxDataCard/TxDataCardUI/index.tsx +133 -0
  105. package/src/components/TxDataCard/index.tsx +30 -0
  106. package/src/components/TxStatusButton/index.tsx +64 -108
  107. package/src/components/global.css +115 -4
  108. package/src/components/index.ts +18 -1
  109. package/src/config/fonts.ts +114 -0
  110. package/src/config/global.d.ts +4 -0
  111. package/src/config/init.tsx +35 -31
  112. package/src/constants/crosschainEstimationTimes.ts +52 -0
  113. package/src/constants/index.ts +8 -0
  114. package/src/context/GlobalDataProvider.tsx +165 -0
  115. package/src/context/HistoryProvider.tsx +339 -0
  116. package/src/context/ModalProvider.tsx +47 -0
  117. package/src/context/SwapProvider.tsx +664 -0
  118. package/src/context/TransactionProvider.tsx +86 -0
  119. package/src/context/TxUIWrapper.tsx +388 -0
  120. package/src/context/index.ts +5 -0
  121. package/src/contracts/abi/CustomXSwapRouter.json +953 -0
  122. package/src/contracts/abi/StakingStableAPR.json +1438 -0
  123. package/src/contracts/abi/XSwapRouter.json +1242 -0
  124. package/src/contracts/abi/XSwapTokenFeeDistributor.json +350 -0
  125. package/src/contracts/abi/index.ts +8 -0
  126. package/src/contracts/addresses.ts +24 -6
  127. package/src/fonts/Satoshi-Black.woff2 +0 -0
  128. package/src/fonts/Satoshi-BlackItalic.woff2 +0 -0
  129. package/src/fonts/Satoshi-Bold.woff2 +0 -0
  130. package/src/fonts/Satoshi-BoldItalic.woff2 +0 -0
  131. package/src/fonts/Satoshi-Light.woff2 +0 -0
  132. package/src/fonts/Satoshi-LightItalic.woff2 +0 -0
  133. package/src/fonts/Satoshi-Medium.woff2 +0 -0
  134. package/src/fonts/Satoshi-MediumItalic.woff2 +0 -0
  135. package/src/fonts/Satoshi-Regular.woff2 +0 -0
  136. package/src/fonts/Satoshi-RegularItalic.woff2 +0 -0
  137. package/src/fonts/Satoshi-Variable.woff2 +0 -0
  138. package/src/fonts/Satoshi-VariableItalic.woff2 +0 -0
  139. package/src/hooks/index.ts +5 -0
  140. package/src/hooks/networkManagement/clientToSigner.tsx +17 -0
  141. package/src/hooks/networkManagement/useNetworks.ts +51 -0
  142. package/src/hooks/useBatchQuery.ts +46 -0
  143. package/src/hooks/useDebounce.tsx +28 -0
  144. package/src/hooks/useEvmContractApi.ts +85 -0
  145. package/src/hooks/usePortal.ts +79 -0
  146. package/src/hooks/useXPower.ts +58 -0
  147. package/src/models/Addresses.ts +8 -2
  148. package/src/models/TokenData.ts +13 -0
  149. package/src/models/TransactionHistory.ts +2 -0
  150. package/src/models/TxUIWrapper.ts +51 -0
  151. package/src/models/XSwapConfig.ts +1 -0
  152. package/src/models/index.ts +1 -0
  153. package/src/models/payloads/GetBalancesPayload.ts +3 -0
  154. package/src/models/payloads/GetFinishedLeaderboardQuestsPayload.ts +3 -0
  155. package/src/models/payloads/GetLeaderboardChangePayload.ts +4 -0
  156. package/src/models/payloads/{GetSwapTxPayload.ts → ModalIntegrationPayload.ts} +3 -1
  157. package/src/models/payloads/index.ts +5 -2
  158. package/src/services/api.ts +34 -5
  159. package/src/services/integrations/monitoring.ts +49 -37
  160. package/src/services/integrations/transactions.ts +50 -6
  161. package/src/utils/contracts.ts +29 -0
  162. package/src/utils/index.ts +3 -3
  163. package/src/utils/numbers.ts +14 -0
  164. package/src/utils/parseWeb3Error.ts +7 -3
  165. package/tailwind.config.js +15 -5
  166. package/tsconfig.json +7 -7
  167. package/tsup.config.ts +61 -0
  168. package/xswap.config.ts +1 -0
  169. package/dist/index.css +0 -1
  170. package/src/components/TxConfigForm/BalanceComponent.tsx +0 -52
  171. package/src/components/TxConfigForm/Button.tsx +0 -20
  172. package/src/components/TxConfigForm/ChainListElement.tsx +0 -36
  173. package/src/components/TxConfigForm/ConfirmationAmount.tsx +0 -91
  174. package/src/components/TxConfigForm/ConfirmationView/ErrorView.tsx +0 -38
  175. package/src/components/TxConfigForm/ConfirmationView/SuccessView.tsx +0 -159
  176. package/src/components/TxConfigForm/ConfirmationView/index.tsx +0 -149
  177. package/src/components/TxConfigForm/ConnectWalletPrompt.tsx +0 -21
  178. package/src/components/TxConfigForm/Description.tsx +0 -17
  179. package/src/components/TxConfigForm/ErrorField.tsx +0 -19
  180. package/src/components/TxConfigForm/FeesDetails.tsx +0 -117
  181. package/src/components/TxConfigForm/Form.tsx +0 -574
  182. package/src/components/TxConfigForm/History.tsx +0 -131
  183. package/src/components/TxConfigForm/HistoryCard.tsx +0 -182
  184. package/src/components/TxConfigForm/PoweredBy.tsx +0 -12
  185. package/src/components/TxConfigForm/Settings.tsx +0 -143
  186. package/src/components/TxConfigForm/Summary.tsx +0 -105
  187. package/src/components/TxConfigForm/SwapPanel.tsx +0 -185
  188. package/src/components/TxConfigForm/TokenPicker.tsx +0 -176
  189. package/src/components/TxConfigForm/TopBar.tsx +0 -58
  190. package/src/components/TxConfigForm/UsdPrice.tsx +0 -44
  191. package/src/components/icons/ChevronDownIcon.tsx +0 -15
  192. package/src/components/icons/ChevronUpIcon.tsx +0 -10
  193. package/src/components/icons/CloseIcon.tsx +0 -15
  194. package/src/components/icons/HistoryIcon.tsx +0 -16
  195. package/src/components/icons/SettingsIcon.tsx +0 -16
  196. /package/src/{components → assets}/icons/ChainlinkCCIPIcon.tsx +0 -0
  197. /package/src/{components → assets}/icons/SearchIcon.tsx +0 -0
  198. /package/src/{components → assets}/icons/XSwapBadgeIcon.tsx +0 -0
  199. /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
+ };