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) => (
))}
>
);
};