StakeModalContent.tsx
1 import { ChainId, Stake } from '@aave/contract-helpers'; 2 import { normalize, valueToBigNumber } from '@aave/math-utils'; 3 import { Trans } from '@lingui/macro'; 4 import { Typography } from '@mui/material'; 5 import React, { useRef, useState } from 'react'; 6 import { useGeneralStakeUiData } from 'src/hooks/stake/useGeneralStakeUiData'; 7 import { useUserStakeUiData } from 'src/hooks/stake/useUserStakeUiData'; 8 import { useModalContext } from 'src/hooks/useModal'; 9 import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; 10 import { useRootStore } from 'src/store/root'; 11 import { stakeAssetNameFormatted, stakeConfig } from 'src/ui-config/stakeConfig'; 12 import { getNetworkConfig } from 'src/utils/marketsAndNetworksConfig'; 13 import { STAKE } from 'src/utils/mixPanelEvents'; 14 15 import { CooldownWarning } from '../../Warnings/CooldownWarning'; 16 import { AssetInput } from '../AssetInput'; 17 import { TxErrorView } from '../FlowCommons/Error'; 18 import { GasEstimationError } from '../FlowCommons/GasEstimationError'; 19 import { TxSuccessView } from '../FlowCommons/Success'; 20 import { DetailsNumberLine, TxModalDetails } from '../FlowCommons/TxModalDetails'; 21 import { TxModalTitle } from '../FlowCommons/TxModalTitle'; 22 import { ChangeNetworkWarning } from '../Warnings/ChangeNetworkWarning'; 23 import { StakeActions } from './StakeActions'; 24 25 export type StakeProps = { 26 stakeAssetName: Stake; 27 icon: string; 28 }; 29 30 export enum ErrorType { 31 NOT_ENOUGH_BALANCE, 32 } 33 34 export const StakeModalContent = ({ stakeAssetName, icon }: StakeProps) => { 35 const { chainId: connectedChainId, readOnlyModeAddress } = useWeb3Context(); 36 const { gasLimit, mainTxState: txState, txError } = useModalContext(); 37 const currentMarketData = useRootStore((store) => store.currentMarketData); 38 const currentNetworkConfig = useRootStore((store) => store.currentNetworkConfig); 39 const currentChainId = useRootStore((store) => store.currentChainId); 40 41 const { data: stakeUserResult } = useUserStakeUiData(currentMarketData, stakeAssetName); 42 const { data: stakeGeneralResult } = useGeneralStakeUiData(currentMarketData, stakeAssetName); 43 44 const stakeData = stakeGeneralResult?.[0]; 45 const stakeUserData = stakeUserResult?.[0]; 46 47 // states 48 const [_amount, setAmount] = useState(''); 49 const amountRef = useRef<string>(); 50 51 const walletBalance = normalize(stakeUserData?.underlyingTokenUserBalance || '0', 18); 52 53 const isMaxSelected = _amount === '-1'; 54 const amount = isMaxSelected ? walletBalance : _amount; 55 56 const handleChange = (value: string) => { 57 const maxSelected = value === '-1'; 58 amountRef.current = maxSelected ? walletBalance : value; 59 setAmount(value); 60 }; 61 62 // staking token usd value 63 const amountInUsd = Number(amount) * Number(stakeData?.stakeTokenPriceUSDFormatted); 64 65 // error handler 66 let blockingError: ErrorType | undefined = undefined; 67 if (valueToBigNumber(amount).gt(walletBalance)) { 68 blockingError = ErrorType.NOT_ENOUGH_BALANCE; 69 } 70 71 const handleBlocked = () => { 72 switch (blockingError) { 73 case ErrorType.NOT_ENOUGH_BALANCE: 74 return <Trans>Not enough balance on your wallet</Trans>; 75 default: 76 return null; 77 } 78 }; 79 80 const nameFormatted = stakeAssetNameFormatted(stakeAssetName); 81 82 // is Network mismatched 83 const stakingChain = 84 currentNetworkConfig.isFork && currentNetworkConfig.underlyingChainId === stakeConfig.chainId 85 ? currentChainId 86 : stakeConfig.chainId; 87 const isWrongNetwork = connectedChainId !== stakingChain; 88 89 const networkConfig = getNetworkConfig(stakingChain); 90 91 if (txError && txError.blocking) { 92 return <TxErrorView txError={txError} />; 93 } 94 if (txState.success) 95 return ( 96 <TxSuccessView 97 action={<Trans>Staked</Trans>} 98 amount={amountRef.current} 99 symbol={nameFormatted} 100 /> 101 ); 102 103 return ( 104 <> 105 <TxModalTitle title="Stake" symbol={nameFormatted} /> 106 {isWrongNetwork && !readOnlyModeAddress && ( 107 <ChangeNetworkWarning 108 networkName={networkConfig.name} 109 chainId={stakingChain} 110 funnel={'Stake Modal'} 111 /> 112 )} 113 114 <CooldownWarning /> 115 116 <AssetInput 117 value={amount} 118 onChange={handleChange} 119 usdValue={amountInUsd.toString()} 120 symbol={nameFormatted} 121 assets={[ 122 { 123 balance: walletBalance.toString(), 124 symbol: icon, 125 }, 126 ]} 127 isMaxSelected={isMaxSelected} 128 maxValue={walletBalance.toString()} 129 balanceText={<Trans>Wallet balance</Trans>} 130 /> 131 {blockingError !== undefined && ( 132 <Typography variant="helperText" color="red"> 133 {handleBlocked()} 134 </Typography> 135 )} 136 <TxModalDetails gasLimit={gasLimit} chainId={ChainId.mainnet}> 137 <DetailsNumberLine 138 description={<Trans>Staking APR</Trans>} 139 value={Number(stakeData?.stakeApy || '0') / 10000} 140 percent 141 /> 142 </TxModalDetails> 143 144 {txError && <GasEstimationError txError={txError} />} 145 146 <StakeActions 147 sx={{ mt: '48px' }} 148 amountToStake={amount} 149 isWrongNetwork={isWrongNetwork} 150 symbol={nameFormatted} 151 blocked={blockingError !== undefined} 152 selectedToken={stakeAssetName} 153 event={STAKE.STAKE_TOKEN} 154 /> 155 </> 156 ); 157 };