268 lines
9.1 KiB
TypeScript
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)} />
|
|
))}
|
|
</>
|
|
);
|
|
};
|