import { useTranslation } from "@plexswap/localization"; import { getBalanceNumber } from "@plexswap/utils/formatBalance"; import BigNumber from "bignumber.js"; import { useEffect, useMemo, useRef } from "react"; import { styled } from "styled-components"; import { useTooltip } from "../../hooks/useTooltip"; import Modal from "../../widgets/Modal/Modal"; import { BalanceInput } from "../BalanceInput"; import { Flex } from "../Box"; import { Button } from "../Button"; import { ButtonMenu, ButtonMenuItem } from "../ButtonMenu"; import { Checkbox } from "../Checkbox"; import { HelpIcon } from "../Svg"; import { Text } from "../Text"; import AnimatedArrow from "./AnimatedArrow"; import RoiCalculatorFooter from "./RoiCalculatorFooter"; import RoiCard from "./RoiCard"; import useRoiCalculatorReducer, { CalculatorMode, DefaultCompoundStrategy, EditingCurrency, } from "./useRoiCalculatorReducer"; const StyledModal = styled(Modal)` & > :nth-child(2) { padding: 0; } ${({ theme }) => theme.mediaQueries.md} { width: 380px; } `; export const ScrollableContainer = styled.div` padding: 24px; max-height: 500px; overflow-x: hidden; overflow-y: auto; ${({ theme }) => theme.mediaQueries.sm} { max-height: none; } `; const FullWidthButtonMenu = styled(ButtonMenu)<{ disabled?: boolean }>` width: 100%; & > button { width: 100%; } opacity: ${({ disabled }) => (disabled ? 0.5 : 1)}; `; export interface RoiCalculatorModalProps { account?: string; pid?: number; earningTokenPrice: number; apr?: number; lpRewardsApr?: number; apy?: number; displayApr?: string; linkLabel: string; linkHref?: string; stakingTokenBalance: BigNumber; stakingTokenDecimals: number; stakingTokenSymbol: string; stakingTokenPrice: number; earningTokenSymbol?: string; multiplier?: string; autoCompoundFrequency?: number; performanceFee?: number; isFarm?: boolean; initialState?: any; initialValue?: string; strategy?: any; header?: React.ReactNode; rewardWayaPerSecond?: boolean; onBack?: () => void; onDismiss?: () => void; wayaCalculatorSlot?: (stakingTokenBalance: string) => React.ReactNode; isLocked?: boolean; stableSwapAddress?: string; stableLpFee?: number; farmWayaPerSecond?: string; totalMultipliers?: string; dualTokenRewardApr?: number; isWayaBooster?: boolean; } const RoiCalculatorModal: React.FC> = ({ account, earningTokenPrice, apr, apy, displayApr, linkLabel, linkHref, stakingTokenBalance, stakingTokenSymbol, stakingTokenPrice, multiplier, initialValue, earningTokenSymbol = "WAYA", autoCompoundFrequency = 0, performanceFee = 0, isFarm = false, initialState, strategy, header, children, stakingTokenDecimals, rewardWayaPerSecond, onBack, onDismiss, wayaCalculatorSlot, isLocked = false, stableSwapAddress, stableLpFee, farmWayaPerSecond, totalMultipliers, dualTokenRewardApr, lpRewardsApr, isWayaBooster, }) => { const { t } = useTranslation(); const balanceInputRef = useRef(null); const { state, setPrincipalFromUSDValue, setPrincipalFromTokenValue, setStakingDuration, toggleCompounding, toggleEditingCurrency, setCompoundingFrequency, setCalculatorMode, setTargetRoi, dispatch, } = useRoiCalculatorReducer({ stakingTokenPrice, earningTokenPrice, autoCompoundFrequency }, initialState); const { compounding, activeCompoundingIndex, stakingDuration, editingCurrency } = state.controls; const { principalAsUSD, principalAsToken } = state.data; // Auto-focus input on opening modal useEffect(() => { if (balanceInputRef.current) { balanceInputRef.current.focus(); } }, []); // If user comes to calculator from staking modal - initialize with whatever they put in there useEffect(() => { if (initialValue) { setPrincipalFromTokenValue(initialValue); } }, [initialValue, setPrincipalFromTokenValue]); const { targetRef, tooltip, tooltipVisible } = useTooltip( isFarm ? t("“My Balance” here includes both LP Tokens in your wallet, and LP Tokens already staked in this farm.") : t( "“My Balance” here includes both %assetSymbol% in your wallet, and %assetSymbol% already staked in this pool.", { assetSymbol: stakingTokenSymbol } ), { placement: "top-end", tooltipOffset: [20, 10] } ); const onBalanceFocus = () => { setCalculatorMode(CalculatorMode.ROI_BASED_ON_PRINCIPAL); }; const editingUnit = editingCurrency === EditingCurrency.TOKEN ? stakingTokenSymbol : "USD"; const editingValue = editingCurrency === EditingCurrency.TOKEN ? principalAsToken : principalAsUSD; const conversionUnit = editingCurrency === EditingCurrency.TOKEN ? "USD" : stakingTokenSymbol; const conversionValue = editingCurrency === EditingCurrency.TOKEN ? principalAsUSD : principalAsToken; const onUserInput = editingCurrency === EditingCurrency.TOKEN ? setPrincipalFromTokenValue : setPrincipalFromUSDValue; const DURATION = useMemo(() => [t("1D"), t("7D"), t("30D"), t("1Y"), t("5Y")], [t]); const isDisableMyBalance = useMemo(() => { return ( !Number.isFinite(stakingTokenPrice) || !stakingTokenBalance.isFinite() || stakingTokenBalance.lte(0) || !account ); }, [account, stakingTokenBalance, stakingTokenPrice]); return ( {strategy ? ( strategy(state, dispatch) ) : ( )} {header} {t("%asset% staked", { asset: stakingTokenSymbol })} {tooltipVisible && tooltip} {children || ( <> {t("Staked for")} {DURATION.map((duration) => ( {duration} ))} )} {wayaCalculatorSlot && wayaCalculatorSlot(principalAsToken)} {autoCompoundFrequency === 0 && ( <> {t("Compounding every")} {t("1D")} {t("7D")} {t("14D")} {t("30D")} )} ); }; export default RoiCalculatorModal;