ghost-dao-interface/src/components/TopBar/Wallet/Token.tsx
Uncle Fatso 5c7546b0d9
refetch token balances on each wallet open
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-08-20 21:39:28 +03:00

268 lines
9.1 KiB
TypeScript

import {
Accordion,
AccordionDetails,
AccordionSummary,
Box,
Button,
Typography,
Skeleton,
useTheme,
} from "@mui/material";
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { ChangeEvent, useState, useEffect } from "react";
import { useNavigate, createSearchParams } from "react-router-dom";
import { useQuery } from "react-query";
import { formatCurrency, formatNumber } from "../../../helpers";
import GhostStyledIcon from "../../Icon/GhostIcon";
import TokenStack from "../../TokenStack/TokenStack";
import { PrimaryButton, SecondaryButton } from "../../Button";
import { useBalance, useTokenSymbol } from "../../../hooks/tokens";
import { useDaiPrice, useFtsoPrice, useStnkPrice, useGhstPrice } from "../../../hooks/prices";
import { useLpValuation } from "../../../hooks/treasury";
import { useAccount } 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,
}) => (
<Box sx={{ textAlign: "right", display: "flex", flexDirection: "column", justifyContent: "space-between" }}>
<Typography variant="body2" style={{ fontWeight: 600 }}>
{formatNumber(balance, 5)}
</Typography>
<Typography variant="body2" color="textSecondary">
{formatCurrency(balanceValueUSD, 2)}
</Typography>
</Box>
);
export const Token = (props) => {
const {
symbol,
icons,
address,
price = 0,
balance,
onAddTokenToWallet,
expanded,
onChangeExpanded,
daiAddress,
onClose,
isPool
} = props;
const theme = useTheme();
const navigate = useNavigate();
const useLink = (symbol, fromAddress, toAddress, isPool) => {
if (symbol.toUpperCase() === "GDAI") {
navigate({ pathname: "/faucet" })
} else {
navigate({
pathname: "/dex/uniswap",
search: isPool ?
createSearchParams({
pool: "true",
from: `${fromAddress}`,
to: `${toAddress}`,
}).toString()
:
createSearchParams({
from: `${fromAddress}`,
to: `${toAddress}`,
}).toString()
})
}
onClose();
}
return (
<Accordion expanded={expanded} onChange={onChangeExpanded}>
<AccordionSummary expandIcon={<GhostStyledIcon component={ExpandMoreIcon} color="disabled" />}>
<Box sx={{ display: "flex", justifyContent: "space-between", width: "100%", marginRight: "10px" }}>
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
<TokenStack
style={{
width: "28px",
height: "28px",
}}
tokens={props.icons}
/>
<Typography>{symbol}</Typography>
</Box>
<BalanceValue
balance={balance}
balanceValueUSD={balance.mul(price)}
/>
</Box>
</AccordionSummary>
<AccordionDetails style={{ margin: "auto", padding: theme.spacing(1, 0) }}>
<Box
sx={{ display: "flex", flexDirection: "column", flex: 1, mx: "32px", justifyContent: "center" }}
style={{ gap: theme.spacing(1) }}
>
<Box display="flex" flexDirection="column" className="ghst-pairs" style={{ width: "100%" }}>
<PrimaryButton
onClick={onAddTokenToWallet}
fullWidth
>
<Typography>Add to Wallet</Typography>
</PrimaryButton>
<SecondaryButton
onClick={() => useLink(symbol, daiAddress, address, isPool)}
fullWidth
>
<Typography>Get on {symbol.toUpperCase() === "GDAI" ? "Faucet" : "Uniswap"}</Typography>
</SecondaryButton>
</Box>
</Box>
</AccordionDetails>
</Accordion>
);
};
const sumObjValues = (obj: Record<string, string> = {}) =>
Object.values(obj).reduce((sum, b = "0.0") => sum + (parseFloat(b) || 0), 0);
export const useWallet = (chainId, userAddress) => {
const {
balance: daiBalance,
refetch: daiRefetch,
contractAddress: daiAddress,
} = useBalance(chainId, "GDAI", userAddress);
const {
balance: ftsoBalance,
refetch: ftsoRefetch,
contractAddress: ftsoAddress,
} = useBalance(chainId, "FTSO", userAddress);
const {
balance: stnkBalance,
refetch: stnkRefetch,
contractAddress: stnkAddress,
} = useBalance(chainId, "STNK", userAddress);
const {
balance: ghstBalance,
refetch: ghstRefetch,
contractAddress: ghstAddress,
} = useBalance(chainId, "GHST", userAddress);
const {
balance: lpDaiFtsoBalance,
refetch: lpDaiFtsoRefetch,
contractAddress: lpDaiFtsoBalanceAddress,
} = useBalance(chainId, "GDAI_FTSO", userAddress);
const daiPrice = useDaiPrice(chainId);
const ftsoPrice = useFtsoPrice(chainId);
const stnkPrice = useStnkPrice(chainId);
const ghstPrice = useGhstPrice(chainId);
const lpDaiFtsoPrice = useLpValuation(chainId, "GDAI_FTSO", 1000000000000000000n);
const { symbol: daiSymbol } = useTokenSymbol(chainId, "GDAI");
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK");
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
const { symbol: lpDaiFtsoSymbol } = useTokenSymbol(chainId, "GDAI_FTSO");
const tokens = {
dai: {
symbol: daiSymbol,
address: daiAddress,
balance: daiBalance,
price: daiPrice,
icons: ["GDAI"],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/gDAI.svg",
refetch: daiRefetch,
},
ftso: {
symbol: ftsoSymbol,
address: ftsoAddress,
balance: ftsoBalance,
price: ftsoPrice,
icons: ["FTSO"],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/eGHST.svg",
refetch: ftsoRefetch,
},
stnk: {
symbol: stnkSymbol,
address: stnkAddress,
balance: stnkBalance,
price: stnkPrice,
icons: ["STNK"],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/sGHST.svg",
refetch: stnkRefetch,
},
ghst: {
symbol: ghstSymbol,
address: ghstAddress,
balance: ghstBalance,
price: ghstPrice,
icons: ["GHST"],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/GHST.svg",
refetch: ghstRefetch,
},
daiFtso: {
isPool: true,
symbol: lpDaiFtsoSymbol,
address: lpDaiFtsoBalanceAddress,
balance: lpDaiFtsoBalance,
price: lpDaiFtsoPrice,
icons: ["GDAI", "FTSO"],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/uni-v2.svg",
refetch: lpDaiFtsoRefetch,
}
};
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.dai, tokens.ftso, tokens.stnk, tokens.ghst, tokens.daiFtso];
const tokenProps = (token) => ({
...token,
expanded: expanded === token.symbol,
daiAddress: tokens.dai.address,
onChangeExpanded: (e, isExpanded) => setExpanded(isExpanded ? token.symbol : null),
onAddTokenToWallet: () => addTokenToWallet(token, address),
onClose: () => onClose(),
});
return (
<>
{alwaysShowTokens.map((token, i) => (
<Token key={i} {...tokenProps(token)} />
))}
</>
);
};