From 8d23d55ae2021a1d042186d589f675d30f3f9922 Mon Sep 17 00:00:00 2001 From: Uncle Fatso Date: Thu, 19 Mar 2026 20:48:38 +0300 Subject: [PATCH] integrate native coin into dex Signed-off-by: Uncle Fatso --- package.json | 2 +- src/App.jsx | 2 +- .../TokenAllowanceGuard.jsx | 3 +- src/components/TopBar/Wallet/Token.tsx | 3 +- src/constants/addresses.js | 2 + src/containers/Dex/Dex.jsx | 58 +++++- src/containers/Dex/PoolContainer.jsx | 24 ++- src/containers/Dex/SwapContainer.jsx | 84 +++++--- src/containers/Dex/TokenModal.jsx | 22 +- src/containers/Stake/components/FarmPools.jsx | 4 +- src/helpers/tokenConverter.js | 2 +- src/hooks/helpers.js | 6 + src/hooks/tokens/index.js | 21 +- src/hooks/uniswapv2/UniswapV2Factory.js | 4 +- src/hooks/uniswapv2/UniswapV2Router.js | 191 +++++++++++++----- 15 files changed, 323 insertions(+), 105 deletions(-) diff --git a/package.json b/package.json index ed76c59..312b5a3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ghost-dao-interface", "private": true, - "version": "0.6.4", + "version": "0.6.5", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.jsx b/src/App.jsx index 145201c..2a94595 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -215,7 +215,7 @@ function App() { : } /> } } /> - } /> + } /> {isGovernanceAvailable(chainId, addressChainId) && } />} {isGovernanceAvailable(chainId, addressChainId) && } />} {isGovernanceAvailable(chainId, addressChainId) && } />} diff --git a/src/components/TokenAllowanceGuard/TokenAllowanceGuard.jsx b/src/components/TokenAllowanceGuard/TokenAllowanceGuard.jsx index 6e29b00..6da3955 100644 --- a/src/components/TokenAllowanceGuard/TokenAllowanceGuard.jsx +++ b/src/components/TokenAllowanceGuard/TokenAllowanceGuard.jsx @@ -58,6 +58,7 @@ export const TokenAllowanceGuard = ({ height = "auto", spendAmount, tokenName, + isNative, owner, spender, decimals, @@ -88,7 +89,7 @@ export const TokenAllowanceGuard = ({ ); } - if (allowance && spendAmount && allowance.lt(spendAmount)) + if (!isNative && allowance && spendAmount && allowance.lt(spendAmount)) return ( diff --git a/src/components/TopBar/Wallet/Token.tsx b/src/components/TopBar/Wallet/Token.tsx index 7b5bd29..4605a21 100644 --- a/src/components/TopBar/Wallet/Token.tsx +++ b/src/components/TopBar/Wallet/Token.tsx @@ -16,6 +16,7 @@ import { formatCurrency, formatNumber } from "../../../helpers"; import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber" import { tokenNameConverter } from "../../../helpers/tokenConverter"; import { isNetworkLegacy } from "../../../constants"; +import { EMPTY_ADDRESS } from "../../../constants/addresses"; import GhostStyledIcon from "../../Icon/GhostIcon"; import TokenStack from "../../TokenStack/TokenStack"; @@ -305,7 +306,7 @@ export const Tokens = ({ address, tokens, onClose }) => { const tokenProps = (token) => ({ ...token, expanded: expanded === token.symbol, - reserveAddress: tokens.reserve.address, + reserveAddress: EMPTY_ADDRESS, onChangeExpanded: (e, isExpanded) => setExpanded(isExpanded ? token.symbol : null), onAddTokenToWallet: () => addTokenToWallet(token, address), onClose: () => onClose(), diff --git a/src/constants/addresses.js b/src/constants/addresses.js index 616741b..451edc8 100644 --- a/src/constants/addresses.js +++ b/src/constants/addresses.js @@ -1,5 +1,7 @@ import { NetworkId } from "../constants"; +export const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000"; + export const STAKING_ADDRESSES = { [NetworkId.TESTNET_SEPOLIA]: "0xC2C579631Bf6daA93252154080fecfd68c6aa506", [NetworkId.TESTNET_HOODI]: "0x25F62eDc6C89FF84E957C22336A35d2dfc861a86", diff --git a/src/containers/Dex/Dex.jsx b/src/containers/Dex/Dex.jsx index 06c61df..35602e3 100644 --- a/src/containers/Dex/Dex.jsx +++ b/src/containers/Dex/Dex.jsx @@ -10,7 +10,7 @@ import { useTheme, } from "@mui/material"; import SettingsIcon from '@mui/icons-material/Settings'; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useParams, useLocation, useSearchParams } from "react-router-dom"; import { Helmet } from "react-helmet"; import ReactGA from "react-ga4"; @@ -28,14 +28,17 @@ import { 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 }) => { +const Dex = ({ chainId, address, connect, config }) => { const location = useLocation(); const pathname = useParams(); @@ -58,11 +61,44 @@ const Dex = ({ chainId, address, connect }) => { const [actualDestinationAddress, setActualDestinationAddress] = useState(localStorage.getItem("dex-destination")); const [destinationAddress, setDestinationAddress] = useState(actualDestinationAddress); - const [tokenAddressTop, setTokenAddressTop] = useState(RESERVE_ADDRESSES[chainId]); + const [tokenAddressTop, setTokenAddressTop] = useState(EMPTY_ADDRESS); const [tokenAddressBottom, setTokenAddressBottom] = useState(FTSO_ADDRESSES[chainId]); - const { symbol: tokenNameTop } = useTokenSymbol(chainId, tokenAddressTop); - const { symbol: tokenNameBottom } = useTokenSymbol(chainId, tokenAddressBottom); + 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")) { @@ -77,8 +113,8 @@ const Dex = ({ chainId, address, connect }) => { setTokenAddressTop(currentQueryParameters.get("from")); newQueryParameters.set("from", currentQueryParameters.get("from")); } else { - setTokenAddressTop(RESERVE_ADDRESSES[chainId]); - newQueryParameters.set("from", RESERVE_ADDRESSES[chainId]); + setTokenAddressTop(EMPTY_ADDRESS); + newQueryParameters.set("from", EMPTY_ADDRESS); } if (currentQueryParameters.has("to")) { @@ -94,7 +130,7 @@ const Dex = ({ chainId, address, connect }) => { useEffect(() => { ReactGA.send({ hitType: "pageview", page: location.pathname + location.search }); - }, [location]) + }, [location]); const dexAddresses = { router: UNISWAP_V2_ROUTER[chainId], @@ -115,7 +151,7 @@ const Dex = ({ chainId, address, connect }) => { } const changeSwapTab = (swap) => { - if (swap) newQueryParameters.delete("pool"); + if (swap || (isWrapping || isUnwrapping)) newQueryParameters.delete("pool"); else newQueryParameters.set("pool", true); newQueryParameters.set("from", currentQueryParameters.get("from")); newQueryParameters.set("to", currentQueryParameters.get("to")); @@ -315,6 +351,7 @@ const Dex = ({ chainId, address, connect }) => { { setTokenAddress={setInnerTokenAddressTop} /> { setBottomTokenListOpen={setBottomTokenListOpen} setIsSwap={setIsSwap} formatDecimals={formatDecimals} + isWrapping={isWrapping} + isUnwrapping={isUnwrapping} /> : { const theme = useTheme(); const isSmallScreen = useMediaQuery("(max-width: 456px)"); @@ -40,11 +45,13 @@ const PoolContainer = ({ balance: balanceTop, refetch: balanceRefetchTop, contractAddress: addressTop, + isNative: topIsNative, } = useBalance(chainId, tokenNameTop, address); const { balance: balanceBottom, refetch: balanceRefetchBottom, contractAddress: addressBottom, + isNative: bottomIsNative, } = useBalance(chainId, tokenNameBottom, address); const { @@ -142,7 +149,7 @@ const PoolContainer = ({ const amountAMin = amountADesired * bigIntSlippage / one; const amountBMin = amountBDesired * bigIntSlippage / one; - await addLiquidity( + const params = { chainId, tokenNameTop, tokenNameBottom, @@ -150,9 +157,16 @@ const PoolContainer = ({ amountBDesired, amountAMin, amountBMin, + address, destination, deadline, - ); + } + + if (topIsNative || bottomIsNative) { + await addLiquidityETH(params) + } else { + await addLiquidity(params); + } await balanceRefetchTop(); await balanceRefetchBottom(); @@ -233,6 +247,7 @@ const PoolContainer = ({ owner={address} spender={dexAddresses.router} decimals={balanceTop._decimals} + isNative={topIsNative} approvalText={"Approve " + tokenNameTop} approvalPendingText={"Approving..."} connect={connect} @@ -245,6 +260,7 @@ const PoolContainer = ({ owner={address} spender={dexAddresses.router} decimals={balanceBottom._decimals} + isNative={bottomIsNative} approvalText={"Approve " + tokenNameBottom} approvalPendingText={"Approving..."} connect={connect} diff --git a/src/containers/Dex/SwapContainer.jsx b/src/containers/Dex/SwapContainer.jsx index 180de50..36566a0 100644 --- a/src/containers/Dex/SwapContainer.jsx +++ b/src/containers/Dex/SwapContainer.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useMemo, useEffect } from "react"; import { Box, Typography, useTheme, useMediaQuery } from "@mui/material"; import toast from "react-hot-toast"; @@ -13,8 +13,16 @@ import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { prettifySecondsInDays } from "../../helpers/timeUtil"; import { getTokenAddress } from "../../hooks/helpers"; -import { useBalance } from "../../hooks/tokens"; -import { useUniswapV2Pair, useUniswapV2PairReserves, swapExactTokensForTokens } from "../../hooks/uniswapv2"; +import { useBalance, depositNative, withdrawWeth } from "../../hooks/tokens"; +import { + useUniswapV2Pair, + useUniswapV2PairReserves, + swapExactTokensForTokens, + swapExactETHForTokens, + swapExactTokensForETH, +} from "../../hooks/uniswapv2"; + +import { EMPTY_ADDRESS } from "../../constants/addresses"; const SwapContainer = ({ tokenNameTop, @@ -30,7 +38,9 @@ const SwapContainer = ({ destination, secondsToWait, setIsSwap, - formatDecimals + formatDecimals, + isWrapping, + isUnwrapping, }) => { const theme = useTheme(); const isSmallScreen = useMediaQuery("(max-width: 456px)"); @@ -46,12 +56,14 @@ const SwapContainer = ({ balance: balanceTop, refetch: balanceRefetchTop, contractAddress: addressTop, + isNative: topIsNative, } = useBalance(chainId, tokenNameTop, address); const { balance: balanceBottom, refetch: balanceRefetchBottom, contractAddress: addressBottom, + isNative: bottomIsNative, } = useBalance(chainId, tokenNameBottom, address); const { @@ -75,14 +87,18 @@ const SwapContainer = ({ const setMax = () => setAmountTop(balanceTop.toString()); useEffect(() => { + if (isWrapping || isUnwrapping) { + setAmountBottom(amountTop.toString()); + setNextPrice("1"); + setCurrentPrice("1"); + return; + } + const zero = new DecimalBigNumber(0n, 0); const raw = new DecimalBigNumber(amountTop, balanceTop._decimals); const amountInRaw = new DecimalBigNumber(raw._value.toBigInt(), balanceTop._decimals); const amountInWithFee = amountInRaw.mul(new DecimalBigNumber(997n, 3)); - const topAddress = getTokenAddress(chainId, tokenNameTop); - const bottomAddress = getTokenAddress(chainId, tokenNameBottom); - const amountIn = addressTop.toUpperCase() === tokenAddresses.token0.toUpperCase() ? pairReserves.reserve0 : pairReserves.reserve1; const amountOut = addressBottom.toUpperCase() === tokenAddresses.token1.toUpperCase() ? pairReserves.reserve1 : pairReserves.reserve0; @@ -103,7 +119,15 @@ const SwapContainer = ({ setAmountBottom(amountOut.sub(newAmountOut).toString()); setNextPrice(denominator.div(newAmountOut).toString()) } - }, [amountTop, addressTop]); + }, [amountTop, addressTop, isWrapping, isUnwrapping]); + + const buttonText = useMemo(() => { + let text = "Swap"; + if (isWrapping) text = "Wrap"; + else if (isUnwrapping) text = "Unwrap"; + else if (pairAddress === EMPTY_ADDRESS) text = "Create Pool"; + return text; + }, [isWrapping, isUnwrapping, pairAddress]); const swapTokens = async () => { setIsPending(true); @@ -120,14 +144,30 @@ const SwapContainer = ({ const amountBDesired = BigInt(Math.round(parseFloat(amountBottom) * Math.pow(10, balanceBottom._decimals))); const amountBMin = amountBDesired * bigIntSlippage / one; - await swapExactTokensForTokens( - chainId, - amountADesired, - amountBMin, - [tokenNameTop, tokenNameBottom], - destination, - deadline - ); + if (isWrapping) { + await depositNative(chainId, address, amountADesired); + } else if (isUnwrapping) { + await withdrawWeth(chainId, address, amountADesired); + } else { + const params = { + chainId, + amountADesired, + amountBMin, + tokenNameTop, + tokenNameBottom, + destination, + address, + deadline + }; + + if (topIsNative) { + await swapExactETHForTokens(params) + } else if (bottomIsNative) { + await swapExactTokensForETH(params) + } else { + await swapExactTokensForTokens(params); + } + } await balanceRefetchTop(); await balanceRefetchBottom(); @@ -204,6 +244,7 @@ const SwapContainer = ({ owner={address} spender={dexAddresses.router} decimals={balanceTop._decimals} + isNative={topIsNative} approvalText={"Approve " + tokenNameTop} approvalPendingText={"Approving..."} connect={connect} @@ -223,17 +264,10 @@ const SwapContainer = ({ onClick={() => address === "" ? connect() : - pairAddress === "0x0000000000000000000000000000000000000000" ? setIsSwap(false) : swapTokens() + (!isWrapping && !isUnwrapping) && pairAddress === EMPTY_ADDRESS ? setIsSwap(false) : swapTokens() } > - {address === "" ? - "Connect" - : - pairAddress === "0x0000000000000000000000000000000000000000" ? - "Create Pool" - : - "Swap" - } + {address === "" ? "Connect" : buttonText } diff --git a/src/containers/Dex/TokenModal.jsx b/src/containers/Dex/TokenModal.jsx index 2345c7e..7bb847e 100644 --- a/src/containers/Dex/TokenModal.jsx +++ b/src/containers/Dex/TokenModal.jsx @@ -21,9 +21,15 @@ import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { formatNumber } from "../../helpers/"; import { useBalance, useTokenSymbol } from "../../hooks/tokens"; import { isNetworkLegacy } from "../../constants"; -import { RESERVE_ADDRESSES, FTSO_ADDRESSES, STNK_ADDRESSES, GHST_ADDRESSES } from "../../constants/addresses"; +import { + RESERVE_ADDRESSES, + FTSO_ADDRESSES, + STNK_ADDRESSES, + GHST_ADDRESSES, + EMPTY_ADDRESS +} from "../../constants/addresses"; -const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }) => { +const TokenModal = ({ chainId, account, chainSymbol, listOpen, setListOpen, setTokenAddress }) => { const isSmallScreen = useMediaQuery("(max-width: 599px)"); const isVerySmallScreen = useMediaQuery("(max-width: 425px)"); @@ -39,6 +45,7 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress } const { symbol: searchSymbol } = useTokenSymbol(chainId, address); const { balance: searchBalance } = useBalance(chainId, address, account); + const { balance: nativeBalance } = useBalance(chainId, chainSymbol, account); const { balance: reserveBalance } = useBalance(chainId, "RESERVE", account); const { balance: ftsoBalance } = useBalance(chainId, "FTSO", account); const { balance: stnkBalance } = useBalance(chainId, "STNK", account); @@ -49,9 +56,6 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress } const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK"); const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST"); - const config = useConfig(); - const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol; - const searchToken = useMemo(() => { return [{ name: searchSymbol, @@ -63,9 +67,15 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress } const knownTokens = useMemo(() => { return [ + { + name: chainSymbol, + icons: [chainSymbol], + balance: nativeBalance, + address: EMPTY_ADDRESS, + }, { name: reserveSymbol, - icons: isNetworkLegacy(chainId) ? ["GDAI"] : [nativeSymbol], + icons: isNetworkLegacy(chainId) ? ["GDAI"] : [chainSymbol], balance: reserveBalance, address: RESERVE_ADDRESSES[chainId] }, diff --git a/src/containers/Stake/components/FarmPools.jsx b/src/containers/Stake/components/FarmPools.jsx index e56943a..5b68d7e 100644 --- a/src/containers/Stake/components/FarmPools.jsx +++ b/src/containers/Stake/components/FarmPools.jsx @@ -22,7 +22,7 @@ import { isNetworkLegacy } from "../../../constants"; import { useLpValuation } from "../../../hooks/treasury"; import { useTotalSupply, useTokenSymbol } from "../../../hooks/tokens"; -import { RESERVE_ADDRESSES, FTSO_ADDRESSES } from "../../../constants/addresses"; +import { EMPTY_ADDRESS, FTSO_ADDRESSES } from "../../../constants/addresses"; const FarmPools = ({ chainId }) => { const isSmallScreen = useMediaQuery("(max-width: 775px)"); @@ -42,7 +42,7 @@ const FarmPools = ({ chainId }) => { tvl: reserveFtsoUniValuation, params: createSearchParams({ pool: "true", - from: `${RESERVE_ADDRESSES[chainId]}`, + from: `${EMPTY_ADDRESS}`, to: `${FTSO_ADDRESSES[chainId]}`, }) }, diff --git a/src/helpers/tokenConverter.js b/src/helpers/tokenConverter.js index b6120f7..bd66ce6 100644 --- a/src/helpers/tokenConverter.js +++ b/src/helpers/tokenConverter.js @@ -1,4 +1,4 @@ -export const tokenNameConverter = (chainId, name) => { +export const tokenNameConverter = (chainId, name, address) => { if (name?.toUpperCase() === "WETH") { switch (chainId) { case 63: diff --git a/src/hooks/helpers.js b/src/hooks/helpers.js index c365879..c477967 100644 --- a/src/hooks/helpers.js +++ b/src/hooks/helpers.js @@ -142,6 +142,12 @@ export const getTokenAddress = (chainId, name) => { case "WMETC": address = WETH_ADDRESSES[chainId]; break; + case "ETH": + address = undefined; + break; + case "METC": + address = undefined; + break; } return address; } diff --git a/src/hooks/tokens/index.js b/src/hooks/tokens/index.js index 0e4b62c..da97e1c 100644 --- a/src/hooks/tokens/index.js +++ b/src/hooks/tokens/index.js @@ -9,6 +9,7 @@ import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { shorten } from "../../helpers"; import { tokenNameConverter } from "../../helpers/tokenConverter"; import { config } from "../../config"; +import { WETH_ADDRESSES } from "../../constants/addresses"; export const usePastVotes = (chainId, name, timepoint, address) => { const decimals = getTokenDecimals(name); @@ -60,20 +61,29 @@ export const useTotalSupply = (chainId, name) => { }; export const useBalance = (chainId, name, address) => { - const contractAddress = getTokenAddress(chainId, name); - const { data, refetch, error } = useInnerBalance({ + let contractAddress = getTokenAddress(chainId, name); + let isNative = false; + + let requestObj = { address, chainId, scopeKey: `balance-${contractAddress}-${address}-${chainId}`, - token: contractAddress, - }); + }; + if (contractAddress !== undefined) { + requestObj.token = contractAddress; + } else { + contractAddress = WETH_ADDRESSES[chainId]; + isNative = true; + } + + const { data, refetch, error } = useInnerBalance(requestObj); const balancePrepared = data ? data.value : 0n; const decimals = data ? data.decimals : getTokenDecimals(name); const balance = new DecimalBigNumber(balancePrepared, decimals); - return { balance, refetch, contractAddress }; + return { balance, refetch, contractAddress, isNative }; } export const useAllowance = (chainId, name, owner, spender, decimals) => { @@ -96,6 +106,7 @@ export const useAllowance = (chainId, name, owner, spender, decimals) => { export const useTokenSymbol = (chainId, name) => { const contractAddress = getTokenAddress(chainId, name); + const { data, refetch } = useReadContract({ abi: getTokenAbi(name), address: contractAddress, diff --git a/src/hooks/uniswapv2/UniswapV2Factory.js b/src/hooks/uniswapv2/UniswapV2Factory.js index 0c73640..626f10f 100644 --- a/src/hooks/uniswapv2/UniswapV2Factory.js +++ b/src/hooks/uniswapv2/UniswapV2Factory.js @@ -4,14 +4,12 @@ import { abi as UniswapV2Factory } from "../../abi/UniswapV2Factory.json"; import { UNISWAP_V2_FACTORY } from "../../constants/addresses"; export const useUniswapV2Pair = (chainId, factoryAddress, token0, token1) => { - const t0 = token0 > token1 ? token0 : token1; - const t1 = token0 > token1 ? token1 : token0; const { data, refetch } = useReadContract({ abi: UniswapV2Factory, address: factoryAddress, functionName: "getPair", args: [token0, token1], - scopeKey: `getPair-${t0}-${t1}-${chainId}`, + scopeKey: `getPair-${token0}-${token1}-${chainId}`, chainId: chainId, }); diff --git a/src/hooks/uniswapv2/UniswapV2Router.js b/src/hooks/uniswapv2/UniswapV2Router.js index e4b1f3b..0ff83da 100644 --- a/src/hooks/uniswapv2/UniswapV2Router.js +++ b/src/hooks/uniswapv2/UniswapV2Router.js @@ -2,91 +2,189 @@ import { simulateContract, writeContract, waitForTransactionReceipt } from "@wag import toast from "react-hot-toast"; import { isNetworkLegacyType } from "../../constants"; -import { UNISWAP_V2_ROUTER } from "../../constants/addresses"; +import { UNISWAP_V2_ROUTER, WETH_ADDRESSES } from "../../constants/addresses"; import { abi as RouterAbi } from "../../abi/UniswapV2Router.json"; import { getTokenAddress } from "../helpers"; import { config } from "../../config"; -export const swapExactTokensForTokens = async ( +const swapMessages = { + replacedMsg: "Swap transaction was replaced. Wait for inclusion please.", + successMsg: "Swap executed successfully! Wait for balances update.", + errorMsg: "Swap tokens failed. Check logs for error detalization.", +}; + +const addMessages = { + replacedMsg: "Add liquidity transaction was replaced. Wait for inclusion please.", + successMsg: "Liquidity added successfully! You should get LP tokens to your wallet.", + errorMsg: "Adding liquidity failed. Check logs for error detalization.", +}; + +export const swapExactETHForTokens = async ({ chainId, - amountDesired, - amountMin, - pathRaw, + amountADesired, + amountBMin, + tokenNameTop, + tokenNameBottom, destination, deadline -) => { - - const path = pathRaw.map(tokenName => getTokenAddress(chainId, tokenName)); +}) => { const args = [ - amountDesired, - amountMin, - path, + amountBMin, + [WETH_ADDRESSES[chainId], getTokenAddress(chainId, tokenNameBottom)], destination, deadline ]; - const messages = { - replacedMsg: "Swap transaction was replaced. Wait for inclusion please.", - successMsg: "Swap executed successfully! Wait for balances update.", - errorMsg: "Swap tokens failed. Check logs for error detalization.", - }; - - await executeOnChainTransaction( + await executeOnChainTransaction({ chainId, - "swapExactTokensForTokens", + functionName: "swapExactETHForTokens", args, - destination, - messages - ); + account: destination, + messages: swapMessages, + value: amountADesired, + }); } -export const addLiquidity = async ( +export const swapExactTokensForETH = async ({ chainId, - tokenA, - tokenB, + amountADesired, + amountBMin, + tokenNameTop, + tokenNameBottom, + destination, + address, + deadline +}) => { + const args = [ + amountADesired, + amountBMin, + [getTokenAddress(chainId, tokenNameTop), WETH_ADDRESSES[chainId]], + destination, + deadline + ]; + + await executeOnChainTransaction({ + chainId, + functionName: "swapExactTokensForETH", + args, + account: address, + messages: swapMessages, + }); +} + +export const swapExactTokensForTokens = async ({ + chainId, + amountADesired, + amountBMin, + tokenNameTop, + tokenNameBottom, + destination, + address, + deadline +}) => { + const args = [ + amountADesired, + amountBMin, + [getTokenAddress(chainId, tokenNameTop), getTokenAddress(chainId, tokenNameBottom)], + destination, + deadline + ]; + + await executeOnChainTransaction({ + chainId, + functionName: "swapExactTokensForTokens", + args, + account: address, + messages: swapMessages + }); +} + +export const addLiquidity = async ({ + chainId, + tokenNameTop, + tokenNameBottom, amountADesired, amountBDesired, amountAMin, amountBMin, - to, + address, + destination, deadline, -) => { - const token1 = getTokenAddress(chainId, tokenA); - const token2 = getTokenAddress(chainId, tokenB); - +}) => { const args = [ - token1, - token2, + getTokenAddress(chainId, tokenNameTop), + getTokenAddress(chainId, tokenNameBottom), amountADesired, amountBDesired, amountAMin, amountBMin, - to, + destination, deadline ]; - const messages = { - replacedMsg: "Add liquidity transaction was replaced. Wait for inclusion please.", - successMsg: "Liquidity added successfully! You should get LP tokens to your wallet.", - errorMsg: "Adding liquidity failed. Check logs for error detalization.", - }; - await executeOnChainTransaction( + await executeOnChainTransaction({ chainId, - "addLiquidity", + functionName: "addLiquidity", args, - to, - messages - ); + account: address, + messages: addMessages + }); } -const executeOnChainTransaction = async ( +export const addLiquidityETH = async ({ + chainId, + tokenNameTop, + tokenNameBottom, + amountADesired, + amountBDesired, + amountAMin, + amountBMin, + address, + destination, + deadline, +}) => { + let token = getTokenAddress(chainId, tokenNameTop); + let amountTokenDesired = amountADesired; + let amountETHDesired = amountBDesired; + let amountTokenMin = amountAMin; + let amountETHMin = amountBMin; + + if (token === undefined) { + token = getTokenAddress(chainId, tokenNameBottom); + amountTokenDesired = amountBDesired; + amountETHDesired = amountADesired; + amountTokenMin = amountBMin; + amountETHMin = amountAMin; + } + + const args = [ + token, + amountTokenDesired, + amountTokenMin, + amountETHMin, + destination, + deadline + ]; + + await executeOnChainTransaction({ + chainId, + functionName: "addLiquidityETH", + args, + account: address, + messages: addMessages, + value: amountETHDesired, + }); +} + +const executeOnChainTransaction = async ({ chainId, functionName, args, account, - messages -) => { + messages, + value, +}) => { try { const { request } = await simulateContract(config, { abi: RouterAbi, @@ -96,6 +194,7 @@ const executeOnChainTransaction = async ( account, chainId, type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559', + value: value ?? 0n, }); const txHash = await writeContract(config, request);