@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,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,11 @@
1
+ import { Delivery } from "./Delivery";
2
+ import { Slippage } from "./Slippage";
3
+
4
+ export const SettingsView = () => {
5
+ return (
6
+ <div className="flex flex-col gap-5">
7
+ <Delivery />
8
+ <Slippage />
9
+ </div>
10
+ );
11
+ };
@@ -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
+ };