/ src / modules / migration / MigrationBottomPanel.tsx
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  };