ghost-dao-interface/src/containers/Bond/components/BondInputArea.jsx
Uncle Fatso 5dffd62c5a
replace hardcoded token symbols with on-chain data hook
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-06-30 19:49:07 +03:00

230 lines
11 KiB
JavaScript

import { CheckBoxOutlineBlank, CheckBoxOutlined } from "@mui/icons-material";
import { Box, Checkbox, FormControlLabel } from "@mui/material";
import { useState, useMemo } from "react";
import { useLocation } from "react-router-dom";
import { BOND_DEPOSITORY_ADDRESSES } from "../../../constants/addresses";
import { shorten, formatNumber, formatCurrency } from "../../../helpers";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
import BondDiscount from "./BondDiscount";
import BondVesting from "./BondVesting";
import BondConfirmModal from "./BondConfirmModal";
import { TokenAllowanceGuard } from "../../../components/TokenAllowanceGuard/TokenAllowanceGuard";
import { PrimaryButton } from "../../../components/Button";
import SwapCard from "../../../components/Swap/SwapCard";
import SwapCollection from "../../../components/Swap/SwapCollection";
import TokenStack from "../../../components/TokenStack/TokenStack";
import DataRow from "../../../components/DataRow/DataRow";
import Paper from "../../../components/Paper/Paper";
import { useCurrentIndex } from "../../../hooks/staking";
import { useBalance } from "../../../hooks/tokens";
const BondInputArea = ({
bond,
chainId,
slippage,
recipientAddress,
formatDecimals,
handleSettingsOpen,
address,
connect
}) => {
const { pathname } = useLocation();
const { currentIndex } = useCurrentIndex(chainId);
const { balance } = useBalance(chainId, bond.quoteToken.quoteTokenAddress, address);
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
const [amount, setAmount] = useState("");
const [checked, setChecked] = useState(false);
const [confirmOpen, setConfirmOpen] = useState(false);
const parsedAmount = useMemo(() => {
return new DecimalBigNumber(amount, bond.quoteToken.decimals);
}, [bond, amount])
const amountInBaseToken = useMemo(() => {
if (bond.price.inBaseToken._value !== 0n) return parsedAmount.div(bond.price.inBaseToken);
return new DecimalBigNumber(0n, 0);
}, [parsedAmount]);
const showDisclaimer = useMemo(() => {
return new DecimalBigNumber("0").gt(bond.discount);
}, [bond]);
const handleConfirmClose = () => {
setAmount("");
setConfirmOpen(false);
}
const setMax = () => {
if (!balance) return;
if (bond.capacity.inQuoteToken.lt(bond.maxPayout.inQuoteToken)) {
return setAmount(
bond.capacity.inQuoteToken.lt(balance)
? bond.capacity.inQuoteToken.toString() // Capacity is the smallest
: balance.toString(),
);
}
setAmount(
bond.maxPayout.inQuoteToken.lt(balance)
? bond.maxPayout.inQuoteToken.toString() // Payout is the smallest
: balance.toString(),
);
};
const baseTokenString = (bond.maxPayout.inBaseToken.lt(bond.capacity.inBaseToken)
? bond.maxPayout.inBaseToken
: bond.capacity.inBaseToken
);
return (
<Box minHeight="calc(100vh - 210px)" display="flex" flexDirection="column" justifyContent="center">
<Box display="flex" flexDirection="row" width="100%" justifyContent="center" mt="10px">
<Box display="flex" flexDirection="column" width="100%" maxWidth="476px">
<Box mb="21px">
<SwapCollection
UpperSwapCard={
<SwapCard
maxWidth="476px"
inputWidth="280px"
id="from"
token={<TokenStack tokens={bond.quoteToken.icons} sx={{ fontSize: "21px" }} />}
tokenName={bond.quoteToken.name}
info={formatCurrency(balance, formatDecimals, bond.quoteToken.name)}
endString="Max"
endStringOnClick={setMax}
value={amount}
onChange={event => setAmount(event.currentTarget.value)}
inputProps={{ "data-testid": "fromInput" }}
/>
}
LowerSwapCard={
<SwapCard
maxWidth="476px"
inputWidth="280px"
id="to"
token={<TokenStack tokens={bond.baseToken.icons} sx={{ fontSize: "21px" }} />}
tokenName={bond.baseToken.name}
value={amountInBaseToken.toString({ decimals: 9 })}
inputProps={{ "data-testid": "toInput" }}
/>
}
/>
</Box>
<TokenAllowanceGuard
spendAmount={parsedAmount}
tokenName={bond.quoteToken.quoteTokenAddress}
owner={address}
spender={BOND_DEPOSITORY_ADDRESSES[chainId]}
decimals={parsedAmount._decimals}
approvalText={`Approve ${bond.quoteToken.name} to Bond`}
approvalPendingText={"Approving..."}
connect={connect}
width="100%"
height="60px"
isVertical
message={
<>
First time bonding <b>{bond.quoteToken.name}</b>? <br /> Please approve ghostDAO to use your{" "}
<b>{bond.quoteToken.name}</b> for bonding.
</>
}
>
{showDisclaimer && (
<FormControlLabel
control={
<Checkbox
checked={checked}
onChange={event => setChecked(event.target.checked)}
icon={<CheckBoxOutlineBlank viewBox="0 0 24 24" />}
checkedIcon={<CheckBoxOutlined viewBox="0 0 24 24" />}
/>
}
label="I understand that I'm buying a negative discount bond"
/>
)}
<PrimaryButton
fullWidth
disabled={bond.isSoldOut || (showDisclaimer && !checked)}
onClick={() => setConfirmOpen(true)}
>
Bond
</PrimaryButton>
</TokenAllowanceGuard>
<Paper style={{ marginBottom: "7px", marginTop: "21px" }} fullWidth enableBackground>
<Box mt="24px">
<DataRow
title={"You Will Get"}
balance={
<span>
{formatCurrency(amountInBaseToken, formatDecimals, ftsoSymbol)}
{" "}
{!!currentIndex && (
<span>
({formatCurrency(amountInBaseToken.div(currentIndex), formatDecimals, ghstSymbol)})
</span>
)}
</span>
}
tooltip={`The total amount of payout asset you will receive from this bond purchase`}
/>
<DataRow
title="Max You Can Buy"
tooltip={`The maximum quantity of payout token offered via bonds at this moment in time`}
balance={
<span>
{bond.baseToken.tokenAddress.toUpperCase() === bond.quoteToken.quoteTokenAddress.toUpperCase()
? `${formatCurrency(baseTokenString, formatDecimals, ftsoSymbol)}`
: `${formatCurrency(baseTokenString, formatDecimals, ftsoSymbol)} (≈${formatCurrency(baseTokenString.div(currentIndex), formatDecimals, ghstSymbol)})`}
</span>
}
/>
<DataRow
title="Discount"
balance={<BondDiscount discount={bond.discount} textOnly />}
tooltip={`The bond discount is the percentage difference between ${ftsoSymbol} market value and the bond's price`}
/>
<DataRow
title={`Vesting Term`}
balance={<BondVesting vesting={bond.vesting} />}
tooltip={"The duration of the Bond whereby the bond can be claimed in its entirety"}
/>
{recipientAddress !== address && (
<DataRow title={`Recipient`} balance={shorten(recipientAddress)} />
)}
</Box>
</Paper>
</Box>
</Box>
<BondConfirmModal
chainId={chainId}
bond={bond}
slippage={slippage}
recipientAddress={recipientAddress}
spendAmountValue={parsedAmount}
spendAmount={formatNumber(parsedAmount, formatDecimals)}
receiveAmount={formatNumber(amountInBaseToken, formatDecimals)}
handleSettingsOpen={handleSettingsOpen}
isOpen={confirmOpen}
handleConfirmClose={() => handleConfirmClose()}
/>
</Box>
);
};
export default BondInputArea;