ghost-dao-interface/src/containers/Bond/BondModal.jsx
Uncle Fatso 421f2cef27
add flexibility during bond purchase
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2026-04-07 21:54:48 +03:00

188 lines
7.5 KiB
JavaScript

import { ArrowBack } from "@mui/icons-material";
import { Box, Link, Skeleton, Typography } from "@mui/material";
import { useEffect, useState, useMemo, useCallback } from "react";
import { Link as RouterLink, useLocation, useParams, useNavigate } from "react-router-dom";
import { useAccount, useSwitchChain } from "wagmi";
import { isAddress } from "viem";
import ReactGA from "react-ga4";
import PageTitle from "../../components/PageTitle/PageTitle";
import Metric from "../../components/Metric/Metric";
import TokenStack from "../../components/TokenStack/TokenStack";
import BondPrice from "./components/BondPrice";
import BondDiscount from "./components/BondDiscount";
import BondInputArea from "./components/BondInputArea";
import BondSettingsModal from "./components/BondSettingsModal";
import NotFound from "../NotFound/NotFound";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { isNetworkLegacy } from "../../constants";
import { formatCurrency } from "../../helpers";
import { useLocalStorage } from "../../hooks/localstorage";
import { useLiveBonds } from "../../hooks/bonds";
import { useFtsoPrice } from "../../hooks/prices";
const BondModalContainer = ({ chainId, address, config, connect }) => {
const { id, network } = useParams();
const { liveBonds } = useLiveBonds(chainId, network);
const bond = liveBonds.find(bond => bond.id === Number(id));
if (!bond) return <NotFound />;
return <BondModal config={config} chainId={chainId} bond={bond} address={address} connect={connect} />;
};
export const BondModal = ({ bond, chainId, address, config, connect }) => {
const navigate = useNavigate();
const { network } = useParams();
const { pathname } = useLocation();
const [isSettingsOpen, setSettingsOpen] = useState(false);
const { getStorageValue, setStorageValue } = useLocalStorage();
const [slippage, setSlippage] = useState(() => getStorageValue(chainId, address, "bond-slippage", "10"));
const [formatDecimals, setFormatDecimals] = useState(() => getStorageValue(chainId, address, "bond-decimals", "5"));
const [recipientAddress, setRecipientAddressInner] = useState(() => getStorageValue(chainId, address, "bond-recipient", address));
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: pathname });
}, [])
const setRecipientAddress = useCallback((value) => {
setRecipientAddressInner(value);
if (isAddress(value)) {
setStorageValue(chainId, address, "bond-recipient", value);
}
}, [chainId, address]);
const setSlippageInner = useCallback((value) => {
const maybeValue = parseFloat(value);
if (!maybeValue || parseFloat(value) <= 100) {
setSlippage(value);
setStorageValue(chainId, address, "bond-slippage", value);
}
}, [chainId, address]);
const setFormatDecimalsInner = useCallback((value) => {
if (Number(value) <= 17) {
setFormatDecimals(value);
setStorageValue(chainId, address, "bond-decimals", value);
}
}, [chainId, address]);
useEffect(() => {
const handleKeyDown = (event) => {
if (event.key === "Escape") isSettingsOpen ? setSettingsOpen(false) : navigate(`/${network}/bonds`);
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [network, navigate, isSettingsOpen]);
const chainSymbol = useMemo(() => {
const chainSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
if (chainSymbol) return chainSymbol;
return "WTF";
}, [config]);
const preparedQuoteToken = useMemo(() => {
if (isNetworkLegacy(chainId)) {
return {
address: bond.quoteToken.quoteTokenAddress,
icons: bond.quoteToken.icons,
name: bond.quoteToken.name,
}
}
return { address: undefined, icons: [chainSymbol], name: chainSymbol };
}, [bond, chainSymbol, chainId])
return (
<Box>
<PageTitle
name={
<Box display="flex" flexDirection="row" alignItems="center">
<Link component={RouterLink} to={`/${network}/bonds`}>
<Box display="flex" flexDirection="row">
<ArrowBack />
<Typography fontWeight="500" marginLeft="9.5px" marginRight="18px">
Back
</Typography>
</Box>
</Link>
<TokenStack tokens={preparedQuoteToken.icons} sx={{ fontSize: "27px" }} />
<Box display="flex" flexDirection="column" ml={1} justifyContent="center" alignItems="center">
<Typography variant="h4" fontWeight={500}>
{preparedQuoteToken.name}
</Typography>
</Box>
</Box>
}
/>
<Box display="flex" flexDirection="column" alignItems="center">
<BondSettingsModal
open={isSettingsOpen}
handleClose={() => setSettingsOpen(false)}
slippage={slippage}
recipientAddress={recipientAddress}
formatDecimals={formatDecimals}
onRecipientAddressChange={event => setRecipientAddress(event.currentTarget.value)}
onSlippageChange={event => setSlippageInner(event.currentTarget.value)}
onDecimalsChange={event => setFormatDecimalsInner(event.currentTarget.value)}
/>
<Box display="flex" flexDirection="row" justifyContent="space-between" width={["100%", "70%"]} mt="24px">
<Metric
label={`Bond Price`}
metric={
bond.isSoldOut ? (
"--"
) : (
<BondPrice
price={bond.price.inUsd}
/>
)
}
/>
<Metric
label="Market Price"
metric={<TokenPrice priceInUsd={bond.price.marketPriceInUsd} />}
/>
<Metric
label="Discount"
metric={<BondDiscount discount={bond.discount} textOnly />}
/>
</Box>
<Box width="100%" mt="24px">
<BondInputArea
chainId={chainId}
bond={bond}
config={config}
connect={connect}
address={address}
slippage={slippage}
recipientAddress={recipientAddress}
preparedQuoteToken={preparedQuoteToken}
handleSettingsOpen={() => setSettingsOpen(true)}
formatDecimals={formatDecimals}
/>
</Box>
</Box>
</Box>
);
};
const TokenPrice = ({ priceInUsd }) => {
return priceInUsd ? (
<>{formatCurrency(priceInUsd, 2)}</>
) : (
<Skeleton width={60} />
);
};
export default BondModalContainer;