ghost-dao-interface/src/containers/TreasuryDashboard/components/ProtocolDetails.jsx
Uncle Fatso 2308d5dbab
fix sold out bonds representation on the left menu panel
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2026-04-28 13:32:09 +03:00

150 lines
6.8 KiB
JavaScript

import { useMemo } from "react";
import { Grid, Box, Typography } from "@mui/material";
import { useConfig } from "wagmi";
import { useNavigate, createSearchParams } from "react-router-dom";
import { formatNumber } from "../../../helpers";
import { useLiveBonds } from "../../../hooks/bonds/index";
import { useEpoch, useGatekeeperApy } from "../../../hooks/staking";
import { useTokenSymbol, useBalance, useCirculatingSupply } from "../../../hooks/tokens";
import { SecondaryButton } from "../../../components/Button";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
import { GATEKEEPER_ADDRESSES, EMPTY_ADDRESS } from "../../../constants/addresses";
const ProtocolDetail = ({ isMobileScreen, theme, name, sideName, url, urlParams, description }) => {
const navigate = useNavigate();
return (
<Box position="relative" width={`${isMobileScreen ? "100%" : "48%"}`}>
<Box
borderRadius="9px"
padding="12px"
sx={{ backgroundColor: theme.colors.paper.card }}
display="flex"
flexDirection="column"
justifyContent="space-between"
>
<Box display="flex" gap={"3px"} alignItems="center" justifyContent="space-between">
<Typography fontSize="20px" fontWeight="bold" lineHeight="33px">
{name}
</Typography>
<Typography color={theme.colors.primary[300]} fontSize="20px" fontWeight="bold" lineHeight="33px">
{sideName}
</Typography>
</Box>
<>
<Box my="18px">
<Typography color={theme.colors.gray[40]} mt="9px">
{description}
</Typography>
</Box>
<Box display="flex" justifyContent="center" width="100%">
<SecondaryButton
onClick={() => urlParams
? navigate({
pathname: url,
search: urlParams.toString()
})
: navigate(url, { replace: true })
}
fullWidth
>
{name}
</SecondaryButton>
</Box>
</>
</Box>
</Box>
)
}
const ProtocolDetails = ({ chainId, isMobileScreen, theme, }) => {
const config = useConfig();
const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
const networkName = config?.getClient()?.chain?.name;
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: csprSymbol } = useTokenSymbol(chainId, "CSPR");
const { contractAddress: ftsoAddress } = useBalance(chainId, "FTSO", EMPTY_ADDRESS);
const { liveBonds } = useLiveBonds(chainId);
const circulatingSupply = useCirculatingSupply(chainId, "STNK");
const gatekeepedApy = useGatekeeperApy(chainId);
const { epoch } = useEpoch(chainId);
const maxBondDiscountTest = useMemo(() => {
const sortedGhostBonds = liveBonds.filter((bond) => !bond.isSoldOut)
if (sortedGhostBonds?.length === 0) return "Coming Up";
const maxDiscountBond = sortedGhostBonds.reduce((prev, current) =>
(prev.discount > current.discount) ? prev : current
);
const maxDiscount = maxDiscountBond.discount.mul(new DecimalBigNumber(100, 0));
return `Up to ${formatNumber(maxDiscount, 0)}% Discount`;
}, [liveBonds]);
const apyInner = useMemo(() => {
let apy = Infinity;
if (circulatingSupply._value > 0n) {
const value = epoch.distribute.div(circulatingSupply);
apy = 100 * (Math.pow(1 + parseFloat(value.toString()), 1095) - 1);
if (apy === 0) apy = Infinity;
}
return apy;
}, [circulatingSupply, epoch]);
const bridgeNumbers = useMemo(() => {
const connectedNetworks = Object.keys(GATEKEEPER_ADDRESSES).length;
const number = 1 + connectedNetworks * 3;
return `(${number}, ${number})`;
}, [chainId]);
return (
<Grid container spacing={0} justifyContent={"center"}>
<Box display="flex" flexWrap="wrap" justifyContent="space-between" mt="10px" gap="25px">
<ProtocolDetail
isMobileScreen={isMobileScreen}
theme={theme}
url={`/${networkName.toLowerCase()}/dex/uniswap`}
urlParams={createSearchParams({
from: EMPTY_ADDRESS,
to: `${ftsoAddress}`,
})}
name="(3, 3) Swap"
sideName="Unlock Magic"
description={`Buying strategy expands bond capacity triggering a positive loop to strengthen the Treasury and attract more stakers. Swap ${nativeSymbol} for ${ftsoSymbol} to unlock (3, 3) Stake.`}
/>
<ProtocolDetail
isMobileScreen={isMobileScreen}
theme={theme}
url={`/${networkName.toLowerCase()}/bonds`}
name="(1, 1) Bond"
sideName={maxBondDiscountTest}
description={`Bonding strategy grows Treasury and builds Protocol Owned Liquidity (POL) by allowing users to acquire ${csprSymbol} at a discount. Take advantage of the next available bond.`}
/>
<ProtocolDetail
isMobileScreen={isMobileScreen}
theme={theme}
url={`/${networkName.toLowerCase()}/stake`}
name="(3, 3) Stake"
sideName={`${formatNumber(apyInner, 0)}% APY`}
description={`Staking enables (3, 3) coordination by aligning long-term incentives, sustainably backed by (1, 1) Bonds, LP fees, and Farming. Stake ${ftsoSymbol} to earn rewards and unlock ${bridgeNumbers} Stake\u00B2.`}
/>
<ProtocolDetail
isMobileScreen={isMobileScreen}
theme={theme}
url={`/${networkName.toLowerCase()}/bridge`}
name={`${bridgeNumbers} Stake\u00B2`}
sideName={`${formatNumber(apyInner * gatekeepedApy, 0)}% APY`}
description={`Staking\u00B2 strategy further deepens long-term incentives powered by sustainable crosschain bridging revenue. ${bridgeNumbers} Stake\u00B2 your ${csprSymbol} to receive organic APY with no warmup and no dilution.`}
/>
</Box>
</Grid>
)
}
export default ProtocolDetails;