UNPKG

@xswap-link/sdk

Version:
190 lines (164 loc) 5.48 kB
import { addTransactionToRenderedTransactions, removeTransactionFromRenderedTransactions, renderTxStatusButtons, updateTransactionDoneInRenderedTransactions, } from "@src/components"; import { MSG_RECEIVED_EVENT_ABI, MSG_RECEIVED_EVENT_SIG, MSG_SENT_EVENT_ABI, MSG_SENT_EVENT_SIG, TX_RECEIPT_STATUS_LIFETIME, } from "@src/constants"; import { ADDRESSES } from "@src/contracts"; import { Chain, Token, Transaction, Web3Environment } from "@src/models"; import { getChains, getTxStatus } from "@src/services"; import retry from "async-retry"; import { ethers } from "ethers"; import { getCustomTokenData } from "../blockchain"; export const renderTxStatus = async ( txChainId: string, txHash: string, dstTokenAddress?: string, dstTokenAmount?: string, ): Promise<void> => { const txReceipt = await getTxReceipt(txChainId, txHash); const supportedChains: Chain[] = (await getChains()).filter( ({ web3Environment, swapSupported }) => web3Environment === Web3Environment.MAINNET && swapSupported, ); const messageSentEvent = txReceipt.logs?.find((log: { topics: string[] }) => { return log.topics[0] === ethers.utils.id(MSG_SENT_EVENT_SIG); }); if (!messageSentEvent) { throw new Error( `No transaction event found for chain ${txChainId} tx ${txHash}`, ); } const messageId = messageSentEvent?.topics[1] as string; const iface = new ethers.utils.Interface(MSG_SENT_EVENT_ABI); const decodedMessageSentEvent = iface.parseLog(messageSentEvent); if (!decodedMessageSentEvent) { throw new Error( `No transaction event arguments found for chain ${txChainId} tx ${txHash}`, ); } const MessageSentEventArgs = decodedMessageSentEvent.args; const srcChain = supportedChains.find( (chain) => chain.chainId === txChainId.toString(), ); if (!srcChain) { throw new Error(`Unknown src chain ${txChainId}`); } const srcTokenAddress = ( MessageSentEventArgs?.token.toString() as string ).toLowerCase(); let srcToken = srcChain.tokens.find( (token) => token.address.toLowerCase() === srcTokenAddress, ); if (!srcToken) { srcToken = await getCustomTokenData(srcChain, srcTokenAddress); } const dstChain = supportedChains.find( (chain) => chain.ccipChainId === MessageSentEventArgs?.destinationChainSelector?.toString(), ); const dstChainId = dstChain?.chainId; if (!dstChainId) { throw new Error(`Unknown destination chain!`); } let dstToken: Token | undefined = undefined; if (dstTokenAddress) { dstToken = dstChain.tokens.find( (token) => token.address.toLowerCase() === dstTokenAddress.toLowerCase(), ); } const timestamp = Date.now() / 1000; const transaction: Transaction = { hash: txReceipt.transactionHash, timestamp, sourceChainId: srcChain.chainId, targetChainId: dstChain.chainId, amountWei: MessageSentEventArgs?.tokenAmount.toString(), tokenAddress: srcToken.address, tokenOutAddress: dstToken?.address, tokenOutAmount: dstTokenAmount, estimatedDeliveryTimestamp: MessageSentEventArgs?.valueForInstantCcipRecieve.toString() !== "0" ? timestamp * 1000 + 30 * 1000 : timestamp * 1000 + 30 * 60 * 1000, status: "IN_PROGRESS", }; addTransactionToRenderedTransactions({ transaction, supportedChains, dstToken, srcToken, }); renderTxStatusButtons(); const xSwapRouterOnDestinationAddress = ADDRESSES[dstChainId]?.XSwapRouter; if (!xSwapRouterOnDestinationAddress) { throw new Error(`Unknown destination XSwapRouter!`); } const xSwapRouterOnDestination = new ethers.Contract( xSwapRouterOnDestinationAddress, MSG_RECEIVED_EVENT_ABI, ethers.getDefaultProvider( supportedChains.find((chain) => chain.chainId === dstChainId) ?.publicRpcUrls[0], ), ); const msgReceivedEventFilter = { address: xSwapRouterOnDestinationAddress, topics: [ethers.utils.id(MSG_RECEIVED_EVENT_SIG), messageId], }; const onSuccess = async () => { updateTransactionDoneInRenderedTransactions(txHash); renderTxStatusButtons(); await new Promise((resolve) => setTimeout(() => resolve(true), TX_RECEIPT_STATUS_LIFETIME), ); removeTransactionFromRenderedTransactions(txHash); renderTxStatusButtons(); }; // check tx status on backend getTxStatus({ transferId: messageId }).then((response) => { if (response.status === "DONE") { onSuccess(); } else { setTimeout( () => getTxStatus({ transferId: messageId }).then((response) => { if (response.status === "DONE") { onSuccess(); } }), 30 * 60 * 1000, ); // additional check after 30 min xSwapRouterOnDestination.once(msgReceivedEventFilter, () => onSuccess()); } }); }; const getTxReceipt = async (txChainId: string, txHash: string) => { try { const chain = (await getChains()).find( (chain) => chain.chainId === txChainId, ); const provider = new ethers.providers.JsonRpcProvider( chain?.publicRpcUrls[0], ); return await retry(async () => { const txReceipt = await provider.getTransactionReceipt(txHash); if (!txReceipt) { throw new Error(`Couldn't fetch tx receipt`); } return txReceipt; }); } catch (e) { throw new Error( `Couldn't fetch tx ${txHash} receipt on chain ${txChainId}`, ); } };