ghost-dao-interface/src/containers/Stake/components/StakeInputArea.jsx
2025-07-25 00:34:58 +03:00

334 lines
12 KiB
JavaScript

import { Avatar, Box, Link } from "@mui/material";
import { styled } from "@mui/material/styles";
import React, { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
import { useChainId } from "wagmi";
import TokenModal from "./TokenModal";
import StakeConfirmationModal from "./StakeConfirmationModal";
import StakeSettingsModal from "./StakeSettingsModal";
import SwapCard from "../../../components/Swap/SwapCard";
import SwapCollection from "../../../components/Swap/SwapCollection";
import { InfoNotification } from "../../../components/Notification";
import { PrimaryButton } from "../../../components/Button";
import {
GHST_ADDRESSES,
FTSO_ADDRESSES,
STNK_ADDRESSES,
STAKING_ADDRESSES,
} from "../../../constants/addresses";
import { useCurrentIndex } from "../../../hooks/staking";
import { useBalance, useTokenSymbol } from "../../../hooks/tokens";
import { formatNumber } from "../../../helpers";
const PREFIX = "StakeInputArea";
const classes = {
inputRow: `${PREFIX}-inputRow`,
gridItem: `${PREFIX}-gridItem`,
input: `${PREFIX}-input`,
button: `${PREFIX}-button`,
};
const StyledBox = styled(Box)(({ theme }) => ({
[`& .${classes.inputRow}`]: {
justifyContent: "space-around",
alignItems: "center",
height: "auto",
marginTop: "4px",
},
[`& .${classes.gridItem}`]: {
width: "100%",
paddingRight: "5px",
alignItems: "center",
justifyContent: "center",
},
[`& .${classes.input}`]: {
[theme.breakpoints.down("md")]: {
marginBottom: "10px",
},
[theme.breakpoints.up("sm")]: {
marginBottom: "0",
},
},
[`& .${classes.button}`]: {
width: "100%",
minWidth: "163px",
maxWidth: "542px",
},
}));
export const StakeInputArea = ({
upperToken,
setUpperToken,
bottomToken,
setBottomToken,
action,
chainId,
address,
connect,
settingsModalOpen,
closeSettingModal
}) => {
const [upperTokenModalOpen, setUpperTokenModalOpen] = useState(false);
const [bottomTokenModalOpen, setBottomTokenModalOpen] = useState(false);
const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
const [formatDecimals, setFormatDecimals] = useState(localStorage.getItem("stake-decimals")
? localStorage.getItem("stake-decimals")
: "5"
);
const [isClaim, setIsClaim] = useState(localStorage.getItem("stake-isClaim")
? localStorage.getItem("stake-isClaim") === 'true'
: true
);
const [isTrigger, setIsTrigger] = useState(localStorage.getItem("stake-isTrigger")
? localStorage.getItem("stake-isTrigger") === 'true'
: true
);
const [amount, setAmount] = useState("");
const [receiveAmount, setReceiveAmount] = useState("");
const [exceedsAmount, setExceedsAmount] = useState(false)
const { currentIndex, refetch: refetchCurrentIndex } = useCurrentIndex(chainId);
const { balance: ftsoBalance, refetch: ftsoRefetch } = useBalance(chainId, "FTSO", address);
const { balance: stnkBalance, refetch: stnkRefetch } = useBalance(chainId, "STNK", address);
const { balance: ghstBalance, refetch: ghstRefetch } = useBalance(chainId, "GHST", address);
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK");
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
const setIsClaimInner = (value) => {
setIsClaim(value);
localStorage.setItem("stake-isClaim", value);
}
const setIsTriggerInner = (value) => {
setIsTrigger(value);
localStorage.setItem("stake-isTrigger", value);
}
const setFormatDecimalsInner = (value) => {
if (Number(value) <= 17) {
setFormatDecimals(value);
localStorage.setItem("staking-decimals", value);
}
}
useEffect(() => {
const innerBalance = upperToken === "GHST" ?
ghstBalance
:
upperToken === "STNK" ?
stnkBalance
:
ftsoBalance;
if (
(upperToken === "FTSO" && bottomToken === "STNK") ||
(upperToken === "STNK" && bottomToken === "FTSO")
) {
setReceiveAmount(amount);
setExceedsAmount(new DecimalBigNumber(amount, 9).gt(innerBalance))
} else {
const decimals = upperToken === "GHST" ? 18 : 9;
const raw = new DecimalBigNumber(amount, decimals);
const amountIn = new DecimalBigNumber(raw._value.toBigInt(), decimals);
const result = upperToken === "GHST" ?
currentIndex.mul(amountIn).toString()
:
currentIndex._value > 0n ? amountIn.div(currentIndex).toString() : "0";
const innerBalance = upperToken === "GHST" ?
ghstBalance
:
upperToken === "STNK" ?
stnkBalance
:
ftsoBalance;
setReceiveAmount(result);
setExceedsAmount(amountIn.gt(innerBalance));
}
}, [upperToken, bottomToken, amount, currentIndex]);
const handleTokenModalInput = (value, isUpper) => {
if (isUpper) {
if (value === bottomToken) {
const newValue = value === "GHST" ? "STNK" : "GHST";
setBottomToken(newValue)
}
setUpperToken(value);
} else {
setBottomToken(value);
}
}
const onSwapClick = () => {
const tmpUpper = upperToken;
const tmpBottom = bottomToken;
setUpperToken(tmpBottom);
setBottomToken(tmpUpper);
}
const SwapCardTemplate = (tokenName, tokenAmount, isUpper, handleModal) => {
const balance = tokenName === "STNK" || tokenName === "sCSPR" ?
stnkBalance : tokenName === "FTSO" || tokenName === "eCSPR" ?
ftsoBalance : ghstBalance;
let realTokenName = "";
switch (tokenName.toUpperCase()) {
case "FTSO":
realTokenName = ftsoSymbol;
break;
case "ECSPR":
realTokenName = ftsoSymbol;
break;
case "STNK":
realTokenName = stnkSymbol;
break;
case "SCSPR":
realTokenName = stnkSymbol;
break;
case "GHST":
realTokenName = ghstSymbol;
break;
case "CSPR":
realTokenName = ghstSymbol;
break;
}
return (
<SwapCard
id={`${tokenName.toLowerCase()}-input`}
token={tokenName}
tokenName={realTokenName}
tokenOnClick={() => handleModal(true)}
inputProps={{ "data-testid": `${tokenName.toLowerCase()}-input`, min: "0" }}
value={tokenAmount}
onChange={event => setAmount(event.target.value)}
info={`Balance: ${formatNumber(balance, formatDecimals)} ${tokenName}`}
endString={isUpper ? "Max" : ""}
endStringOnClick={() => isUpper && setAmount(balance.toString())}
disabled={!isUpper}
inputWidth={`${tokenAmount.length > 0 ? tokenAmount.length : 1}ch`}
/>
)
}
const closeAndUpdate = () => {
setAmount("");
setConfirmationModalOpen(false);
refetchCurrentIndex();
ftsoRefetch();
stnkRefetch();
ghstRefetch();
}
return (
<>
<StyledBox mb={3}>
<Box display="flex" flexDirection="row" width="100%" justifyContent="center" mt="24px">
<Box display="flex" flexDirection="column" width="100%" maxWidth="476px">
<Box mb="21px">
<SwapCollection
UpperSwapCard={SwapCardTemplate(upperToken, amount, true, setUpperTokenModalOpen)}
LowerSwapCard={SwapCardTemplate(bottomToken, receiveAmount, false, setBottomTokenModalOpen)}
arrowOnClick={onSwapClick}
/>
</Box>
{upperTokenModalOpen && (
<TokenModal
open={upperTokenModalOpen}
handleSelect={data => handleTokenModalInput(data.token, data.isUpper)}
handleClose={() => setUpperTokenModalOpen(false)}
ftsoBalance={formatNumber(ftsoBalance, formatDecimals)}
stnkBalance={formatNumber(stnkBalance, formatDecimals)}
ghstBalance={formatNumber(ghstBalance, formatDecimals)}
isUpper={true}
ftsoSymbol={ftsoSymbol}
stnkSymbol={stnkSymbol}
ghstSymbol={ghstSymbol}
/>
)}
{bottomTokenModalOpen && (
<TokenModal
open={bottomTokenModalOpen}
handleSelect={data => handleTokenModalInput(data.token, data.isUpper)}
handleClose={() => setBottomTokenModalOpen(false)}
ftsoBalance={formatNumber(ftsoBalance, formatDecimals)}
stnkBalance={formatNumber(stnkBalance, formatDecimals)}
ghstBalance={formatNumber(ghstBalance, formatDecimals)}
tokenToExclude={upperToken}
isUpper={false}
ftsoSymbol={ftsoSymbol}
stnkSymbol={stnkSymbol}
ghstSymbol={ghstSymbol}
/>
)}
<Box>
<PrimaryButton
fullWidth
onClick={() => setConfirmationModalOpen(true)}
loading={false}
disabled={exceedsAmount}
>
{exceedsAmount ?
"Exceeds amount"
:
action[0] + String(action.toLowerCase()).slice(1)
}
</PrimaryButton>
</Box>
</Box>
</Box>
</StyledBox>
<StakeSettingsModal
open={settingsModalOpen}
onClose={closeSettingModal}
formatDecimals={formatDecimals}
onFormatDecimalsChange={event => setFormatDecimalsInner(event.currentTarget.value)}
isClaim={isClaim}
onIsClaimChanged={setIsClaimInner}
isTrigger={isTrigger}
onTriggerRebaseChange={setIsTriggerInner}
/>
<StakeConfirmationModal
open={confirmationModalOpen}
onClose={() => closeAndUpdate()}
chainId={chainId}
address={address}
action={action}
upperToken={upperToken}
bottomToken={bottomToken}
amount={amount}
receiveAmount={receiveAmount}
amountExceedsBalance={exceedsAmount}
connect={connect}
spendDecimals={upperToken === "GHST" ? 18 : 9}
receiveDecimals={bottomToken === "GHST" ? 18 : 9}
isClaim={isClaim}
isTrigger={isTrigger}
ftsoSymbol={ftsoSymbol}
stnkSymbol={stnkSymbol}
ghstSymbol={ghstSymbol}
/>
</>
);
};