MigrationBottomPanel.tsx
1 import { valueToBigNumber } from '@aave/math-utils'; 2 import { ExclamationIcon } from '@heroicons/react/solid'; 3 import { Trans } from '@lingui/macro'; 4 import { ArrowDownward } from '@mui/icons-material'; 5 import { 6 Box, 7 Button, 8 Checkbox, 9 FormControlLabel, 10 Paper, 11 SvgIcon, 12 Typography, 13 useMediaQuery, 14 useTheme, 15 } from '@mui/material'; 16 import { useState } from 'react'; 17 import { Row } from 'src/components/primitives/Row'; 18 import { Warning } from 'src/components/primitives/Warning'; 19 import { IsolationModeWarning } from 'src/components/transactions/Warnings/IsolationModeWarning'; 20 import { UserSummaryAfterMigration } from 'src/hooks/migration/useUserSummaryAfterMigration'; 21 import { UserSummaryAndIncentives } from 'src/hooks/pool/useUserSummaryAndIncentives'; 22 import { useModalContext } from 'src/hooks/useModal'; 23 import { MarketDataType } from 'src/ui-config/marketsConfig'; 24 25 import { MigrationMarketCard, SelectableMarkets } from './MigrationMarketCard'; 26 27 export interface UserSummaryBeforeMigration { 28 fromUserSummaryBeforeMigration: UserSummaryAndIncentives; 29 toUserSummaryBeforeMigration: UserSummaryAndIncentives; 30 } 31 32 interface MigrationBottomPanelProps { 33 disableButton?: boolean; 34 loading?: boolean; 35 enteringIsolationMode: boolean; 36 fromMarketData: MarketDataType; 37 toMarketData: MarketDataType; 38 userSummaryAfterMigration?: UserSummaryAfterMigration; 39 userSummaryBeforeMigration?: UserSummaryBeforeMigration; 40 setFromMarketData: (marketData: MarketDataType) => void; 41 selectableMarkets: SelectableMarkets; 42 } 43 44 enum ErrorType { 45 NO_SELECTION, 46 V2_HF_TOO_LOW, 47 V3_HF_TOO_LOW, 48 INSUFFICIENT_LTV, 49 } 50 51 interface BlockErrorTextProps { 52 blockingError?: ErrorType | null; 53 } 54 55 const getBlockingError = ( 56 userSummaryAfterMigration: UserSummaryAfterMigration, 57 disableButton: boolean, 58 isChecked: boolean 59 ) => { 60 const { 61 totalCollateralMarketReferenceCurrency, 62 totalBorrowsMarketReferenceCurrency, 63 currentLoanToValue, 64 } = userSummaryAfterMigration.toUserSummaryAfterMigration; 65 66 const maxBorrowAmount = valueToBigNumber(totalCollateralMarketReferenceCurrency).multipliedBy( 67 currentLoanToValue 68 ); 69 70 const insufficientLtv = valueToBigNumber(totalBorrowsMarketReferenceCurrency).isGreaterThan( 71 maxBorrowAmount 72 ); 73 if (disableButton && isChecked) { 74 return ErrorType.NO_SELECTION; 75 } else if ( 76 Number(userSummaryAfterMigration.fromUserSummaryAfterMigration.healthFactor) < 1.005 && 77 userSummaryAfterMigration.fromUserSummaryAfterMigration.healthFactor !== '-1' 78 ) { 79 return ErrorType.V2_HF_TOO_LOW; 80 } else if ( 81 Number(userSummaryAfterMigration.toUserSummaryAfterMigration.healthFactor) < 1.005 && 82 userSummaryAfterMigration.toUserSummaryAfterMigration.healthFactor !== '-1' 83 ) { 84 return ErrorType.V3_HF_TOO_LOW; 85 } else if (insufficientLtv) { 86 return ErrorType.INSUFFICIENT_LTV; 87 } 88 return null; 89 }; 90 91 const BlockErrorText = ({ blockingError }: BlockErrorTextProps) => { 92 switch (blockingError) { 93 case ErrorType.NO_SELECTION: 94 return <Trans>No assets selected to migrate.</Trans>; 95 case ErrorType.V2_HF_TOO_LOW: 96 return ( 97 <Trans> 98 This action will reduce V2 health factor below liquidation threshold. retain collateral or 99 migrate borrow position to continue. 100 </Trans> 101 ); 102 case ErrorType.V3_HF_TOO_LOW: 103 return ( 104 <> 105 <Trans> 106 This action will reduce health factor of V3 below liquidation threshold. Increase 107 migrated collateral or reduce migrated borrow to continue. 108 </Trans> 109 </> 110 ); 111 case ErrorType.INSUFFICIENT_LTV: 112 return ( 113 <Trans> 114 The loan to value of the migrated positions would cause liquidation. Increase migrated 115 collateral or reduce migrated borrow to continue. 116 </Trans> 117 ); 118 default: 119 return <></>; 120 } 121 }; 122 123 export const MigrationBottomPanel = ({ 124 disableButton, 125 enteringIsolationMode, 126 fromMarketData, 127 toMarketData, 128 userSummaryAfterMigration, 129 userSummaryBeforeMigration, 130 setFromMarketData, 131 selectableMarkets, 132 loading, 133 }: MigrationBottomPanelProps) => { 134 const { openV3Migration } = useModalContext(); 135 const [isChecked, setIsChecked] = useState(false); 136 137 const theme = useTheme(); 138 const downToSM = useMediaQuery(theme.breakpoints.down('sm')); 139 140 const blockingError = userSummaryAfterMigration 141 ? getBlockingError(userSummaryAfterMigration, !!disableButton, isChecked) 142 : null; 143 144 return ( 145 <Box 146 sx={{ 147 display: 'flex', 148 flexDirection: 'column', 149 width: { xs: '100%', lg: '40%' }, 150 }} 151 > 152 <Paper 153 sx={{ 154 p: { 155 xs: '16px 24px 24px 24px', 156 }, 157 mb: { xs: 6, md: 0 }, 158 }} 159 > 160 <Row caption={<Trans>Migrate your assets</Trans>} captionVariant="h3" sx={{ mb: 6 }} /> 161 162 <Box 163 sx={{ 164 display: 'flex', 165 flexDirection: 'column', 166 justifyContent: 'center', 167 gap: 2, 168 mb: 12, 169 position: 'relative', 170 alignItems: 'center', 171 }} 172 > 173 <MigrationMarketCard 174 marketData={fromMarketData} 175 userSummaryBeforeMigration={userSummaryBeforeMigration?.fromUserSummaryBeforeMigration} 176 userSummaryAfterMigration={userSummaryAfterMigration?.fromUserSummaryAfterMigration} 177 selectableMarkets={selectableMarkets} 178 setFromMarketData={setFromMarketData} 179 loading={loading} 180 /> 181 <Box 182 border={1} 183 borderColor="divider" 184 bgcolor="background.paper" 185 sx={{ 186 display: 'flex', 187 alignItems: 'center', 188 justifyContent: 'center', 189 position: 'absolute', 190 borderRadius: '12px', 191 width: 36, 192 height: 36, 193 }} 194 > 195 <ArrowDownward /> 196 </Box> 197 <MigrationMarketCard 198 marketData={toMarketData} 199 userSummaryBeforeMigration={userSummaryBeforeMigration?.toUserSummaryBeforeMigration} 200 userSummaryAfterMigration={userSummaryAfterMigration?.toUserSummaryAfterMigration} 201 loading={loading} 202 /> 203 </Box> 204 205 {blockingError !== null && ( 206 <Warning severity="warning"> 207 <BlockErrorText blockingError={blockingError} /> 208 </Warning> 209 )} 210 211 {enteringIsolationMode && <IsolationModeWarning severity="warning" />} 212 213 {blockingError === null && ( 214 <Box 215 sx={{ 216 height: '44px', 217 backgroundColor: 'background.surface', 218 borderRadius: '4px', 219 display: 'flex', 220 justifyContent: 'center', 221 mb: 4, 222 }} 223 data-cy={`migration-risk-checkbox`} 224 > 225 <FormControlLabel 226 sx={{ margin: 0 }} 227 control={ 228 <Checkbox 229 checked={isChecked} 230 onChange={() => setIsChecked(!isChecked)} 231 size="small" 232 /> 233 } 234 label={ 235 <Typography variant="description" sx={{ position: 'relative', top: 1 }}> 236 <Trans>I fully understand the risks of migrating.</Trans> 237 </Typography> 238 } 239 /> 240 </Box> 241 )} 242 243 <Box> 244 <Button 245 onClick={openV3Migration} 246 disabled={loading || !isChecked || blockingError !== null} 247 sx={{ width: '100%', height: '44px' }} 248 variant={!isChecked || blockingError !== null ? 'contained' : 'gradient'} 249 size="medium" 250 data-cy={`migration-button`} 251 > 252 <Trans>Preview tx and migrate</Trans> 253 </Button> 254 </Box> 255 <Box 256 sx={{ 257 p: downToSM ? '20px 16px' : '20px 30px', 258 mt: downToSM ? 4 : 0, 259 }} 260 > 261 <Typography 262 variant="h3" 263 sx={{ fontWeight: 700, mb: { xs: 4, lg: 6 }, display: 'flex', alignItems: 'center' }} 264 > 265 <SvgIcon sx={{ fontSize: '24px', color: 'warning.main', mr: 2 }}> 266 <ExclamationIcon /> 267 </SvgIcon> 268 <Trans>Migration risks</Trans> 269 </Typography> 270 <Typography sx={{ mb: { xs: 3, lg: 4 } }}> 271 <Trans> 272 Please always be aware of your <b>Health Factor (HF)</b> when partially migrating a 273 position and that your rates will be updated to V3 rates. 274 </Trans> 275 </Typography> 276 <Typography sx={{ mb: { xs: 3, lg: 4 } }}> 277 <Trans> 278 Migrating multiple collaterals and borrowed assets at the same time can be an 279 expensive operation and might fail in certain situations. 280 <b> 281 Therefore it’s not recommended to migrate positions with more than 5 assets 282 (deposited + borrowed) at the same time. 283 </b> 284 </Trans> 285 </Typography> 286 <Typography sx={{ mb: { xs: 4, lg: 6 } }}> 287 <Trans>Be mindful of the network congestion and gas prices.</Trans> 288 </Typography> 289 </Box> 290 </Paper> 291 </Box> 292 ); 293 };