ghost-dao-interface/src/containers/Bond/components/ClaimBonds.jsx
Uncle Fatso 8bfc14f2f0
add price estimation for bond notes and warmup during staking
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2026-04-07 13:46:39 +03:00

236 lines
12 KiB
JavaScript

import { Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
import { useState, useEffect } from "react";
import toast from "react-hot-toast";
import BondVesting from "./BondVesting";
import BondDuration from "./BondDuration";
import WarmupConfirmModal from "./WarmupConfirmModal";
import Paper from "../../../components/Paper/Paper";
import TokenStack from "../../../components/TokenStack/TokenStack";
import { Tab, Tabs } from "../../../components/Tabs/Tabs";
import { PrimaryButton, TertiaryButton } from "../../../components/Button";
import { useScreenSize } from "../../../hooks/useScreenSize";
import { BOND_DEPOSITORY_ADDRESSES } from "../../../constants/addresses";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
import { formatCurrency } from "../../../helpers";
import { useCurrentIndex, useEpoch, useWarmupLength, useWarmupInfo } from "../../../hooks/staking";
import { useNotes, redeem } from "../../../hooks/bonds";
import { useTokenSymbol } from "../../../hooks/tokens";
import { useGhstPrice } from "../../../hooks/prices";
export const ClaimBonds = ({ chainId, address, secondsTo }) => {
const isSmallScreen = useScreenSize("md");
const [isPending, setIsPending] = useState(false);
const [isWarmup, setIsWapmup] = useState(false);
const [isPreClaimConfirmed, setPreClaimConfirmed] = useState(false);
const [isPayoutGhst, _] = useState(true);
const ghstPrice = useGhstPrice(chainId);
const { epoch } = useEpoch(chainId);
const { warmupExists } = useWarmupLength(chainId);
const { warmupInfo } = useWarmupInfo(chainId, BOND_DEPOSITORY_ADDRESSES[chainId]);
const { currentIndex, refetch: currentIndexRefetch } = useCurrentIndex(chainId);
const { notes, refetch: notesRefetch } = useNotes(chainId, address);
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
if (!notes || notes.length === 0) return null;
const totalClaimableBalance = new DecimalBigNumber(
notes.reduce((res, note) => {
if (secondsTo < note.matured) return res + 0n;
return res + note.payout._value
}, 0n),
18
);
const onSubmit = async (indexes) => {
const isFundsInWarmup = warmupInfo.deposit._value > 0n;
if (warmupExists && isFundsInWarmup && !isPreClaimConfirmed) {
setIsWapmup(true);
} else {
setIsPending(true);
await redeem(
chainId,
address,
isPayoutGhst,
indexes
);
await notesRefetch();
setIsPending(false);
}
}
return (
<>
<WarmupConfirmModal
isOpen={isWarmup}
handleConfirmClose={() => setIsWapmup(false)}
address={address}
warmupLength={warmupInfo.expiry - epoch.number}
setPreClaimConfirmed={() => setPreClaimConfirmed(true)}
/>
<Paper
fullWidth
enableBackground
headerContent={
<Box display="flex" alignItems="center" flexDirection="row" gap="5px">
<Typography variant="h6">
Your Bonds
</Typography>
</Box>
}
>
<Box display="flex" justifyContent="center">
<Box display="flex" flexDirection="column" alignItems="center" mt="24px" width={isSmallScreen ? "100%" : "50%"}>
<Typography variant="h5" align="center" color="textSecondary" style={{ fontSize: "1.2rem" }}>
Claimable Balance
</Typography>
<Box mt="4px" mb="8px">
<Typography variant="h4" align="center">
{isPayoutGhst
? formatCurrency(totalClaimableBalance, 5, ghstSymbol)
: formatCurrency(currentIndex.mul(totalClaimableBalance), 5, stnkSymbol)
}
&nbsp;
({formatCurrency(totalClaimableBalance * ghstPrice, 2)})
</Typography>
</Box>
<PrimaryButton
disabled={isPending || totalClaimableBalance._value === 0n}
fullWidth
className=""
onClick={() => onSubmit(notes.filter((note) => secondsTo > note.matured).map(note => note.id))}
>
Claim All
</PrimaryButton>
</Box>
</Box>
<Box mt="48px">
{isSmallScreen ? (
<>
{notes.map((note, index) => (
<Box key={index} mt="32px">
<Box display="flex" alignItems="center">
<TokenStack tokens={note.quoteToken.icons} />
<Box ml="8px">
<Typography>{note.quoteToken.name}</Typography>
</Box>
</Box>
<Box display="flex" justifyContent="space-between" mt="16px">
<Typography>Duration</Typography>
<Typography>
<BondVesting vesting={note.vesting} />
</Typography>
</Box>
<Box display="flex" justifyContent="space-between" mt="8px">
<Typography>Remaining</Typography>
<Typography>
<BondDuration msg="Matured" secondsTo={secondsTo} duration={note.matured} />
</Typography>
</Box>
<Box display="flex" justifyContent="space-between" mt="8px">
<Typography>Payout</Typography>
<Box display="flex" flexDirection="column" alignItems="flex-end">
<Typography>
{isPayoutGhst
? formatCurrency(note.payout, 5, ghstSymbol)
: formatCurrency(currentIndex.mul(note.payout), 5, stnkSymbol)
}
</Typography>
<Typography>{formatCurrency(note.payout * ghstPrice, 2)}</Typography>
</Box>
</Box>
<Box mt="16px">
<TertiaryButton
fullWidth
disabled={isPending || secondsTo < note.matured}
onClick={() => onSubmit([note.id])}
>
Claim
</TertiaryButton>
</Box>
</Box>
))}
</>
) : (
<TableContainer>
<Table aria-label="Available bonds" style={{ tableLayout: "fixed" }}>
<TableHead>
<TableRow>
<TableCell style={{ width: "180px", padding: "8px 0" }}>Bond</TableCell>
<TableCell style={{ padding: "8px 0" }}>Duration</TableCell>
<TableCell style={{ padding: "8px 0" }}>Remaining</TableCell>
<TableCell style={{ padding: "8px 0" }}>Payout</TableCell>
</TableRow>
</TableHead>
<TableBody>
{notes.map((note, index) => (
<TableRow key={index}>
<TableCell style={{ padding: "8px 0" }}>
<Box display="flex" alignItems="center">
<TokenStack tokens={note.quoteToken.icons} />
<Box display="flex" flexDirection="column" ml="16px">
<Typography>{note.quoteToken.name}</Typography>
</Box>
</Box>
</TableCell>
<TableCell style={{ padding: "8px 0" }}>
<Typography>
<BondVesting vesting={note.vesting} />
</Typography>
</TableCell>
<TableCell style={{ padding: "8px 0" }}>
<Typography>
<BondDuration msg="Matured" secondsTo={secondsTo} duration={note.matured} />
</Typography>
</TableCell>
<TableCell style={{ padding: "8px 0" }}>
<Box display="flex" flexDirection="column" alignItems="flex-start">
<Typography>
{isPayoutGhst
? formatCurrency(note.payout, 5, ghstSymbol)
: formatCurrency(currentIndex.mul(note.payout), 5, stnkSymbol)
}
</Typography>
<Typography variant="body2">{formatCurrency(note.payout * ghstPrice, 2)}</Typography>
</Box>
</TableCell>
<TableCell style={{ padding: "8px 0" }}>
<TertiaryButton
fullWidth
disabled={isPending || secondsTo < note.matured}
onClick={() => onSubmit([note.id])}
>
Claim
</TertiaryButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
</Box>
</Paper>
</>
);
};