@xswap-link/sdk 0.1.2 → 0.1.3

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 (35) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/index.css +92 -33
  3. package/dist/index.d.mts +60 -155
  4. package/dist/index.d.ts +60 -155
  5. package/dist/index.js +915 -1841
  6. package/dist/index.mjs +867 -1800
  7. package/package.json +1 -1
  8. package/src/components/Skeleton/index.tsx +8 -5
  9. package/src/components/TxConfigForm/FeesDetails.tsx +5 -5
  10. package/src/components/TxConfigForm/Form.tsx +10 -3
  11. package/src/components/TxConfigForm/History.tsx +1 -1
  12. package/src/components/TxConfigForm/HistoryCard.tsx +19 -40
  13. package/src/components/TxConfigForm/Summary.tsx +57 -28
  14. package/src/components/TxConfigForm/SwapPanel.tsx +70 -77
  15. package/src/components/TxConfigForm/TokenPicker.tsx +2 -2
  16. package/src/components/TxConfigForm/TopBar.tsx +2 -2
  17. package/src/components/TxConfigForm/UsdPrice.tsx +41 -0
  18. package/src/components/TxConfigForm/index.tsx +1 -2
  19. package/src/components/TxHistoryButton/index.tsx +150 -0
  20. package/src/components/icons/ArrowLeftIcon.tsx +16 -0
  21. package/src/components/icons/ChevronUpIcon.tsx +1 -6
  22. package/src/components/icons/CircularProgressIcon.tsx +13 -15
  23. package/src/components/icons/XSwapLogo.tsx +98 -0
  24. package/src/components/icons/index.ts +2 -0
  25. package/src/components/index.ts +1 -0
  26. package/src/constants/index.ts +1 -0
  27. package/src/contracts/abi/index.ts +0 -2
  28. package/src/contracts/addresses.ts +6 -0
  29. package/src/models/Addresses.ts +1 -0
  30. package/src/models/TransactionHistory.ts +44 -29
  31. package/src/services/integrations/transactions.ts +166 -1
  32. package/src/utils/index.ts +7 -0
  33. package/tailwind.config.js +6 -1
  34. package/tsconfig.json +3 -7
  35. package/src/contracts/abi/XSwapRouter.json +0 -1242
@@ -0,0 +1,150 @@
1
+ import { FC, useState } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import {
4
+ ArrowLeftIcon,
5
+ ArrowRightIcon,
6
+ CheckIcon,
7
+ CircularProgressIcon,
8
+ } from "../icons";
9
+ import { ArrowUpRightIcon, XSwapLogo } from "../icons";
10
+ import { MonitoredTransaction } from "@src/models";
11
+
12
+ interface Props {
13
+ transaction: MonitoredTransaction;
14
+ }
15
+
16
+ export const TxHistoryButton: FC<Props> = ({ transaction }) => {
17
+ const {
18
+ fromChain,
19
+ fromChainImage,
20
+ fromToken,
21
+ fromTokenImage,
22
+ fromAmount,
23
+ toChain,
24
+ toChainImage,
25
+ isDone,
26
+ txHash,
27
+ explorer,
28
+ } = transaction;
29
+ const [isWide, setIsWide] = useState(false);
30
+ return (
31
+ <div className="flex gap-2" onClick={() => setIsWide((x: boolean) => !x)}>
32
+ {isWide && (
33
+ <div
34
+ className={`flex items-center min-w-60 sm:w-[520px] text-white h-14 px-4 text-sm gap-3 rounded-xl bg-gradient-to-r ${
35
+ isDone
36
+ ? "from-[rgba(76,175,80,0.1)] to-[rgba(46,125,50,0.1)] border border-solid border-x_green_dark"
37
+ : "from-[rgba(54,129,198,0.1)] to-[rgba(43,74,157,0.1)] border border-solid border-x_blue_dark"
38
+ }`}
39
+ >
40
+ <div className="flex justify-between flex-col gap-2 sm:gap-5 sm:flex-row w-full">
41
+ <div className="flex items-center gap-2">
42
+ <img
43
+ className="w-5 h-5"
44
+ src={fromChainImage}
45
+ alt="source chain"
46
+ />
47
+ <div>{fromChain}</div>
48
+ <div className="w-3.5 h-3.5">
49
+ <ArrowRightIcon />
50
+ </div>
51
+ <img className="w-5 h-5" src={toChainImage} alt="to chain" />
52
+ <div>{toChain}</div>
53
+ </div>
54
+
55
+ <div className="flex items-center gap-2">
56
+ <div>Sent</div>
57
+ <div>{fromAmount}</div>
58
+ <div>{fromToken}</div>
59
+ <img
60
+ className="w-5 h-5"
61
+ src={fromTokenImage}
62
+ alt="source token"
63
+ />
64
+ </div>
65
+ </div>
66
+ <a
67
+ href={explorer}
68
+ target="_blank"
69
+ rel="noreferrer"
70
+ className="no-underline cursor-pointer"
71
+ aria-label="Show the transaction in the chain explorer"
72
+ >
73
+ <div className="w-3.5 h-3.5">
74
+ <ArrowUpRightIcon />
75
+ </div>
76
+ </a>
77
+ </div>
78
+ )}
79
+ <div
80
+ className={`text-white rounded-xl cursor-pointer bg-gradient-to-r
81
+ ${
82
+ isDone
83
+ ? "from-[rgba(76,175,80,0.1)] to-[rgba(46,125,50,0.1)] border border-solid border-x_green_dark"
84
+ : "from-[rgba(54,129,198,0.1)] to-[rgba(43,74,157,0.1)] border border-solid border-x_blue_dark"
85
+ }`}
86
+ >
87
+ <div className="flex gap-2 items-center h-14 w-20 justify-center">
88
+ <ArrowLeftIcon />
89
+ <div className="relative flex items-center justify-center w-9 h-9">
90
+ {isDone ? (
91
+ <div className="text-x_green w-5 h-5">
92
+ <CheckIcon />
93
+ </div>
94
+ ) : (
95
+ <>
96
+ <div className="w-5 h-5">
97
+ <XSwapLogo />
98
+ </div>
99
+ <div className="absolute flex items-center justify-center top-0 left-0 right-0 bottom-0 text-white">
100
+ <CircularProgressIcon />
101
+ </div>
102
+ </>
103
+ )}
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+ );
109
+ };
110
+
111
+ let renderedTransactions: MonitoredTransaction[] = [];
112
+
113
+ export const addTransactionToRenderedTransactions = (
114
+ transaction: MonitoredTransaction,
115
+ ) => {
116
+ if (!renderedTransactions.some(({ txHash }) => txHash === transaction.txHash))
117
+ renderedTransactions.push(transaction);
118
+ };
119
+
120
+ export const removeTransactionFromRenderedTransactions = (txHash: string) => {
121
+ const filteredRenderedTransactions = renderedTransactions.filter(
122
+ (item) => item.txHash !== txHash,
123
+ );
124
+ renderedTransactions = [...filteredRenderedTransactions];
125
+ };
126
+
127
+ export const updateTransactionDoneInRenderedTransactions = (txHash: string) => {
128
+ const transaction = renderedTransactions.find(
129
+ (item) => item.txHash === txHash,
130
+ );
131
+ if (transaction) {
132
+ transaction.isDone = true;
133
+ }
134
+ };
135
+
136
+ const historyElement = document.createElement("div");
137
+ historyElement.setAttribute("id", "xswap-history");
138
+ historyElement.setAttribute(
139
+ "class",
140
+ "absolute bottom-5 right-2 flex flex-col gap-3 pl-9 items-end w-full z-50",
141
+ );
142
+ document.body.appendChild(historyElement);
143
+ const txHistoryButtonRoot = createRoot(historyElement);
144
+
145
+ export const renderTxHistoryButtons = () => {
146
+ const buttons = renderedTransactions.map((item) => (
147
+ <TxHistoryButton key={item.txHash} transaction={item} />
148
+ ));
149
+ txHistoryButtonRoot.render(buttons);
150
+ };
@@ -0,0 +1,16 @@
1
+ export const ArrowLeftIcon = () => {
2
+ return (
3
+ <svg
4
+ width="10"
5
+ height="16"
6
+ viewBox="0 0 10 16"
7
+ fill="none"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ >
10
+ <path
11
+ d="M0.5 8L8.13251 15.5L9.5 14.1562L3.20318 8L9.4682 1.84375L8.10071 0.5L0.5 8Z"
12
+ fill="white"
13
+ />
14
+ </svg>
15
+ );
16
+ };
@@ -1,11 +1,6 @@
1
1
  export const ChevronUpIcon = () => {
2
2
  return (
3
- <svg
4
- height="12"
5
- width="12"
6
- xmlns="http://www.w3.org/2000/svg"
7
- viewBox="0 0 512 512"
8
- >
3
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
9
4
  <path
10
5
  fill="currentColor"
11
6
  d="M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
@@ -1,24 +1,22 @@
1
1
  export const CircularProgressIcon = () => {
2
2
  return (
3
3
  <svg
4
- className="animate-spin -ml-1 mr-3 h-10 w-10 text-white"
5
- xmlns="http://www.w3.org/2000/svg"
4
+ className="animate-spin-slow text-white"
5
+ width="33"
6
+ height="33"
7
+ viewBox="0 0 33 33"
6
8
  fill="none"
7
- viewBox="0 0 24 24"
9
+ xmlns="http://www.w3.org/2000/svg"
8
10
  >
9
11
  <circle
10
- className="opacity-25"
11
- cx="12"
12
- cy="12"
13
- r="10"
14
- stroke="currentColor"
15
- strokeWidth="4"
16
- ></circle>
17
- <path
18
- className="opacity-75"
19
- fill="currentColor"
20
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
21
- ></path>
12
+ cx="16.5"
13
+ cy="16.5"
14
+ r="15.5"
15
+ stroke="white"
16
+ strokeOpacity="0.2"
17
+ strokeWidth="2"
18
+ />
19
+ <path d="M1 16C1 7.71573 7.71573 1 16 1" stroke="white" strokeWidth="2" />
22
20
  </svg>
23
21
  );
24
22
  };
@@ -0,0 +1,98 @@
1
+ export const XSwapLogo = () => {
2
+ return (
3
+ <svg
4
+ version="1.1"
5
+ id="Layer_1"
6
+ xmlns="http://www.w3.org/2000/svg"
7
+ x="0px"
8
+ y="0px"
9
+ viewBox="0 0 50 50"
10
+ enableBackground="new 0 0 50 50"
11
+ >
12
+ <image
13
+ id="image0"
14
+ x="0"
15
+ y="0"
16
+ href="data:image/png;base64,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"
95
+ />
96
+ </svg>
97
+ );
98
+ };
@@ -1,5 +1,6 @@
1
1
  export * from "./ArrowRightIcon";
2
2
  export * from "./ArrowDownIcon";
3
+ export * from "./ArrowLeftIcon";
3
4
  export * from "./ArrowUpRightIcon";
4
5
  export * from "./CheckIcon";
5
6
  export * from "./ChevronDownIcon";
@@ -18,3 +19,4 @@ export * from "./InfoIcon";
18
19
  export * from "./SettingsIcon";
19
20
  export * from "./XSwapBadgeIcon";
20
21
  export * from "./ChainlinkCCIPIcon";
22
+ export * from "./XSwapLogo";
@@ -1,3 +1,4 @@
1
1
  export * from "./Alert";
2
2
  export * from "./TxConfigForm";
3
+ export * from "./TxHistoryButton";
3
4
  export * from "./Skeleton";
@@ -9,3 +9,4 @@ export const BALANCES_CHUNK_SIZE = 500;
9
9
  export const ROUTE_TIMEOUT_MS = 5000;
10
10
  export const MINIMUM_DISPLAYED_TOKEN_AMOUNT = 0.0001;
11
11
  export const DEFAULT_SOURCE_CHAIN_ID = "1";
12
+ export const CCIP_EXPLORER = "https://ccip.chain.link/";
@@ -1,7 +1,5 @@
1
1
  import BatchQuery from "./BatchQuery.json";
2
2
  import ERC20 from "./ERC20.json";
3
- import XSwapRouter from "./XSwapRouter.json";
4
3
 
5
4
  export const BatchQueryAbi = BatchQuery;
6
5
  export const ERC20Abi = ERC20;
7
- export const XSwapRouterAbi = XSwapRouter;
@@ -4,31 +4,37 @@ export const ADDRESSES: Addresses = {
4
4
  // arbitrum
5
5
  "42161": {
6
6
  [ContractName.BatchQuery]: "0xb5f9934ea0810ef5faa677e8c68c6983b88664b1",
7
+ [ContractName.XSwapRouter]: "0xe1c14b9f065dead2e89ee35382f8bd42bdb87a04",
7
8
  [ContractName.FeeCollector]: "0xb37275558f02f05104c2ba35199d16adeb43432f",
8
9
  },
9
10
  // avalanche
10
11
  "43114": {
11
12
  [ContractName.BatchQuery]: "0xb5f9934ea0810ef5faa677e8c68c6983b88664b1",
13
+ [ContractName.XSwapRouter]: "0xe1c14b9f065dead2e89ee35382f8bd42bdb87a04",
12
14
  [ContractName.FeeCollector]: "0xb37275558f02f05104c2ba35199d16adeb43432f",
13
15
  },
14
16
  // base
15
17
  "8453": {
16
18
  [ContractName.BatchQuery]: "0xb5f9934ea0810ef5faa677e8c68c6983b88664b1",
19
+ [ContractName.XSwapRouter]: "0xe1c14b9f065dead2e89ee35382f8bd42bdb87a04",
17
20
  [ContractName.FeeCollector]: "0xb37275558f02f05104c2ba35199d16adeb43432f",
18
21
  },
19
22
  // ethereum
20
23
  "1": {
21
24
  [ContractName.BatchQuery]: "0xb5f9934ea0810ef5faa677e8c68c6983b88664b1",
25
+ [ContractName.XSwapRouter]: "0xe1c14b9f065dead2e89ee35382f8bd42bdb87a04",
22
26
  [ContractName.FeeCollector]: "0xb37275558f02f05104c2ba35199d16adeb43432f",
23
27
  },
24
28
  // optimism
25
29
  "10": {
26
30
  [ContractName.BatchQuery]: "0xb5f9934ea0810ef5faa677e8c68c6983b88664b1",
31
+ [ContractName.XSwapRouter]: "0xe1c14b9f065dead2e89ee35382f8bd42bdb87a04",
27
32
  [ContractName.FeeCollector]: "0xb37275558f02f05104c2ba35199d16adeb43432f",
28
33
  },
29
34
  // polygon
30
35
  "137": {
31
36
  [ContractName.BatchQuery]: "0xb5f9934ea0810ef5faa677e8c68c6983b88664b1",
37
+ [ContractName.XSwapRouter]: "0xe1c14b9f065dead2e89ee35382f8bd42bdb87a04",
32
38
  [ContractName.FeeCollector]: "0xb37275558f02f05104c2ba35199d16adeb43432f",
33
39
  },
34
40
  };
@@ -9,4 +9,5 @@ export type Contracts = {
9
9
  export enum ContractName {
10
10
  BatchQuery = "BatchQuery",
11
11
  FeeCollector = "FeeCollector",
12
+ XSwapRouter = "XSwapRouter",
12
13
  }
@@ -1,35 +1,37 @@
1
1
  type TransferType = "CROSS_CHAIN" | "SINGLE_CHAIN";
2
2
 
3
3
  export type TransactionHistory = {
4
- history: {
5
- transferId: string;
6
- transferType: TransferType;
7
- walletAddress: string;
8
- // ExecutorRunFailed
9
- failed: boolean;
10
- // MessageSent
11
- source: {
12
- blockchainId: string;
13
- transactionHash: string;
14
- blockTime: Date;
15
- tokenAmount: string;
16
- tokenAddress: string;
17
- transferredTokenAddress: string;
18
- transferredTokenAmount: string;
19
- valueForInstantCcipRecieve: string;
20
- tokenOutAddress: string;
21
- estimatedAmountOut: string;
22
- targetBlockchainId: string;
23
- } | null;
24
- // MessageReceived
25
- target: {
26
- blockchainId: string;
27
- transactionHash: string;
28
- blockTime: Date;
29
- tokenAmount: string;
30
- tokenAddress: string;
31
- } | null;
32
- }[];
4
+ history: HistoryTransaction[];
5
+ };
6
+
7
+ export type HistoryTransaction = {
8
+ transferId: string;
9
+ transferType: TransferType;
10
+ walletAddress: string;
11
+ // ExecutorRunFailed
12
+ failed: boolean;
13
+ // MessageSent
14
+ source: {
15
+ blockchainId: string;
16
+ transactionHash: string;
17
+ blockTime: Date;
18
+ tokenAmount: string;
19
+ tokenAddress: string;
20
+ transferredTokenAddress: string;
21
+ transferredTokenAmount: string;
22
+ valueForInstantCcipRecieve: string;
23
+ tokenOutAddress: string;
24
+ estimatedAmountOut: string;
25
+ targetBlockchainId: string;
26
+ } | null;
27
+ // MessageReceived
28
+ target: {
29
+ blockchainId: string;
30
+ transactionHash: string;
31
+ blockTime: Date;
32
+ tokenAmount: string;
33
+ tokenAddress: string;
34
+ } | null;
33
35
  };
34
36
 
35
37
  export type TransactionStatus = "IN_PROGRESS" | "DONE" | "REVERTED";
@@ -46,3 +48,16 @@ export type Transaction = {
46
48
  estimatedDeliveryTimestamp: number;
47
49
  status: TransactionStatus;
48
50
  };
51
+
52
+ export type MonitoredTransaction = {
53
+ fromChain: string | undefined;
54
+ fromChainImage: string | undefined;
55
+ fromToken: string | undefined;
56
+ fromTokenImage: string | undefined;
57
+ fromAmount: string | undefined;
58
+ toChain: string | undefined;
59
+ toChainImage: string | undefined;
60
+ isDone: boolean;
61
+ txHash: string;
62
+ explorer: string | undefined;
63
+ };
@@ -3,9 +3,20 @@ import {
3
3
  GetSwapTxPayload,
4
4
  Transactions,
5
5
  Web3Environment,
6
+ MonitoredTransaction,
6
7
  } from "@src/models";
7
8
  import { getChains } from "@src/services";
8
- import { openTxConfigForm } from "@src/components";
9
+ import { ethers } from "ethers";
10
+ import { ADDRESSES } from "@src/contracts";
11
+ import {
12
+ openTxConfigForm,
13
+ addTransactionToRenderedTransactions,
14
+ removeTransactionFromRenderedTransactions,
15
+ renderTxHistoryButtons,
16
+ updateTransactionDoneInRenderedTransactions,
17
+ } from "@src/components";
18
+ import { weiToHumanReadable } from "@src/utils";
19
+ import { CCIP_EXPLORER } from "@src/constants";
9
20
 
10
21
  export const getSwapTx = async ({
11
22
  dstChain,
@@ -18,6 +29,8 @@ export const getSwapTx = async ({
18
29
  web3Environment === Web3Environment.MAINNET && swapSupported,
19
30
  );
20
31
 
32
+ validateSwapTxData(supportedChains, dstChain, dstToken);
33
+
21
34
  const route = await openTxConfigForm({
22
35
  dstChainId: dstChain,
23
36
  dstTokenAddr: dstToken,
@@ -28,3 +41,155 @@ export const getSwapTx = async ({
28
41
 
29
42
  return route.transactions;
30
43
  };
44
+
45
+ const validateSwapTxData = (
46
+ supportedChains: Chain[],
47
+ dstChain: string,
48
+ dstToken: string,
49
+ ) => {
50
+ const supportedDstChain = supportedChains.find(
51
+ ({ chainId }) => chainId === dstChain,
52
+ );
53
+ if (!supportedDstChain) {
54
+ throw new Error(`Provided chain '${dstChain}' is not supported`);
55
+ }
56
+
57
+ if (!supportedDstChain.tokens.some(({ address }) => address === dstToken)) {
58
+ throw new Error(`Provided token '${dstToken}' is not supported`);
59
+ }
60
+ };
61
+
62
+ export const monitorTransactionStatus = async (
63
+ srcChainId: number,
64
+ txHash: string,
65
+ ) => {
66
+ const txReceipt = await getTxReceipt(srcChainId, txHash);
67
+
68
+ if (!txReceipt) {
69
+ throw new Error(
70
+ `No transaction found for chain ${srcChainId} tx ${txHash}`,
71
+ );
72
+ }
73
+
74
+ const supportedChains: Chain[] = (await getChains()).filter(
75
+ ({ web3Environment, swapSupported }) =>
76
+ web3Environment === Web3Environment.MAINNET && swapSupported,
77
+ );
78
+
79
+ const messageSentEventSignature =
80
+ "MessageSent(bytes32,uint64,address,bytes,address,uint256,uint256,address,uint256)";
81
+
82
+ const messageSentEventAbi = [
83
+ "event MessageSent(bytes32 indexed messageId, uint64 indexed destinationChainSelector, address indexed sender, bytes data, address token, uint256 tokenAmount, uint256 valueForInstantCcipRecieve, address transferedToken, uint256 transferedTokenAmount)",
84
+ ];
85
+ const messageReceivedEventSignature =
86
+ "MessageReceived(bytes32,uint64,address,bytes,address,uint256)";
87
+
88
+ const messageReceivedEventAbi = [
89
+ "event MessageReceived(bytes32 indexed messageId, uint64 indexed sourceChainSelector, address indexed sender, bytes data, address token, uint256 tokenAmount)",
90
+ ];
91
+
92
+ const messageSentEvent = txReceipt.logs?.find((log) => {
93
+ return log.topics[0] === ethers.utils.id(messageSentEventSignature);
94
+ });
95
+
96
+ if (!messageSentEvent) {
97
+ throw new Error(
98
+ `No transaction event found for chain ${srcChainId} tx ${txHash}`,
99
+ );
100
+ }
101
+
102
+ const messageId = messageSentEvent?.topics[1] as string;
103
+ const iface = new ethers.utils.Interface(messageSentEventAbi);
104
+ const decodedMessageSentEvent = iface.parseLog(messageSentEvent);
105
+
106
+ if (!decodedMessageSentEvent) {
107
+ throw new Error(
108
+ `No transaction event arguments found for chain ${srcChainId} tx ${txHash}`,
109
+ );
110
+ }
111
+ const srcChain = supportedChains.find(
112
+ (chain) => chain.chainId === srcChainId.toString(),
113
+ );
114
+
115
+ const srcToken = srcChain?.tokens.find(
116
+ (token) => token.address === decodedMessageSentEvent.args?.token.toString(),
117
+ );
118
+
119
+ const targetChain = supportedChains.find(
120
+ (chain) =>
121
+ chain.ccipChainId ===
122
+ decodedMessageSentEvent.args?.destinationChainSelector?.toString(),
123
+ );
124
+
125
+ const toChainId = targetChain?.chainId;
126
+
127
+ if (!toChainId) {
128
+ throw new Error(`Unknown destination chain!`);
129
+ }
130
+
131
+ const transaction: MonitoredTransaction = {
132
+ txHash: txHash,
133
+ fromChain: srcChain?.displayName,
134
+ fromChainImage: srcChain?.image,
135
+ toChain: targetChain.displayName,
136
+ toChainImage: targetChain.image,
137
+ fromToken: srcToken?.symbol,
138
+ fromTokenImage: srcToken?.image,
139
+ fromAmount: weiToHumanReadable({
140
+ amount: decodedMessageSentEvent.args?.tokenAmount.toString(),
141
+ decimals: srcToken?.decimals || 18,
142
+ precisionFractionalPlaces: 4,
143
+ }),
144
+ isDone: false,
145
+ explorer: `${CCIP_EXPLORER}/tx/${txHash}`,
146
+ };
147
+
148
+ addTransactionToRenderedTransactions(transaction);
149
+ renderTxHistoryButtons();
150
+
151
+ // start listening
152
+ // 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.
153
+
154
+ const xSwapRouterOnDestinationAddress = ADDRESSES[toChainId]?.XSwapRouter;
155
+ if (!xSwapRouterOnDestinationAddress) {
156
+ throw new Error(`Unknown destination XSwapRouter!`);
157
+ }
158
+
159
+ const xSwapRouterOnDestination = new ethers.Contract(
160
+ xSwapRouterOnDestinationAddress,
161
+ messageReceivedEventAbi,
162
+ ethers.getDefaultProvider(
163
+ supportedChains.find((chain) => chain.chainId === toChainId)
164
+ ?.publicRpcUrls[0],
165
+ ),
166
+ );
167
+
168
+ const eventFilter = {
169
+ address: xSwapRouterOnDestinationAddress,
170
+ topics: [ethers.utils.id(messageReceivedEventSignature), messageId],
171
+ };
172
+
173
+ const onSuccess = async () => {
174
+ updateTransactionDoneInRenderedTransactions(txHash);
175
+ renderTxHistoryButtons();
176
+ await new Promise((resolve) => setTimeout(() => resolve(true), 5000));
177
+ removeTransactionFromRenderedTransactions(txHash);
178
+ renderTxHistoryButtons();
179
+ };
180
+ xSwapRouterOnDestination.once(eventFilter, () => onSuccess());
181
+ };
182
+
183
+ const getTxReceipt = async (txChainId: number, txHash: string) => {
184
+ const chains = await getChains();
185
+ const chain = chains.find((chain) => chain.chainId === txChainId.toString());
186
+ const provider = new ethers.providers.JsonRpcProvider(
187
+ chain?.publicRpcUrls[0],
188
+ );
189
+ try {
190
+ return await provider.getTransactionReceipt(txHash);
191
+ } catch (error) {
192
+ console.error("Error fetching transaction receipt:", error);
193
+ return null;
194
+ }
195
+ };