import { Accordion, AccordionDetails, AccordionSummary, Box, Button, Typography, Skeleton, useTheme, } from "@mui/material"; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { ChangeEvent, useState, useMemo, useEffect } from "react"; import { useNavigate, createSearchParams } from "react-router-dom"; import { useQuery } from "react-query"; 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"; import { PrimaryButton, SecondaryButton } from "../../Button"; import { useBalance, useTokenSymbol } from "../../../hooks/tokens"; import { useNativePrice, useReservePrice, useFtsoPrice, useGhstPrice } from "../../../hooks/prices"; import { useLpValuation } from "../../../hooks/treasury"; import { useUniswapV2PairReserves } from "../../../hooks/uniswapv2"; import { getTokenIcons } from "../../../hooks/helpers"; import { useAccount, useBalance as useNativeBalance, useConfig, useSwitchChain, useChainId } from "wagmi"; const addTokenToWallet = async (token, userAddress) => { if (!window.ethereum) return; try { await window.ethereum.request({ method: "wallet_watchAsset", params: { type: "ERC20", options: { address: token.address, symbol: token.symbol, decimals: token.balance._decimals, image: token.externalUrl, // external host }, }, }); } catch (error) { console.log(error); } }; const BalanceValue = ({ balance, balanceValueUSD, isLoading = false, }) => ( {formatNumber(balance, 5)} {formatCurrency(balanceValueUSD, 2)} ); export const Token = (props) => { const { isNative, symbol, faucetPath, icons, address, price = 0, balance, onAddTokenToWallet, expanded, onChangeExpanded, reserveAddress, onClose, isPool } = props; const theme = useTheme(); const navigate = useNavigate(); const chainId = useChainId(); const { chains } = useSwitchChain(); const chainName = useMemo(() => { return chains.find(chain => chain.id === chainId).name.toLowerCase(); }, [chains, chainId]) const useLink = (symbol, fromAddress, toAddress, isPool) => { if (faucetPath) { navigate({ pathname: faucetPath }) } else { navigate({ pathname: `${chainName}/dex/uniswap`, search: isPool ? createSearchParams({ pool: "true", from: `${fromAddress}`, to: `${toAddress}`, }).toString() : createSearchParams({ from: `${fromAddress}`, to: `${toAddress}`, }).toString() }) } onClose(); } return ( } > {symbol} {!isNative && Add to Wallet useLink(symbol, reserveAddress, address, isPool)} fullWidth > Get {faucetPath ? "for Free" : "on DEX"} } ); }; const sumObjValues = (obj: Record = {}) => Object.values(obj).reduce((sum, b = "0.0") => sum + (parseFloat(b) || 0), 0); export const useWallet = (chainId, userAddress) => { const { data: nativeBalanceRaw, refetch: nativeBalanceRefetch } = useNativeBalance({ address: userAddress }); const nativeBalance = new DecimalBigNumber(nativeBalanceRaw?.value ?? 0n, 18); const { balance: reserveBalance, refetch: reserveRefetch, contractAddress: reserveAddress, } = useBalance(chainId, "RESERVE", userAddress); const { balance: ftsoBalance, refetch: ftsoRefetch, contractAddress: ftsoAddress, } = useBalance(chainId, "FTSO", userAddress); const { balance: ghstBalance, refetch: ghstRefetch, contractAddress: ghstAddress, } = useBalance(chainId, "GHST", userAddress); const { balance: lpReserveFtsoBalance, refetch: lpReserveFtsoRefetch, contractAddress: lpReserveFtsoBalanceAddress, } = useBalance(chainId, "RESERVE_FTSO", userAddress); const { tokens: lpReserveFtsoTokens, } = useUniswapV2PairReserves(chainId, "RESERVE_FTSO"); const nativePrice = useNativePrice(chainId); const reservePrice = useReservePrice(chainId); const ftsoPrice = useFtsoPrice(chainId); const ghstPrice = useGhstPrice(chainId); const lpReserveFtsoPrice = useLpValuation(chainId, "RESERVE_FTSO", 1000000000000000000n); const config = useConfig(); const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol; const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE"); const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO"); const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST"); const { symbol: lpReserveFtsoSymbol } = useTokenSymbol(chainId, "RESERVE_FTSO"); const lpReserveFtsoTokenNames = useMemo(() => { const token0 = getTokenIcons(chainId, lpReserveFtsoTokens?.token0 ?? []); const token1 = getTokenIcons(chainId, lpReserveFtsoTokens?.token1 ?? []); let tokenAddresses = [lpReserveFtsoTokens?.token1, lpReserveFtsoTokens?.token0]; let tokenNames = [...token1, ...token0]; if (token0?.at(0) === reserveSymbol) { tokenAddresses = [lpReserveFtsoTokens?.token0, lpReserveFtsoTokens?.token1]; let tokenNames = [...token0, ...token1]; } return { tokenAddresses, tokenNames } }, [chainId, reserveSymbol, lpReserveFtsoTokens]); const tokens = { native: { symbol: nativeSymbol, icons: [nativeSymbol], balance: nativeBalance, price: nativePrice, refetch: nativeBalanceRefetch, }, reserve: { symbol: reserveSymbol, address: reserveAddress, balance: reserveBalance, price: reservePrice, icons: isNetworkLegacy(chainId) ? ["GDAI"] : [tokenNameConverter(chainId, reserveSymbol)], externalUrl: isNetworkLegacy(chainId) ? "https://ghostchain.io/wp-content/uploads/2025/03/gDAI.svg" : "https://ghostchain.io/wp-content/uploads/2025/11/6A-Classic-ETC-Token.svg", refetch: reserveRefetch, }, ftso: { symbol: ftsoSymbol, address: ftsoAddress, balance: ftsoBalance, price: ftsoPrice, icons: ["FTSO"], externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/eGHST.svg", refetch: ftsoRefetch, }, ghst: { symbol: ghstSymbol, address: ghstAddress, balance: ghstBalance, price: ghstPrice, icons: ["GHST"], externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/GHST.svg", refetch: ghstRefetch, }, reserveFtso: { isPool: true, symbol: lpReserveFtsoSymbol, address: lpReserveFtsoTokenNames?.tokenAddresses.at(1) ?? "", balance: lpReserveFtsoBalance, price: lpReserveFtsoPrice, icons: lpReserveFtsoTokenNames?.tokenNames, externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/uni-v2.svg", refetch: lpReserveFtsoRefetch, } }; return Object.entries(tokens).reduce((wallet, [key, token]) => { return { ...wallet, [key]: { ...token, totalBalance: "0", }, }; }, {}); }; export const Tokens = ({ address, tokens, onClose }) => { const [expanded, setExpanded] = useState(null); const alwaysShowTokens = [tokens.native, tokens.reserve, tokens.ftso, tokens.ghst, tokens.reserveFtso]; const tokenProps = (token) => ({ ...token, expanded: expanded === token.symbol, reserveAddress: EMPTY_ADDRESS, onChangeExpanded: (e, isExpanded) => setExpanded(isExpanded ? token.symbol : null), onAddTokenToWallet: () => addTokenToWallet(token, address), onClose: () => onClose(), }); return ( <> {alwaysShowTokens.map((token, i) => ( ))} ); };