import { useBalance, useRoutes } from "../../hooks/apis"; import { useDispatch, useSelector } from "react-redux"; import { useContext, useEffect, useState } from "react"; import { ethers } from "ethers"; // actions import { setSelectedRoute } from "../../state/selectedRouteSlice"; import { setBestRoute } from "../../state/quotesSlice"; import { setTxDetails } from "../../state/txDetails"; // components import { ReviewModal } from "./ReviewModal"; import { Button } from "../common/Button"; import { Spinner } from "../common/Spinner"; import { InnerCard } from "../common/InnerCard"; import { Web3Context } from "../../providers/Web3Provider"; import { QuoteStatus, ButtonTexts, NATIVE_TOKEN_ADDRESS, UserTxType, } from "../../consts"; import { useTransition } from "@react-spring/web"; import { Info } from "react-feather"; import { formatCurrencyAmount, timeInMinutes } from "../../utils/"; import { SortOptions } from "@socket.tech/socket-v2-sdk"; export const RouteDetails = () => { const dispatch = useDispatch(); const sourceChainId = useSelector( (state: any) => state.networks.sourceChainId ); const sourceToken = useSelector((state: any) => state.tokens.sourceToken); const destToken = useSelector((state: any) => state.tokens.destToken); const sortPref = useSelector((state: any) => state.quotes.sortPref); const sourceAmount = useSelector((state: any) => state.amount.sourceAmount); const isTxModalOpen = useSelector((state: any) => state.modals.isTxModalOpen); const refuelEnabled = useSelector((state: any) => state.quotes.refuelEnabled); const includeBridges = useSelector( (state: any) => state.customSettings.includeBridges ); const excludeBridges = useSelector( (state: any) => state.customSettings.excludeBridges ); const feeParams = useSelector((state: any) => state.customSettings.feeParams); const isEnoughBalance = useSelector( (state: any) => state.amount.isEnoughBalance ); const web3Context = useContext(Web3Context); const { userAddress } = web3Context.web3Provider; const singleTxOnly = useSelector((state: any) => state.quotes.singleTxOnly); const swapSlippage = useSelector((state: any) => state.quotes.swapSlippage); const zpHide = useSelector((state: any) => state.customSettings.zpHide); // Hook to fetch the quotes for given params. const { data, isQuotesLoading } = useRoutes( sourceToken ?? "", destToken, sourceAmount, sortPref, userAddress, refuelEnabled, includeBridges, excludeBridges, singleTxOnly, swapSlippage, feeParams?.feeTakerAddress, feeParams?.feePercent ); // Boolean variable to fill all condition before the api call is made to fetch quotes. const shouldFetch = sourceAmount && sourceToken && destToken && sortPref; const bestRoute = useSelector((state: any) => state.quotes.bestRoute); const fundMovrData = bestRoute?.route?.userTxs.filter( (item) => item.userTxType === UserTxType.FUND_MOVR )[0]; const swapData = bestRoute?.route?.userTxs.filter( (item) => item.userTxType === UserTxType.DEX_SWAP )?.[0]; const bridgeData = fundMovrData?.steps && fundMovrData?.steps.filter((step) => step.type === "bridge")[0]; const protocol = bridgeData?.protocol ?? swapData?.protocol; const [isReviewOpen, setIsReviewOpen] = useState(false); // Hook to get Balance for the native token. const { data: nativeTokenWithBalance } = useBalance( NATIVE_TOKEN_ADDRESS, sourceChainId, userAddress ); // SetTxDetails from local storage to state useEffect(() => { if (localStorage) { const prevTxDetails = JSON.parse(localStorage.getItem("txData")) ?? {}; dispatch( setTxDetails({ prevTxDetails, }) ); } }, []); useEffect(() => { isTxModalOpen && setIsReviewOpen(false); }, [isTxModalOpen]); const [isNativeTokenEnough, setIsNativeTokenEnough] = useState(false); useEffect(() => { if (data) { const isSameChainSwap = data?.[0]?.path?.fromToken?.chainId === data?.[0]?.path?.toToken?.chainId; // Reversing the order in case of sort-by-time because the API returns the list in descendind order of service time const bestRoute = (!isSameChainSwap && sortPref === SortOptions.Time) ? data.reverse()[0] : data[0]; dispatch(setBestRoute(bestRoute)); // Check if there is sufficient native token for refuel // If selected source token is same as native token, add the 2 if (!!bestRoute?.refuel) { let nativeTokenRequired: string; const nativeTokenTransferAmount = bestRoute?.refuel?.fromAmount; if (sourceToken?.address === NATIVE_TOKEN_ADDRESS) { nativeTokenRequired = ethers.BigNumber.from(sourceAmount) .add(nativeTokenTransferAmount) .toString(); } else { nativeTokenRequired = nativeTokenTransferAmount; } if ( ethers.BigNumber.from(nativeTokenRequired).lte( nativeTokenWithBalance?.balance ) ) { setIsNativeTokenEnough(true); } else setIsNativeTokenEnough(false); } } else { dispatch(setBestRoute(null)); } }, [data]); function review() { dispatch(setSelectedRoute(bestRoute)); setIsReviewOpen(true); } // Function that returns status once the fetching has started to get quotes. function quotesStatus() { // Checking for min native token requirement if (!!bestRoute?.refuel && !isNativeTokenEnough) { let minReq: string; // If selected source token is same as source native token, add the input amount with minimum tokens required for refuel // Else, show only the minimum amount required for refuel if (bestRoute?.path?.fromToken?.address === NATIVE_TOKEN_ADDRESS) { const inputAmount = bestRoute?.amount; const refuelAmount = bestRoute?.refuel?.fromAmount; const totalAmount = ethers.BigNumber.from(inputAmount) .add(ethers.BigNumber.from(refuelAmount)) .toString(); minReq = formatCurrencyAmount( totalAmount, bestRoute?.refuel?.fromAsset?.decimals, 2 ); } else { minReq = formatCurrencyAmount( bestRoute?.refuel?.fromAmount, bestRoute?.refuel?.fromAsset?.decimals, 2 ); } return `Not enough ${nativeTokenWithBalance?.symbol} for Refuel (${minReq} required)`; } return shouldFetch ? ( isQuotesLoading ? ( QuoteStatus.FETCHING_QUOTE ) : bestRoute ? ( ) : ( QuoteStatus.NO_ROUTES_AVAILABLE ) ) : ( QuoteStatus.ENTER_AMOUNT ); } // Returns the text shown on the button depending on the status. function getButtonStatus() { if (!isEnoughBalance) { return ButtonTexts.NOT_ENOUGH_BALANCE; } else { return ButtonTexts.REVIEW_QUOTE; } } const transitions = useTransition(isReviewOpen, { from: { y: "100%" }, enter: { y: "0" }, leave: { y: "100%" }, config: { duration: 200 }, onReset: () => setIsReviewOpen(false), }); const BridgeDetails = () => { const sourceAmount = formatCurrencyAmount( bestRoute?.route?.fromAmount, bestRoute?.path?.fromToken?.decimals ); const destAmount = formatCurrencyAmount( bestRoute?.route?.toAmount, bestRoute?.path?.toToken?.decimals ); const conversion = Number(destAmount) / Number(sourceAmount); const conversionMessage = `1 ${ bestRoute?.path?.fromToken?.symbol } = ${conversion?.toFixed(4)} ${bestRoute?.path?.toToken?.symbol}`; return (

{protocol?.displayName}{" "} {swapData ? ( ~ {conversionMessage} ) : ( ~ {timeInMinutes(bridgeData?.serviceTime)} )}

); }; return (
{sourceAmount && sourceAmount !== "0" && isQuotesLoading ? ( ) : !!bestRoute?.refuel && !isNativeTokenEnough ? ( ) : ( "" )} {quotesStatus()}
{!zpHide && (
Powered by Socket Support
)} {transitions( (style, item) => item && ( setIsReviewOpen(false)} style={style} /> ) )}
); };