@xswap-link/sdk 0.2.6 → 0.3.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 (34) hide show
  1. package/.github/workflows/main.yml +2 -2
  2. package/.github/workflows/publish.yml +2 -2
  3. package/CHANGELOG.md +6 -0
  4. package/dist/index.css +99 -2
  5. package/dist/index.d.mts +4 -2
  6. package/dist/index.d.ts +4 -2
  7. package/dist/index.js +1178 -409
  8. package/dist/index.mjs +1205 -430
  9. package/package.json +1 -1
  10. package/src/components/Skeleton/index.tsx +1 -1
  11. package/src/components/Spinner/index.tsx +28 -0
  12. package/src/components/TxConfigForm/ConfirmationAmount.tsx +91 -0
  13. package/src/components/TxConfigForm/Description.tsx +5 -3
  14. package/src/components/TxConfigForm/FeesDetails.tsx +5 -11
  15. package/src/components/TxConfigForm/Form.tsx +763 -118
  16. package/src/components/TxConfigForm/HistoryCard.tsx +1 -1
  17. package/src/components/TxConfigForm/Summary.tsx +9 -8
  18. package/src/components/TxConfigForm/SwapPanel.tsx +1 -4
  19. package/src/components/TxConfigForm/UsdPrice.tsx +3 -13
  20. package/src/components/TxConfigForm/index.tsx +33 -6
  21. package/src/components/TxStatusButton/index.tsx +12 -2
  22. package/src/components/icons/ErrorIcon.tsx +32 -0
  23. package/src/components/icons/SignIcon.tsx +17 -0
  24. package/src/components/icons/SuccessIcon.tsx +29 -0
  25. package/src/components/icons/index.ts +3 -0
  26. package/src/config/init.tsx +27 -10
  27. package/src/constants/index.ts +2 -2
  28. package/src/models/payloads/GetSwapTxPayload.ts +2 -0
  29. package/src/services/integrations/monitoring.ts +10 -3
  30. package/src/services/integrations/transactions.ts +9 -3
  31. package/src/utils/contracts.ts +18 -0
  32. package/src/utils/index.ts +1 -0
  33. package/src/utils/parseWeb3Error.ts +93 -0
  34. package/tailwind.config.js +39 -0
@@ -98,7 +98,7 @@ export const HistoryCard: FC<Props> = ({ transaction, supportedChains }) => {
98
98
  }/${transaction.hash}`}
99
99
  target="_blank"
100
100
  rel="noreferrer"
101
- className="xpay-ml-2 xpay-no-underline"
101
+ className="xpay-ml-2 xpay-no-underline visited:xpay-text-white hover:xpay-text-white"
102
102
  aria-label="Show the transaction in the chain explorer"
103
103
  >
104
104
  <div className="xpay-w-3.5 xpay-h-3.5">
@@ -27,7 +27,7 @@ export const Summary: FC<Props> = ({
27
27
  weiToHumanReadable({
28
28
  amount: route?.estAmountOut || "0",
29
29
  decimals: dstToken?.decimals || 18,
30
- precisionFractionalPlaces: 5,
30
+ precisionFractionalPlaces: 8,
31
31
  }),
32
32
  [route, dstToken],
33
33
  );
@@ -87,13 +87,14 @@ export const Summary: FC<Props> = ({
87
87
  </div>
88
88
  </div>
89
89
  </div>
90
- <UsdPrice
91
- prices={prices}
92
- token={dstToken}
93
- amount={amountReadable}
94
- loading={isGettingRoute}
95
- type="dst"
96
- />
90
+ <div className="xpay-pt-2">
91
+ <UsdPrice
92
+ prices={prices}
93
+ token={dstToken}
94
+ amount={amountReadable}
95
+ loading={isGettingRoute}
96
+ />
97
+ </div>
97
98
  <div className="xpay-absolute xpay-right-[50%] xpay-top-0 xpay-translate-x-1/2 xpay-translate-y-[-60%] xpay-global-border xpay-rounded-xl p-[5px] xpay-bg-[rgba(15,15,15,1)]">
98
99
  <div className="xpay-flex xpay-items-center xpay-justify-center xpay-rounded-lg xpay-w-8 xpay-h-8 xpay-bg-gradient-to-l xpay-from-[rgba(54,129,198,1)] xpay-to-[rgba(43,74,157,1)] ">
99
100
  <ArrowDownIcon />
@@ -19,7 +19,6 @@ interface Props {
19
19
  balances: TokenBalances | undefined;
20
20
  prices: TokenPrices | undefined;
21
21
  supportedChains: Chain[];
22
- setExceedsExpressDeliveryLimit: (exceeds: boolean) => void;
23
22
  }
24
23
 
25
24
  export const SwapPanel: FC<Props> = ({
@@ -33,7 +32,6 @@ export const SwapPanel: FC<Props> = ({
33
32
  balances,
34
33
  prices,
35
34
  supportedChains,
36
- setExceedsExpressDeliveryLimit,
37
35
  }) => {
38
36
  const [chainListShown, setChainListShown] = useState(false);
39
37
  const [tokenListShown, setTokenListShown] = useState(false);
@@ -111,8 +109,6 @@ export const SwapPanel: FC<Props> = ({
111
109
  token={srcToken}
112
110
  amount={amount}
113
111
  loading={false}
114
- type="src"
115
- setExceedsExpressDeliveryLimit={setExceedsExpressDeliveryLimit}
116
112
  />
117
113
  </div>
118
114
  </div>
@@ -178,6 +174,7 @@ export const SwapPanel: FC<Props> = ({
178
174
  setSrcChain={setSrcChain}
179
175
  setChainListShown={setChainListShown}
180
176
  chain={chain}
177
+ key={chain.chainId}
181
178
  />
182
179
  ))}
183
180
  </ul>
@@ -1,15 +1,12 @@
1
1
  import { Alert, Skeleton } from "@src/components";
2
2
  import { Token, TokenPrices } from "@src/models";
3
- import { FC, useEffect, useMemo, useState } from "react";
4
- import { EXPRESS_DELIVERY_LIMIT } from "@src/constants";
3
+ import { FC, useEffect, useState } from "react";
5
4
 
6
5
  type Props = {
7
6
  prices: TokenPrices | undefined;
8
7
  token: Token | undefined;
9
8
  amount: string | undefined;
10
9
  loading: boolean;
11
- type: "src" | "dst";
12
- setExceedsExpressDeliveryLimit?: (exceeds: boolean) => void;
13
10
  };
14
11
 
15
12
  export const UsdPrice: FC<Props> = ({
@@ -17,27 +14,20 @@ export const UsdPrice: FC<Props> = ({
17
14
  token,
18
15
  amount = "0",
19
16
  loading = false,
20
- type,
21
- setExceedsExpressDeliveryLimit,
22
17
  }) => {
23
18
  const [usdPrice, setUsdPrice] = useState<string>("0.00");
24
19
 
25
20
  useEffect(() => {
26
21
  if (token && prices && prices[token.address]) {
27
22
  const newUsdPrice = (
28
- Number(amount) * Number(prices[token.address])
23
+ Number(amount) * Number(prices[token.address.toLowerCase()])
29
24
  ).toFixed(2);
30
- if (type === "src" && setExceedsExpressDeliveryLimit) {
31
- setExceedsExpressDeliveryLimit(
32
- Number(newUsdPrice) > EXPRESS_DELIVERY_LIMIT,
33
- );
34
- }
35
25
  setUsdPrice(newUsdPrice);
36
26
  }
37
27
  }, [token, prices, amount]);
38
28
 
39
29
  return (
40
- <div className="xpay-opacity-60 xpay-py-4">
30
+ <div className="xpay-opacity-60">
41
31
  {loading ? (
42
32
  <Skeleton width="xpay-w-12" height="xpay-h-4" />
43
33
  ) : token ? (
@@ -1,5 +1,5 @@
1
1
  import "@src/global.css";
2
- import { MouseEvent, useMemo } from "react";
2
+ import { MouseEvent, useMemo, useState } from "react";
3
3
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
4
  import { WagmiProvider } from "wagmi";
5
5
  import { getWagmiConfig, waitForInitialization, xpayRoot } from "@src/config";
@@ -14,6 +14,8 @@ export type TxConfigFormProps = {
14
14
  customContractCalls: ContractCall[];
15
15
  desc?: string;
16
16
  supportedChains: Chain[];
17
+ retrunTransactions?: boolean;
18
+ dstDisplayTokenAddr?: string;
17
19
  };
18
20
 
19
21
  const TxConfigForm = ({
@@ -23,16 +25,19 @@ const TxConfigForm = ({
23
25
  customContractCalls,
24
26
  desc,
25
27
  supportedChains,
28
+ retrunTransactions,
29
+ dstDisplayTokenAddr,
26
30
  onSubmit,
27
31
  onClose,
28
32
  }: TxConfigFormProps & {
29
33
  onSubmit: (route: Route) => void;
30
34
  onClose: () => void;
31
35
  }) => {
36
+ const [backdropClickDisabled, setBackdropClickDisabled] = useState(false);
32
37
  const onBackdropClick = (e: MouseEvent) => {
33
38
  e.stopPropagation();
34
39
  e.nativeEvent.stopImmediatePropagation();
35
- if (e.target === e.currentTarget) {
40
+ if (e.target === e.currentTarget && !backdropClickDisabled) {
36
41
  onClose();
37
42
  }
38
43
  };
@@ -48,7 +53,7 @@ const TxConfigForm = ({
48
53
  <QueryClientProvider client={queryClient}>
49
54
  <div
50
55
  className="xpay-top-0 xpay-left-0 xpay-right-0 xpay-bottom-0 xpay-bg-[rgba(0,0,0,0.8)] xpay-fixed xpay-flex xpay-items-center xpay-justify-center xpay-z-10"
51
- onClick={onBackdropClick}
56
+ onMouseDown={onBackdropClick}
52
57
  >
53
58
  <div className="xpay-relative xpay-bg-black xpay-rounded-3xl xpay-overflow-auto xpay-text-white xpay-border-2 xpay-border-solid xpay-border-[rgba(255,255,255,0.1)]">
54
59
  <Form
@@ -58,8 +63,14 @@ const TxConfigForm = ({
58
63
  customContractCalls={customContractCalls}
59
64
  desc={desc}
60
65
  supportedChains={supportedChains}
66
+ wagmiConfig={wagmiConfig}
67
+ retrunTransactions={!!retrunTransactions}
68
+ dstDisplayTokenAddr={dstDisplayTokenAddr}
61
69
  onSubmit={onSubmit}
62
70
  onClose={onClose}
71
+ setBackdropClickDisabled={(disabled: boolean) =>
72
+ setBackdropClickDisabled(disabled)
73
+ }
63
74
  />
64
75
  <PoweredBy />
65
76
  </div>
@@ -76,10 +87,18 @@ export const openTxConfigForm = async ({
76
87
  customContractCalls,
77
88
  desc,
78
89
  supportedChains,
90
+ retrunTransactions,
91
+ dstDisplayTokenAddr,
79
92
  }: TxConfigFormProps): Promise<Route> => {
80
93
  try {
94
+ if (xpayRoot === null) {
95
+ throw new Error("XPay was incorrectly initialised");
96
+ }
81
97
  return await new Promise(async (resolve) => {
82
98
  await waitForInitialization();
99
+ if (xpayRoot === null) {
100
+ throw new Error("XPay was incorrectly initialised");
101
+ }
83
102
  xpayRoot.render(
84
103
  <TxConfigForm
85
104
  integratorId={integratorId}
@@ -88,12 +107,20 @@ export const openTxConfigForm = async ({
88
107
  customContractCalls={customContractCalls}
89
108
  desc={desc}
90
109
  supportedChains={supportedChains}
110
+ retrunTransactions={retrunTransactions}
111
+ dstDisplayTokenAddr={dstDisplayTokenAddr}
91
112
  onSubmit={(route) => {
92
- resolve(route);
93
- xpayRoot.render("");
113
+ if (retrunTransactions) {
114
+ resolve(route);
115
+ }
116
+ if (xpayRoot) {
117
+ xpayRoot.render("");
118
+ }
94
119
  }}
95
120
  onClose={() => {
96
- xpayRoot.render("");
121
+ if (xpayRoot) {
122
+ xpayRoot.render("");
123
+ }
97
124
  }}
98
125
  />,
99
126
  );
@@ -10,6 +10,7 @@ import {
10
10
  import { MonitoredTransaction } from "@src/models";
11
11
  import { xpayTxStatusRoot } from "@src/config";
12
12
  import { UnknownTokenLogo } from "../UnknownTokenLogo/UnknownTokenLogo";
13
+ import { Spinner } from "../Spinner";
13
14
 
14
15
  interface Props {
15
16
  transaction: MonitoredTransaction;
@@ -118,7 +119,7 @@ export const TxStatusButton: FC<Props> = ({ transaction }) => {
118
119
  <XSwapLogo />
119
120
  </div>
120
121
  <div className="xpay-absolute xpay-flex xpay-items-center xpay-justify-center xpay-top-0 xpay-left-0 xpay-right-0 xpay-bottom-0 xpay-text-white">
121
- <CircularProgressIcon />
122
+ <Spinner width="10" height="10" />
122
123
  </div>
123
124
  </>
124
125
  )}
@@ -158,5 +159,14 @@ export const renderTxStatusButtons = () => {
158
159
  const buttons = renderedTransactions.map((item) => (
159
160
  <TxStatusButton key={item.txHash} transaction={item} />
160
161
  ));
161
- xpayTxStatusRoot.render(buttons);
162
+
163
+ if (xpayTxStatusRoot === null) {
164
+ throw new Error("XPay was incorrectly initialised");
165
+ }
166
+
167
+ xpayTxStatusRoot.render(
168
+ <div className="xpay-fixed xpay-bottom-5 xpay-right-2 xpay-flex xpay-flex-col xpay-gap-3 xpay-pl-9 xpay-items-end xpay-w-full xpay-z-50">
169
+ {buttons}
170
+ </div>,
171
+ );
162
172
  };
@@ -0,0 +1,32 @@
1
+ export const ErrorIcon = () => {
2
+ return (
3
+ <svg
4
+ width="81"
5
+ height="80"
6
+ viewBox="0 0 81 80"
7
+ fill="none"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ >
10
+ <g id="error_FILL0_wght400_GRAD0_opsz24 1">
11
+ <path
12
+ id="Vector"
13
+ d="M40.5001 56.6667C41.4445 56.6667 42.2362 56.3473 42.8751 55.7084C43.514 55.0695 43.8334 54.2779 43.8334 53.3334C43.8334 52.389 43.514 51.5973 42.8751 50.9584C42.2362 50.3195 41.4445 50.0001 40.5001 50.0001C39.5556 50.0001 38.764 50.3195 38.1251 50.9584C37.4862 51.5973 37.1667 52.389 37.1667 53.3334C37.1667 54.2779 37.4862 55.0695 38.1251 55.7084C38.764 56.3473 39.5556 56.6667 40.5001 56.6667ZM37.1667 43.3334H43.8334V23.3334H37.1667V43.3334ZM40.5001 73.3334C35.889 73.3334 31.5556 72.4584 27.5001 70.7084C23.4445 68.9584 19.9167 66.5834 16.9167 63.5834C13.9167 60.5834 11.5417 57.0556 9.79175 53.0001C8.04175 48.9445 7.16675 44.6112 7.16675 40.0001C7.16675 35.389 8.04175 31.0556 9.79175 27.0001C11.5417 22.9445 13.9167 19.4167 16.9167 16.4167C19.9167 13.4167 23.4445 11.0417 27.5001 9.29175C31.5556 7.54175 35.889 6.66675 40.5001 6.66675C45.1112 6.66675 49.4445 7.54175 53.5001 9.29175C57.5556 11.0417 61.0834 13.4167 64.0834 16.4167C67.0834 19.4167 69.4584 22.9445 71.2084 27.0001C72.9584 31.0556 73.8334 35.389 73.8334 40.0001C73.8334 44.6112 72.9584 48.9445 71.2084 53.0001C69.4584 57.0556 67.0834 60.5834 64.0834 63.5834C61.0834 66.5834 57.5556 68.9584 53.5001 70.7084C49.4445 72.4584 45.1112 73.3334 40.5001 73.3334ZM40.5001 66.6667C47.9445 66.6667 54.2501 64.0834 59.4167 58.9167C64.5834 53.7501 67.1667 47.4445 67.1667 40.0001C67.1667 32.5556 64.5834 26.2501 59.4167 21.0834C54.2501 15.9167 47.9445 13.3334 40.5001 13.3334C33.0556 13.3334 26.7501 15.9167 21.5834 21.0834C16.4167 26.2501 13.8334 32.5556 13.8334 40.0001C13.8334 47.4445 16.4167 53.7501 21.5834 58.9167C26.7501 64.0834 33.0556 66.6667 40.5001 66.6667Z"
14
+ fill="url(#paint0_linear_230_166)"
15
+ />
16
+ </g>
17
+ <defs>
18
+ <linearGradient
19
+ id="paint0_linear_230_166"
20
+ x1="7.16675"
21
+ y1="6.66675"
22
+ x2="77.4571"
23
+ y2="10.7358"
24
+ gradientUnits="userSpaceOnUse"
25
+ >
26
+ <stop stopColor="#F44336" />
27
+ <stop offset="1" stopColor="#C62828" />
28
+ </linearGradient>
29
+ </defs>
30
+ </svg>
31
+ );
32
+ };
@@ -0,0 +1,17 @@
1
+ export const SignIcon = () => {
2
+ return (
3
+ <svg
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ fill="none"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ >
10
+ <path
11
+ d="M4 21V16.75L17.175 3.6C17.375 3.4 17.6 3.25 17.85 3.15C18.1 3.05 18.35 3 18.6 3C18.8667 3 19.1208 3.05 19.3625 3.15C19.6042 3.25 19.8167 3.4 20 3.6L21.4 5C21.6 5.18333 21.75 5.39583 21.85 5.6375C21.95 5.87917 22 6.13333 22 6.4C22 6.65 21.95 6.9 21.85 7.15C21.75 7.4 21.6 7.625 21.4 7.825L8.25 21H4ZM6 19H7.4L17.225 9.2L16.525 8.475L15.8 7.775L6 17.6V19ZM16.525 8.475L15.8 7.775L17.225 9.2L16.525 8.475ZM14 21C15.2333 21 16.375 20.6917 17.425 20.075C18.475 19.4583 19 18.6 19 17.5C19 16.9 18.8417 16.3833 18.525 15.95C18.2083 15.5167 17.7833 15.1417 17.25 14.825L15.775 16.3C16.1583 16.4667 16.4583 16.65 16.675 16.85C16.8917 17.05 17 17.2667 17 17.5C17 17.8833 16.6958 18.2292 16.0875 18.5375C15.4792 18.8458 14.7833 19 14 19C13.7167 19 13.4792 19.0958 13.2875 19.2875C13.0958 19.4792 13 19.7167 13 20C13 20.2833 13.0958 20.5208 13.2875 20.7125C13.4792 20.9042 13.7167 21 14 21ZM4.575 13.35L6.075 11.85C5.74167 11.7167 5.47917 11.5792 5.2875 11.4375C5.09583 11.2958 5 11.15 5 11C5 10.8 5.15 10.6 5.45 10.4C5.75 10.2 6.38333 9.89167 7.35 9.475C8.81667 8.84167 9.79167 8.26667 10.275 7.75C10.7583 7.23333 11 6.65 11 6C11 5.08333 10.6333 4.35417 9.9 3.8125C9.16667 3.27083 8.2 3 7 3C6.25 3 5.57917 3.13333 4.9875 3.4C4.39583 3.66667 3.94167 3.99167 3.625 4.375C3.44167 4.59167 3.36667 4.83333 3.4 5.1C3.43333 5.36667 3.55833 5.58333 3.775 5.75C3.99167 5.93333 4.23333 6.00833 4.5 5.975C4.76667 5.94167 4.99167 5.83333 5.175 5.65C5.40833 5.41667 5.66667 5.25 5.95 5.15C6.23333 5.05 6.58333 5 7 5C7.68333 5 8.1875 5.1 8.5125 5.3C8.8375 5.5 9 5.73333 9 6C9 6.23333 8.85417 6.44583 8.5625 6.6375C8.27083 6.82917 7.6 7.16667 6.55 7.65C5.21667 8.23333 4.29167 8.7625 3.775 9.2375C3.25833 9.7125 3 10.3 3 11C3 11.5333 3.14167 11.9875 3.425 12.3625C3.70833 12.7375 4.09167 13.0667 4.575 13.35Z"
12
+ fill="white"
13
+ fillOpacity="0.5"
14
+ />
15
+ </svg>
16
+ );
17
+ };
@@ -0,0 +1,29 @@
1
+ export const SuccessIcon = () => {
2
+ return (
3
+ <svg
4
+ width="86"
5
+ height="85"
6
+ viewBox="0 0 86 85"
7
+ fill="none"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ >
10
+ <path
11
+ d="M38.0416 58.7916L63.0103 33.8228L58.052 28.8645L38.0416 48.8749L27.9478 38.7812L22.9895 43.7395L38.0416 58.7916ZM42.9999 77.9166C38.1006 77.9166 33.4964 76.9869 29.1874 75.1275C24.8784 73.2681 21.1301 70.7447 17.9426 67.5572C14.7551 64.3697 12.2317 60.6214 10.3723 56.3124C8.51294 52.0034 7.58325 47.3992 7.58325 42.4999C7.58325 37.6006 8.51294 32.9964 10.3723 28.6874C12.2317 24.3784 14.7551 20.6301 17.9426 17.4426C21.1301 14.2551 24.8784 11.7317 29.1874 9.87231C33.4964 8.01294 38.1006 7.08325 42.9999 7.08325C47.8992 7.08325 52.5034 8.01294 56.8124 9.87231C61.1214 11.7317 64.8697 14.2551 68.0572 17.4426C71.2447 20.6301 73.7681 24.3784 75.6275 28.6874C77.4869 32.9964 78.4166 37.6006 78.4166 42.4999C78.4166 47.3992 77.4869 52.0034 75.6275 56.3124C73.7681 60.6214 71.2447 64.3697 68.0572 67.5572C64.8697 70.7447 61.1214 73.2681 56.8124 75.1275C52.5034 76.9869 47.8992 77.9166 42.9999 77.9166ZM42.9999 70.8332C50.9096 70.8332 57.6093 68.0885 63.0989 62.5989C68.5885 57.1093 71.3332 50.4096 71.3332 42.4999C71.3332 34.5902 68.5885 27.8905 63.0989 22.401C57.6093 16.9114 50.9096 14.1666 42.9999 14.1666C35.0902 14.1666 28.3905 16.9114 22.901 22.401C17.4114 27.8905 14.6666 34.5902 14.6666 42.4999C14.6666 50.4096 17.4114 57.1093 22.901 62.5989C28.3905 68.0885 35.0902 70.8332 42.9999 70.8332Z"
12
+ fill="url(#paint0_linear_1287_1610)"
13
+ />
14
+ <defs>
15
+ <linearGradient
16
+ id="paint0_linear_1287_1610"
17
+ x1="7.58325"
18
+ y1="7.08325"
19
+ x2="82.2668"
20
+ y2="11.4066"
21
+ gradientUnits="userSpaceOnUse"
22
+ >
23
+ <stop stopColor="#4CAF50" />
24
+ <stop offset="1" stopColor="#2E7D32" />
25
+ </linearGradient>
26
+ </defs>
27
+ </svg>
28
+ );
29
+ };
@@ -20,3 +20,6 @@ export * from "./SettingsIcon";
20
20
  export * from "./XSwapBadgeIcon";
21
21
  export * from "./ChainlinkCCIPIcon";
22
22
  export * from "./XSwapLogo";
23
+ export * from "./SignIcon";
24
+ export * from "./SuccessIcon";
25
+ export * from "./ErrorIcon";
@@ -3,15 +3,17 @@ import xswapConfig from "../../xswap.config";
3
3
  import { version } from "../../package.json";
4
4
  import { WaitingForInit } from "@src/components/WaitingForInit";
5
5
 
6
- export let xpayRoot: Root;
7
- export let xpayInitIndicatorRoot: Root;
8
- export let xpayTxStatusRoot: Root;
6
+ export let xpayRoot: Root | null = null;
7
+ export let xpayInitIndicatorRoot: Root | null = null;
8
+ export let xpayTxStatusRoot: Root | null = null;
9
9
 
10
- let initCompleted = false;
10
+ let xPayInitCompleted = false;
11
+
12
+ const isServer = typeof window === "undefined";
11
13
 
12
14
  export const initDocument = () => {
13
- if (typeof document === "undefined") {
14
- throw new Error("Can't render XPay components from server side.");
15
+ if (isServer) {
16
+ return;
15
17
  }
16
18
  createXPayInitIndicatorRoot();
17
19
 
@@ -20,14 +22,14 @@ export const initDocument = () => {
20
22
  createXPayRoot(),
21
23
  createXPayTxStatusRoot(),
22
24
  ]).then(() => {
23
- initCompleted = true;
25
+ xPayInitCompleted = true;
24
26
  });
25
27
  };
26
28
 
27
29
  export const waitForInitialization = async () => {
28
- if (!initCompleted) {
30
+ if (!xPayInitCompleted) {
29
31
  displayInitIndicator(true);
30
- while (!initCompleted) {
32
+ while (!xPayInitCompleted) {
31
33
  await new Promise((resolve) => setTimeout(resolve, 10));
32
34
  }
33
35
  displayInitIndicator(false);
@@ -35,6 +37,9 @@ export const waitForInitialization = async () => {
35
37
  };
36
38
 
37
39
  const createStyleElement = async () => {
40
+ if (isServer) {
41
+ return;
42
+ }
38
43
  const css = await fetch(
39
44
  `${xswapConfig.apiUrl}/sdk/css?${new URLSearchParams({
40
45
  data: JSON.stringify({ version }),
@@ -47,6 +52,9 @@ const createStyleElement = async () => {
47
52
  };
48
53
 
49
54
  const createXPayRoot = async () => {
55
+ if (isServer) {
56
+ return;
57
+ }
50
58
  const xswapElement = document.createElement("div");
51
59
  xswapElement.setAttribute("id", "xpay-root");
52
60
  xswapElement.setAttribute("class", "xpay");
@@ -55,6 +63,9 @@ const createXPayRoot = async () => {
55
63
  };
56
64
 
57
65
  const createXPayTxStatusRoot = async () => {
66
+ if (isServer) {
67
+ return;
68
+ }
58
69
  const txStatusElement = document.createElement("div");
59
70
  txStatusElement.setAttribute("id", "xpay-tx-status");
60
71
  txStatusElement.setAttribute("class", "xpay-tx-status");
@@ -63,6 +74,9 @@ const createXPayTxStatusRoot = async () => {
63
74
  };
64
75
 
65
76
  const createXPayInitIndicatorRoot = () => {
77
+ if (isServer) {
78
+ return;
79
+ }
66
80
  const initIndicatorElement = document.createElement("div");
67
81
  initIndicatorElement.setAttribute("id", "xpay-init-indicator");
68
82
  document.body.appendChild(initIndicatorElement);
@@ -70,7 +84,10 @@ const createXPayInitIndicatorRoot = () => {
70
84
  };
71
85
 
72
86
  const displayInitIndicator = (display: boolean) => {
87
+ if (isServer || !xpayInitIndicatorRoot) {
88
+ return;
89
+ }
73
90
  display
74
91
  ? xpayInitIndicatorRoot.render(<WaitingForInit />)
75
- : xpayInitIndicatorRoot.render("");
92
+ : xpayInitIndicatorRoot.render(null);
76
93
  };
@@ -7,11 +7,11 @@ export const DELIVERY_TIME = 30 * 1000 * 60;
7
7
  export const EXPRESS_DELIVERY_TIME = 30 * 1000;
8
8
  export const EXPRESS_DELIVERY_LIMIT = 1000; // USD
9
9
  export const BALANCES_CHUNK_SIZE = 500;
10
- export const ROUTE_TIMEOUT_MS = 5000;
10
+ export const ROUTE_TIMEOUT_MS = 10000;
11
11
  export const MINIMUM_DISPLAYED_TOKEN_AMOUNT = 0.0001;
12
12
  export const DEFAULT_SOURCE_CHAIN_ID = "1";
13
13
  export const CCIP_EXPLORER = "https://ccip.chain.link/";
14
- export const TX_RECEIPT_STATUS_LIFETIME = 10000;
14
+ export const TX_RECEIPT_STATUS_LIFETIME = 60000;
15
15
 
16
16
  export const MSG_SENT_EVENT_SIG =
17
17
  "MessageSent(bytes32,uint64,address,bytes,address,uint256,uint256,address,uint256)";
@@ -6,4 +6,6 @@ export type GetSwapTxPayload = {
6
6
  dstToken: string;
7
7
  customContractCalls?: ContractCall[];
8
8
  desc?: string;
9
+ retrunTransactions?: boolean;
10
+ dstDisplayToken?: string;
9
11
  };
@@ -101,9 +101,6 @@ export const renderTxStatus = async (
101
101
  addTransactionToRenderedTransactions(transaction);
102
102
  renderTxStatusButtons();
103
103
 
104
- // start listening
105
- // TODO if someone will provide tx which already is done then we will never get the event. Will need to add status endpoint on backend for transactions to check if they are done or not.
106
-
107
104
  const xSwapRouterOnDestinationAddress = ADDRESSES[dstChainId]?.XSwapRouter;
108
105
  if (!xSwapRouterOnDestinationAddress) {
109
106
  throw new Error(`Unknown destination XSwapRouter!`);
@@ -140,6 +137,16 @@ export const renderTxStatus = async (
140
137
  }
141
138
  });
142
139
 
140
+ setTimeout(
141
+ () =>
142
+ getTxStatus({ transferId: messageId }).then((response) => {
143
+ if (response.status === "DONE") {
144
+ onSuccess();
145
+ }
146
+ }),
147
+ 30 * 60 * 1000,
148
+ ); // additional check after 30 min
149
+
143
150
  xSwapRouterOnDestination.once(msgReceivedEventFilter, () => onSuccess());
144
151
  };
145
152
 
@@ -14,7 +14,9 @@ export const openTransactionModal = async ({
14
14
  dstToken,
15
15
  customContractCalls = [],
16
16
  desc,
17
- }: GetSwapTxPayload): Promise<Transactions> => {
17
+ retrunTransactions,
18
+ dstDisplayToken,
19
+ }: GetSwapTxPayload): Promise<Transactions | undefined> => {
18
20
  const supportedChains: Chain[] = (await getChains()).filter(
19
21
  ({ web3Environment, swapSupported }) =>
20
22
  web3Environment === Web3Environment.MAINNET && swapSupported,
@@ -29,9 +31,13 @@ export const openTransactionModal = async ({
29
31
  customContractCalls,
30
32
  desc,
31
33
  supportedChains,
34
+ retrunTransactions,
35
+ dstDisplayTokenAddr: dstDisplayToken,
32
36
  });
33
37
 
34
- return route.transactions;
38
+ if (retrunTransactions) {
39
+ return route.transactions;
40
+ }
35
41
  };
36
42
 
37
43
  const validateSwapTxData = (
@@ -48,7 +54,7 @@ const validateSwapTxData = (
48
54
 
49
55
  if (!isETHAddressValid(dstToken)) {
50
56
  throw new Error(
51
- `Provided token address ${dstToken} on chain ${dstChain} is not correct`,
57
+ `Provided token address ${dstToken} on chain ${dstChain} is invalid`,
52
58
  );
53
59
  }
54
60
  };
@@ -23,6 +23,24 @@ export const getBalanceOf = async (
23
23
  await contract.decimals(),
24
24
  );
25
25
  };
26
+ export const getAllowanceOf = async (
27
+ token: string,
28
+ wallet: string,
29
+ spender: string,
30
+ rpcUrl: string,
31
+ ): Promise<string> => {
32
+ const provider = ethers.getDefaultProvider(rpcUrl);
33
+
34
+ if (token === ethers.constants.AddressZero) {
35
+ return ethers.constants.MaxUint256.toString();
36
+ }
37
+
38
+ const contract = new ethers.Contract(token, ERC20Abi, provider);
39
+ return ethers.utils.formatUnits(
40
+ await contract.allowance(wallet, spender),
41
+ await contract.decimals(),
42
+ );
43
+ };
26
44
 
27
45
  export const getBalances = async (
28
46
  chain: Chain,
@@ -1,6 +1,7 @@
1
1
  export * from "./contracts";
2
2
  export * from "./numbers";
3
3
  export * from "./strings";
4
+ export * from "./parseWeb3Error";
4
5
 
5
6
  import { format } from "date-fns";
6
7
 
@@ -0,0 +1,93 @@
1
+ import { ethers } from "ethers";
2
+
3
+ /**
4
+ * Parses a Web3 error and returns a user-friendly error message.
5
+ * @param error - The error object or message to parse.
6
+ * @returns A user-friendly error message.
7
+ */
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ export function parseWeb3Error(error: any): string {
10
+ const errorMessage =
11
+ typeof error === "string"
12
+ ? error
13
+ : error?.message || "An unknown error occurred";
14
+
15
+ // User rejected the transaction
16
+ if (
17
+ errorMessage.includes("User denied transaction signature") ||
18
+ errorMessage.includes("User rejected the request")
19
+ ) {
20
+ return "Transaction was cancelled.";
21
+ }
22
+
23
+ // Insufficient funds
24
+ if (errorMessage.includes("insufficient funds")) {
25
+ return "Insufficient funds to complete the transaction.";
26
+ }
27
+
28
+ // Gas price too low
29
+ if (errorMessage.includes("gas price too low")) {
30
+ return "Gas price is too low. Please increase the gas price and try again.";
31
+ }
32
+
33
+ // Nonce too low
34
+ if (errorMessage.includes("nonce too low")) {
35
+ return "Transaction nonce is too low. Please reset your account in MetaMask and try again.";
36
+ }
37
+
38
+ // Contract execution error
39
+ if (errorMessage.includes("execution reverted")) {
40
+ const revertReason = errorMessage.match(/reason: (.+?)(?:, method|$)/)?.[1];
41
+ return revertReason
42
+ ? `Transaction failed: ${revertReason}`
43
+ : "Transaction failed during contract execution.";
44
+ }
45
+
46
+ // Gas limit exceeded
47
+ if (errorMessage.includes("exceeds block gas limit")) {
48
+ return "Transaction exceeded gas limit. Please try again with a higher gas limit.";
49
+ }
50
+
51
+ // Network error
52
+ if (
53
+ errorMessage.includes("network error") ||
54
+ errorMessage.includes("connection error")
55
+ ) {
56
+ return "Network error. Please check your internet connection and try again.";
57
+ }
58
+
59
+ // Metamask specific errors
60
+ if (errorMessage.includes("MetaMask Tx Signature:")) {
61
+ return (
62
+ "MetaMask error: " +
63
+ errorMessage.split("MetaMask Tx Signature:")[1].trim()
64
+ );
65
+ }
66
+
67
+ // Ethers.js specific errors
68
+ if (error.code) {
69
+ switch (error.code) {
70
+ case ethers.errors.INVALID_ARGUMENT:
71
+ return "Invalid argument provided to the transaction.";
72
+ case ethers.errors.MISSING_ARGUMENT:
73
+ return "Missing required argument for the transaction.";
74
+ case ethers.errors.UNEXPECTED_ARGUMENT:
75
+ return "Unexpected argument provided to the transaction.";
76
+ case ethers.errors.CALL_EXCEPTION:
77
+ return "Contract call failed. The transaction might have been reverted.";
78
+ case ethers.errors.INSUFFICIENT_FUNDS:
79
+ return "Insufficient funds to complete the transaction.";
80
+ case ethers.errors.NONCE_EXPIRED:
81
+ return "Transaction nonce has expired. Please try again.";
82
+ case ethers.errors.REPLACEMENT_UNDERPRICED:
83
+ return "Replacement transaction underpriced. Please increase the gas price.";
84
+ case ethers.errors.UNPREDICTABLE_GAS_LIMIT:
85
+ return "Unable to estimate gas limit. The transaction might fail.";
86
+ default:
87
+ return "An error occurred: " + error.message;
88
+ }
89
+ }
90
+
91
+ // If no specific error is caught, return a generic message
92
+ return "An error occurred while processing the transaction. Please try again.";
93
+ }