/ src / modules / dashboard / lists / BorrowAssetsList / GhoBorrowAssetsListItem.tsx
GhoBorrowAssetsListItem.tsx
  1  import { Trans } from '@lingui/macro';
  2  import { Box, Button, Stack, Tooltip, Typography, useMediaQuery, useTheme } from '@mui/material';
  3  import { CapType } from 'src/components/caps/helper';
  4  import { GhoIncentivesCard } from 'src/components/incentives/GhoIncentivesCard';
  5  import { AvailableTooltip } from 'src/components/infoTooltips/AvailableTooltip';
  6  import { FixedAPYTooltip } from 'src/components/infoTooltips/FixedAPYTooltip';
  7  import { ListColumn } from 'src/components/lists/ListColumn';
  8  import { ListItem } from 'src/components/lists/ListItem';
  9  import { Row } from 'src/components/primitives/Row';
 10  import { TokenIcon } from 'src/components/primitives/TokenIcon';
 11  import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
 12  import { useModalContext } from 'src/hooks/useModal';
 13  import { useRootStore } from 'src/store/root';
 14  import { CustomMarket } from 'src/ui-config/marketsConfig';
 15  import { DASHBOARD_LIST_COLUMN_WIDTHS } from 'src/utils/dashboardSortUtils';
 16  import { getMaxGhoMintAmount } from 'src/utils/getMaxAmountAvailableToBorrow';
 17  import { weightedAverageAPY } from 'src/utils/ghoUtilities';
 18  
 19  import { Link, ROUTES } from '../../../../components/primitives/Link';
 20  import { ListButtonsColumn } from '../ListButtonsColumn';
 21  import { ListMobileItemWrapper } from '../ListMobileItemWrapper';
 22  import { ListValueColumn } from '../ListValueColumn';
 23  import { ListValueRow } from '../ListValueRow';
 24  import { GhoBorrowAssetsItem } from './types';
 25  
 26  export const GhoBorrowAssetsListItem = ({
 27    symbol,
 28    iconSymbol,
 29    name,
 30    underlyingAsset,
 31    isFreezed,
 32    reserve,
 33  }: GhoBorrowAssetsItem) => {
 34    const { openBorrow } = useModalContext();
 35    const { user } = useAppDataContext();
 36    const currentMarket = useRootStore((store) => store.currentMarket);
 37    const { ghoReserveData, ghoUserData, ghoUserLoadingData, ghoLoadingData } = useAppDataContext();
 38    const theme = useTheme();
 39    const downToXSM = useMediaQuery(theme.breakpoints.down('xsm'));
 40  
 41    const maxAmountUserCanMint = user ? Number(getMaxGhoMintAmount(user, reserve)) : 0;
 42    const availableBorrows = Math.min(
 43      maxAmountUserCanMint,
 44      ghoReserveData.aaveFacilitatorRemainingCapacity
 45    );
 46    const borrowButtonDisable = isFreezed || availableBorrows <= 0;
 47    const debtBalanceAfterMaxBorrow = availableBorrows + ghoUserData.userGhoBorrowBalance;
 48  
 49    // Determine borrow APY range
 50    const userCurrentBorrowApy = weightedAverageAPY(
 51      ghoReserveData.ghoVariableBorrowAPY,
 52      ghoUserData.userGhoBorrowBalance,
 53      ghoUserData.userGhoAvailableToBorrowAtDiscount,
 54      ghoReserveData.ghoBorrowAPYWithMaxDiscount
 55    );
 56    const userBorrowApyAfterNewBorrow = weightedAverageAPY(
 57      ghoReserveData.ghoVariableBorrowAPY,
 58      debtBalanceAfterMaxBorrow,
 59      ghoUserData.userGhoAvailableToBorrowAtDiscount,
 60      ghoReserveData.ghoBorrowAPYWithMaxDiscount
 61    );
 62    const ghoApyRange: [number, number] | undefined = !ghoUserLoadingData
 63      ? [
 64          ghoUserData.userGhoAvailableToBorrowAtDiscount === 0
 65            ? ghoReserveData.ghoBorrowAPYWithMaxDiscount
 66            : userCurrentBorrowApy,
 67          userBorrowApyAfterNewBorrow,
 68        ]
 69      : undefined;
 70  
 71    const props: GhoBorrowAssetsListItemProps = {
 72      symbol,
 73      iconSymbol,
 74      name,
 75      underlyingAsset,
 76      currentMarket,
 77      availableBorrows,
 78      borrowButtonDisable,
 79      userDiscountTokenBalance: ghoUserData.userDiscountTokenBalance,
 80      ghoApyRange,
 81      ghoUserDataFetched: !ghoUserLoadingData,
 82      userBorrowApyAfterNewBorrow,
 83      ghoLoadingData,
 84      onBorrowClick: () => openBorrow(underlyingAsset, currentMarket, name, 'dashboard'),
 85    };
 86    if (downToXSM) {
 87      return <GhoBorrowAssetsListItemMobile {...props} />;
 88    } else {
 89      return <GhoBorrowAssetsListItemDesktop {...props} />;
 90    }
 91  };
 92  
 93  interface GhoBorrowAssetsListItemProps {
 94    symbol: string;
 95    iconSymbol: string;
 96    name: string;
 97    underlyingAsset: string;
 98    currentMarket: CustomMarket;
 99    availableBorrows: number;
100    borrowButtonDisable: boolean;
101    userDiscountTokenBalance: number;
102    ghoApyRange: [number, number] | undefined;
103    ghoUserDataFetched: boolean;
104    userBorrowApyAfterNewBorrow: number;
105    ghoLoadingData: boolean;
106    onBorrowClick: () => void;
107  }
108  
109  const GhoBorrowAssetsListItemDesktop = ({
110    symbol,
111    iconSymbol,
112    name,
113    underlyingAsset,
114    currentMarket,
115    availableBorrows,
116    borrowButtonDisable,
117    userDiscountTokenBalance,
118    ghoApyRange,
119    ghoUserDataFetched,
120    userBorrowApyAfterNewBorrow,
121    onBorrowClick,
122  }: GhoBorrowAssetsListItemProps) => {
123    return (
124      <ListItem
125        sx={{ borderTop: '1px solid', borderBottom: '1px solid', borderColor: 'divider', mb: 2 }}
126        data-cy={`dashboardBorrowListItem_${symbol.toUpperCase()}`}
127      >
128        <ListColumn maxWidth={DASHBOARD_LIST_COLUMN_WIDTHS.CELL} isRow>
129          <Link
130            href={ROUTES.reserveOverview(underlyingAsset, currentMarket)}
131            noWrap
132            sx={{ display: 'inline-flex', alignItems: 'center' }}
133          >
134            <TokenIcon symbol={iconSymbol} fontSize="large" />
135            <Tooltip title={`${name} (${symbol})`} arrow placement="top">
136              <Typography variant="subheader1" sx={{ ml: 3 }} noWrap data-cy={`assetName`}>
137                {symbol}
138              </Typography>
139            </Tooltip>
140          </Link>
141        </ListColumn>
142        <ListColumn>
143          <Box display="flex" flexDirection="column">
144            <AvailableTooltip
145              capType={CapType.borrowCap}
146              text={<Trans>Available</Trans>}
147              variant="subheader2"
148              color="text.secondary"
149              ml={-1}
150            />
151            <ListValueColumn
152              listColumnProps={{
153                p: 0,
154              }}
155              symbol={symbol}
156              value={availableBorrows}
157              subValue={availableBorrows}
158              disabled={availableBorrows === 0}
159              withTooltip
160            />
161          </Box>
162        </ListColumn>
163        <ListColumn>
164          <FixedAPYTooltip
165            text={<Trans>APY, borrow rate</Trans>}
166            variant="subheader2"
167            color="text.secondary"
168          />
169          <GhoIncentivesCard
170            withTokenIcon={false}
171            useApyRange
172            rangeValues={ghoApyRange}
173            value={ghoUserDataFetched ? userBorrowApyAfterNewBorrow : -1}
174            data-cy={`apyType`}
175            stkAaveBalance={userDiscountTokenBalance}
176            ghoRoute={ROUTES.reserveOverview(underlyingAsset, currentMarket) + '/#discount'}
177            forceShowTooltip
178            userQualifiesForDiscount
179          />
180        </ListColumn>
181        <ListButtonsColumn>
182          <Button disabled={borrowButtonDisable} variant="contained" onClick={onBorrowClick}>
183            <Trans>Borrow</Trans>
184          </Button>
185          <Button
186            variant="outlined"
187            component={Link}
188            href={ROUTES.reserveOverview(underlyingAsset, currentMarket)}
189          >
190            <Trans>Details</Trans>
191          </Button>
192        </ListButtonsColumn>
193      </ListItem>
194    );
195  };
196  
197  const GhoBorrowAssetsListItemMobile = ({
198    symbol,
199    iconSymbol,
200    name,
201    underlyingAsset,
202    currentMarket,
203    availableBorrows,
204    borrowButtonDisable,
205    userDiscountTokenBalance,
206    ghoApyRange,
207    ghoLoadingData,
208    userBorrowApyAfterNewBorrow,
209    onBorrowClick,
210  }: GhoBorrowAssetsListItemProps) => {
211    return (
212      <ListMobileItemWrapper
213        symbol={symbol}
214        iconSymbol={iconSymbol}
215        name={name}
216        underlyingAsset={underlyingAsset}
217        currentMarket={currentMarket}
218      >
219        <ListValueRow
220          title={<Trans>Available to borrow</Trans>}
221          value={availableBorrows}
222          subValue={availableBorrows}
223          disabled={availableBorrows === 0}
224        />
225  
226        <Row
227          caption={
228            <FixedAPYTooltip
229              text={<Trans>APY, borrow rate</Trans>}
230              key="APY_dash_mob_variable_ type"
231              variant="description"
232            />
233          }
234          align="flex-start"
235          captionVariant="description"
236          mb={2}
237        >
238          <Stack alignItems="end">
239            <GhoIncentivesCard
240              withTokenIcon={false}
241              useApyRange
242              rangeValues={ghoApyRange}
243              value={ghoLoadingData ? -1 : userBorrowApyAfterNewBorrow}
244              data-cy="apyType"
245              stkAaveBalance={userDiscountTokenBalance}
246              ghoRoute={ROUTES.reserveOverview(underlyingAsset, currentMarket) + '/#discount'}
247              forceShowTooltip
248              userQualifiesForDiscount
249            />
250          </Stack>
251        </Row>
252  
253        <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mt: 5 }}>
254          <Button
255            disabled={borrowButtonDisable}
256            variant="contained"
257            onClick={onBorrowClick}
258            sx={{ mr: 1.5 }}
259            fullWidth
260          >
261            <Trans>Borrow</Trans>
262          </Button>
263          <Button
264            variant="outlined"
265            component={Link}
266            href={ROUTES.reserveOverview(underlyingAsset, currentMarket)}
267            fullWidth
268          >
269            <Trans>Details</Trans>
270          </Button>
271        </Box>
272      </ListMobileItemWrapper>
273    );
274  };