import { Box, Container, Divider, Typography, InputLabel, FormControl, OutlinedInput, useMediaQuery, useTheme, } from "@mui/material"; import SettingsIcon from '@mui/icons-material/Settings'; import { useEffect, useMemo, useState } from "react"; import { useParams, useLocation, useSearchParams } from "react-router-dom"; import { Helmet } from "react-helmet"; import ReactGA from "react-ga4"; import InfoTooltip from "../../components/Tooltip/InfoTooltip"; import Modal from "../../components/Modal/Modal"; import PageTitle from "../../components/PageTitle/PageTitle"; import Paper from "../../components/Paper/Paper"; import SwapCard from "../../components/Swap/SwapCard"; import GhostStyledIcon from "../../components/Icon/GhostIcon"; import { Tab, Tabs } from "../../components/Tabs/Tabs"; import { UNISWAP_V2_ROUTER, UNISWAP_V2_FACTORY, RESERVE_ADDRESSES, FTSO_ADDRESSES, EMPTY_ADDRESS, WETH_ADDRESSES, } from "../../constants/addresses"; import { useTokenSymbol } from "../../hooks/tokens"; import { getTokenAddress } from "../../hooks/helpers"; import PoolContainer from "./PoolContainer"; import SwapContainer from "./SwapContainer"; import TokenModal from "./TokenModal"; const Dex = ({ chainId, address, connect, config }) => { const location = useLocation(); const pathname = useParams(); const theme = useTheme(); const isSmallScreen = useMediaQuery("(max-width: 650px)"); const isVerySmallScreen = useMediaQuery("(max-width: 379px)"); const [currentQueryParameters, setSearchParams] = useSearchParams(); const newQueryParameters = new URLSearchParams(); const [isSwap, setIsSwap] = useState(false); const [settingsOpen, handleSettingsOpen] = useState(false); const [topTokenListOpen, setTopTokenListOpen] = useState(false); const [bottomTokenListOpen, setBottomTokenListOpen] = useState(false); const [secondsToWait, setSecondsToWait] = useState(localStorage.getItem("dex-deadline") || "60"); const [slippage, setSlippage] = useState(localStorage.getItem("dex-slippage") || "5"); const [formatDecimals, setFormatDecimals] = useState(localStorage.getItem("dex-decimals") || "5"); const [actualDestinationAddress, setActualDestinationAddress] = useState(localStorage.getItem("dex-destination")); const [destinationAddress, setDestinationAddress] = useState(actualDestinationAddress); const [tokenAddressTop, setTokenAddressTop] = useState(EMPTY_ADDRESS); const [tokenAddressBottom, setTokenAddressBottom] = useState(FTSO_ADDRESSES[chainId]); const { symbol: tokenNameTopInner } = useTokenSymbol(chainId, tokenAddressTop); const { symbol: tokenNameBottomInner } = useTokenSymbol(chainId, tokenAddressBottom); const chainSymbol = useMemo(() => { const chainSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol; if (chainSymbol) return chainSymbol; return "WTF"; }, [config]) const tokenNameTop = useMemo(() => { if (chainSymbol && tokenAddressTop === EMPTY_ADDRESS) { return chainSymbol; } return tokenNameTopInner; }, [tokenAddressTop, tokenNameTopInner, chainSymbol]); const tokenNameBottom = useMemo(() => { const chainSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol; if (chainSymbol && tokenAddressBottom === EMPTY_ADDRESS) { return chainSymbol; } return tokenNameBottomInner; }, [tokenAddressBottom, tokenNameBottomInner, config]); const isWrapping = useMemo(() => { const isNative = tokenAddressTop === EMPTY_ADDRESS; const isWrappedNative = tokenAddressBottom === WETH_ADDRESSES[chainId]; return isNative && isWrappedNative; }, [chainId, tokenAddressTop, tokenAddressBottom]); const isUnwrapping = useMemo(() => { const isWrappedNative = tokenAddressTop === WETH_ADDRESSES[chainId]; const isNative = tokenAddressBottom === EMPTY_ADDRESS; return isNative && isWrappedNative; }, [chainId, tokenAddressTop, tokenAddressBottom]); useEffect(() => { if (currentQueryParameters.has("pool")) { setIsSwap(false); newQueryParameters.set("pool", true); } else { setIsSwap(true); newQueryParameters.delete("pool"); } if (currentQueryParameters.has("from")) { setTokenAddressTop(currentQueryParameters.get("from")); newQueryParameters.set("from", currentQueryParameters.get("from")); } else { setTokenAddressTop(EMPTY_ADDRESS); newQueryParameters.set("from", EMPTY_ADDRESS); } if (currentQueryParameters.has("to")) { setTokenAddressBottom(currentQueryParameters.get("to")); newQueryParameters.set("to", currentQueryParameters.get("to")); } else { setTokenAddressBottom(FTSO_ADDRESSES[chainId]); newQueryParameters.set("to", FTSO_ADDRESSES[chainId]); } setSearchParams(newQueryParameters) }, [currentQueryParameters]) useEffect(() => { ReactGA.send({ hitType: "pageview", page: location.pathname + location.search }); }, [location]); const dexAddresses = { router: UNISWAP_V2_ROUTER[chainId], factory: UNISWAP_V2_FACTORY[chainId], } const onCardsSwap = () => { const tmpFrom = currentQueryParameters.get("from"); const tmpTo = currentQueryParameters.get("to"); if (currentQueryParameters.has("pool")) newQueryParameters.set("pool", true); else newQueryParameters.delete("pool"); newQueryParameters.set("from", tmpTo); newQueryParameters.set("to", tmpFrom); setSearchParams(newQueryParameters); } const changeSwapTab = (swap) => { if (swap || (isWrapping || isUnwrapping)) newQueryParameters.delete("pool"); else newQueryParameters.set("pool", true); newQueryParameters.set("from", currentQueryParameters.get("from")); newQueryParameters.set("to", currentQueryParameters.get("to")); setSearchParams(newQueryParameters); } const setInnerTokenAddressTop = (tokenAddress) => { if (currentQueryParameters.has("pool")) newQueryParameters.set("pool", true); else newQueryParameters.delete("pool"); if (currentQueryParameters.get("to") === tokenAddress) { newQueryParameters.set("from", currentQueryParameters.get("from")); } else newQueryParameters.set("from", tokenAddress); newQueryParameters.set("to", currentQueryParameters.get("to")); setSearchParams(newQueryParameters); } const setInnerTokenAddressBottom = (tokenAddress) => { if (currentQueryParameters.has("pool")) newQueryParameters.set("pool", true); else newQueryParameters.delete("pool"); newQueryParameters.set("from", currentQueryParameters.get("from")); if (currentQueryParameters.get("from") === tokenAddress) { newQueryParameters.set("to", currentQueryParameters.get("to")); } else newQueryParameters.set("to", tokenAddress); setSearchParams(newQueryParameters); } const setSlippageInner = (value) => { const maybeValue = parseFloat(value); if (!maybeValue || parseFloat(value) <= 100) { setSlippage(value); localStorage.setItem("dex-slippage", value); } } const setSecondsToWaitInner = (value) => { localStorage.setItem("dex-deadline", value); setSecondsToWait(value); } const setFormatDecimalsInner = (value) => { if (Number(value) <= 17) { localStorage.setItem("dex-decimals", value); setFormatDecimals(value); } } const setDestinationAddressInner = (value) => { const cleanedValue = value.trim(); const isEvmAddress = /^0x[a-fA-F0-9]{40}$/.test(cleanedValue); if (isEvmAddress) { localStorage.setItem("dex-destination", value); setActualDestinationAddress(value); } else if (!isEvmAddress && actualDestinationAddress) { localStorage.removeItem("dex-destination"); setActualDestinationAddress(undefined); } setDestinationAddress(value); } const handleCloseSetting = () => { setDestinationAddress(undefined); handleSettingsOpen(false); } return ( ghostSwap | The pure web3 legacy v2 swap handleCloseSetting()} > Slippage setSlippageInner(event.currentTarget.value)} endAdornment="%" /> Transaction may revert if price changes by more than slippage % Transaction deadline setSecondsToWaitInner(event.currentTarget.value)} endAdornment="seconds" /> How long transaction is valid in the transaction pool Decimal representation setFormatDecimalsInner(event.currentTarget.value)} endAdornment="decimals" /> Number of decimals to be shown in token balances {`${actualDestinationAddress ? "Custom" : "Default"} destination address`} setDestinationAddressInner(event.currentTarget.value)} /> Recipient address of swapped assets and liquidity tokens } headerContent={ changeSwapTab(view === 0)} TabIndicatorProps={{ style: { display: "none" } }} > } > {isSwap ? : } ) } export default Dex;