From 2d146449cace57040ce1c47a855f8b6ac0446379 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Sat, 26 Aug 2023 10:30:56 +0200 Subject: [PATCH] Mp 3260 finalize account summary (#395) --- src/components/Account/AccountComposition.tsx | 72 ++++++----- src/components/Account/AccountDetails.tsx | 44 ++----- src/components/Account/AccountList.tsx | 13 +- src/components/Account/AccountMenuContent.tsx | 4 +- src/components/Account/AccountStats.tsx | 11 +- src/components/Account/AccountSummary.tsx | 8 +- .../Account/CurrentAccountSummary.tsx | 8 +- .../Modals/AssetAmountSelectActionModal.tsx | 8 +- src/components/Modals/BorrowModal.tsx | 65 ++++++---- .../Modals/FundWithdraw/FundAccount.tsx | 24 ++-- .../FundAndWithdrawModalContent.tsx | 11 +- .../FundWithdraw/WithdrawFromAccount.tsx | 37 ++---- .../Modals/LendAndReclaim/index.tsx | 43 ++----- .../Modals/Vault/VaultBorrowings.tsx | 7 +- .../Modals/Vault/VaultModalContent.tsx | 23 ++-- .../Trade/TradeModule/SwapForm/index.tsx | 10 +- src/hooks/useHealthComputer.tsx | 22 ++-- src/hooks/useUpdatedAccount/functions.ts | 33 ++++- src/hooks/useUpdatedAccount/index.ts | 116 +++++++++++++++--- src/pages/_layout.tsx | 3 + src/store/slices/broadcast.ts | 26 ++-- src/types/interfaces/store/broadcast.d.ts | 1 + src/utils/accounts.ts | 20 +-- tailwind.config.js | 1 + 24 files changed, 340 insertions(+), 270 deletions(-) diff --git a/src/components/Account/AccountComposition.tsx b/src/components/Account/AccountComposition.tsx index e962ed54..8d4ba05d 100644 --- a/src/components/Account/AccountComposition.tsx +++ b/src/components/Account/AccountComposition.tsx @@ -11,6 +11,7 @@ import { ORACLE_DENOM } from 'constants/oracle' import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData' import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData' import usePrices from 'hooks/usePrices' +import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' import { calculateAccountApr, @@ -20,7 +21,6 @@ import { interface Props { account: Account - change?: AccountChange } interface ItemProps { @@ -33,7 +33,9 @@ interface ItemProps { } export default function AccountComposition(props: Props) { - const { account, change } = props + const updatedAccount = useStore((s) => s.updatedAccount) + const { account } = props + const hasChanged = !!updatedAccount const { data: prices } = usePrices() const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } = useBorrowMarketAssetsTableData() @@ -48,64 +50,74 @@ export default function AccountComposition(props: Props) { [lendingAvailableAssets, accountLentAssets], ) - const [depositsBalance, lendsBalance, debtsBalance] = useMemo( + const [depositsBalance, lendsBalance, debtsBalance, vaultsBalance] = useMemo( () => getAccountPositionValues(account, prices), [account, prices], ) - const [depositsBalanceChange, lendsBalanceChange, debtsBalanceChange] = useMemo( - () => (change ? getAccountPositionValues(change, prices) : [BN_ZERO, BN_ZERO, BN_ZERO]), - [change, prices], - ) + const positionValue = depositsBalance.plus(lendsBalance).plus(vaultsBalance) + + const [updatedPositionValue, updatedDebtsBalance] = useMemo(() => { + const [updatedDepositsBalance, updatedLendsBalance, updatedDebtsBalance, updatedVaultsBalance] = + updatedAccount + ? getAccountPositionValues(updatedAccount, prices) + : [BN_ZERO, BN_ZERO, BN_ZERO] + + const updatedPositionValue = updatedDepositsBalance + .plus(updatedLendsBalance) + .plus(updatedVaultsBalance) + + return [updatedPositionValue, updatedDebtsBalance] + }, [updatedAccount, prices]) + const totalBalance = useMemo( () => calculateAccountBalanceValue(account, prices), [account, prices], ) - const totalBalanceChange = useMemo( - () => (change ? calculateAccountBalanceValue(change, prices) : BN_ZERO), - [change, prices], + const updatedTotalBalance = useMemo( + () => (updatedAccount ? calculateAccountBalanceValue(updatedAccount, prices) : BN_ZERO), + [updatedAccount, prices], ) - const balance = depositsBalance.plus(lendsBalance) - const balanceChange = depositsBalanceChange.plus(lendsBalanceChange) + const apr = useMemo( - () => calculateAccountApr(account, totalBalance, borrowAssetsData, lendingAssetsData, prices), - [account, totalBalance, borrowAssetsData, lendingAssetsData, prices], + () => calculateAccountApr(account, borrowAssetsData, lendingAssetsData, prices), + [account, borrowAssetsData, lendingAssetsData, prices], ) - const aprChange = useMemo( + const updatedApr = useMemo( () => - change - ? calculateAccountApr( - change, - totalBalance.plus(totalBalanceChange), - borrowAssetsData, - lendingAssetsData, - prices, - ) + updatedAccount + ? calculateAccountApr(updatedAccount, borrowAssetsData, lendingAssetsData, prices) : BN_ZERO, - [change, totalBalance, totalBalanceChange, borrowAssetsData, lendingAssetsData, prices], + [updatedAccount, borrowAssetsData, lendingAssetsData, prices], ) return (
- +
) } @@ -135,7 +147,7 @@ function Item(props: ItemProps) { className='text-sm' /> )} - {!current.isEqualTo(change) && ( + {current.toFixed(2) !== change.toFixed(2) && ( <> diff --git a/src/components/Account/AccountDetails.tsx b/src/components/Account/AccountDetails.tsx index de5b432e..ef40253f 100644 --- a/src/components/Account/AccountDetails.tsx +++ b/src/components/Account/AccountDetails.tsx @@ -7,7 +7,7 @@ import Card from 'components/Card' import DisplayCurrency from 'components/DisplayCurrency' import { FormattedNumber } from 'components/FormattedNumber' import { Gauge } from 'components/Gauge' -import { ArrowRight, ChevronLeft, ChevronRight, Heart } from 'components/Icons' +import { ChevronLeft, ChevronRight, Heart } from 'components/Icons' import Text from 'components/Text' import { ORACLE_DENOM } from 'constants/oracle' import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData' @@ -43,7 +43,6 @@ function AccountDetails(props: Props) { const updatedAccount = useStore((s) => s.updatedAccount) const [isExpanded, setIsExpanded] = useToggle() const { health } = useHealthComputer(account) - const { health: updatedHealth } = useHealthComputer(updatedAccount) const { data: prices } = usePrices() const accountBalanceValue = useMemo( () => calculateAccountBalanceValue(updatedAccount ? updatedAccount : account, prices), @@ -67,15 +66,8 @@ function AccountDetails(props: Props) { [lendingAvailableAssets, accountLentAssets], ) const apr = useMemo( - () => - calculateAccountApr( - account, - accountBalanceValue, - borrowAssetsData, - lendingAssetsData, - prices, - ), - [account, accountBalanceValue, borrowAssetsData, lendingAssetsData, prices], + () => calculateAccountApr(account, borrowAssetsData, lendingAssetsData, prices), + [account, borrowAssetsData, lendingAssetsData, prices], ) return (
- {updatedHealth > 0 && health !== updatedHealth && ( - <> - updatedHealth ? 'text-loss' : 'text-success')} - /> - - - )}
Leverage - - - +
@@ -163,7 +139,7 @@ function AccountDetails(props: Props) { />
-
+
Balances diff --git a/src/components/Account/AccountList.tsx b/src/components/Account/AccountList.tsx index dadf37f6..9c3182a6 100644 --- a/src/components/Account/AccountList.tsx +++ b/src/components/Account/AccountList.tsx @@ -18,6 +18,7 @@ import { getPage, getRoute } from 'utils/route' interface Props { accounts: Account[] + setShowMenu: (show: boolean) => void } const accountCardHeaderClasses = classNames( @@ -27,6 +28,7 @@ const accountCardHeaderClasses = classNames( ) export default function AccountList(props: Props) { + const { accounts, setShowMenu } = props const navigate = useNavigate() const { pathname } = useLocation() const { address } = useParams() @@ -47,11 +49,11 @@ export default function AccountList(props: Props) { } }, [accountId]) - if (!props.accounts?.length) return null + if (!accounts?.length) return null return (
- {props.accounts.map((account) => { + {accounts.map((account) => { const positionBalance = calculateAccountBalanceValue(account, prices) const isActive = accountId === account.id @@ -88,6 +90,7 @@ export default function AccountList(props: Props) { color='tertiary' leftIcon={} onClick={() => { + setShowMenu(false) if (positionBalance.isLessThanOrEqualTo(0)) { useStore.setState({ focusComponent: { @@ -108,6 +111,7 @@ export default function AccountList(props: Props) { leftIcon={} text='Withdraw' onClick={() => { + setShowMenu(false) useStore.setState({ fundAndWithdrawModal: 'withdraw' }) }} disabled={positionBalance.isLessThanOrEqualTo(0)} @@ -117,7 +121,10 @@ export default function AccountList(props: Props) { color='tertiary' leftIcon={} text='Delete' - onClick={() => deleteAccountHandler()} + onClick={() => { + setShowMenu(false) + deleteAccountHandler() + }} />
)} - {hasCreditAccounts && !isLoadingAccount && } + {hasCreditAccounts && !isLoadingAccount && ( + + )}
diff --git a/src/components/Account/AccountStats.tsx b/src/components/Account/AccountStats.tsx index 73e125fb..f0ffd797 100644 --- a/src/components/Account/AccountStats.tsx +++ b/src/components/Account/AccountStats.tsx @@ -36,15 +36,8 @@ export default function AccountStats(props: Props) { [lendingAvailableAssets, accountLentAssets], ) const apr = useMemo( - () => - calculateAccountApr( - props.account, - positionBalance, - borrowAssetsData, - lendingAssetsData, - prices, - ), - [props.account, positionBalance, borrowAssetsData, lendingAssetsData, prices], + () => calculateAccountApr(props.account, borrowAssetsData, lendingAssetsData, prices), + [props.account, borrowAssetsData, lendingAssetsData, prices], ) return (
diff --git a/src/components/Account/AccountSummary.tsx b/src/components/Account/AccountSummary.tsx index f2f52129..959bbe4e 100644 --- a/src/components/Account/AccountSummary.tsx +++ b/src/components/Account/AccountSummary.tsx @@ -19,8 +19,7 @@ import { BNCoin } from 'types/classes/BNCoin' import { calculateAccountBalanceValue, calculateAccountLeverage } from 'utils/accounts' interface Props { - account?: Account - change?: AccountChange + account: Account } export default function AccountSummary(props: Props) { @@ -42,7 +41,6 @@ export default function AccountSummary(props: Props) { () => [...lendingAvailableAssets, ...accountLentAssets], [lendingAvailableAssets, accountLentAssets], ) - const { health } = useHealthComputer(props.account) const leverage = useMemo( () => (props.account ? calculateAccountLeverage(props.account, prices) : BN_ZERO), @@ -76,9 +74,7 @@ export default function AccountSummary(props: Props) { { title: `Subaccount ${props.account.id} Composition`, renderContent: () => - props.account ? ( - - ) : null, + props.account ? : null, isOpen: isOpen[0], toggleOpen: (index: number) => toggleOpen(index), renderSubTitle: () => <>, diff --git a/src/components/Account/CurrentAccountSummary.tsx b/src/components/Account/CurrentAccountSummary.tsx index 95eee3ca..6bab7c22 100644 --- a/src/components/Account/CurrentAccountSummary.tsx +++ b/src/components/Account/CurrentAccountSummary.tsx @@ -1,10 +1,10 @@ -import useCurrentAccount from 'hooks/useCurrentAccount' import AccountSummary from 'components/Account/AccountSummary' +import useCurrentAccount from 'hooks/useCurrentAccount' -function CurrentAccountSummary({ change }: { change?: AccountChange }) { +function CurrentAccountSummary() { const account = useCurrentAccount() - - return + if (!account) return + return } export default CurrentAccountSummary diff --git a/src/components/Modals/AssetAmountSelectActionModal.tsx b/src/components/Modals/AssetAmountSelectActionModal.tsx index 27217f93..e3045db4 100644 --- a/src/components/Modals/AssetAmountSelectActionModal.tsx +++ b/src/components/Modals/AssetAmountSelectActionModal.tsx @@ -20,7 +20,6 @@ interface Props { actionButtonText: string contentHeader?: JSX.Element showProgressIndicator: boolean - accountSummaryChange?: AccountChange onClose: () => void onChange: (value: BigNumber) => void onAction: (value: BigNumber, isMax: boolean) => void @@ -32,7 +31,6 @@ export default function AssetAmountSelectActionModal(props: Props) { title, coinBalances, actionButtonText, - accountSummaryChange, contentHeader = null, showProgressIndicator, onClose, @@ -67,9 +65,9 @@ export default function AssetAmountSelectActionModal(props: Props) { contentClassName='flex flex-col min-h-[400px]' > {contentHeader} -
+
} /> - +
) diff --git a/src/components/Modals/BorrowModal.tsx b/src/components/Modals/BorrowModal.tsx index cb322a9f..696b376c 100644 --- a/src/components/Modals/BorrowModal.tsx +++ b/src/components/Modals/BorrowModal.tsx @@ -1,5 +1,5 @@ import BigNumber from 'bignumber.js' -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import AccountSummary from 'components/Account/AccountSummary' import AssetImage from 'components/AssetImage' @@ -12,11 +12,13 @@ import Switch from 'components/Switch' import Text from 'components/Text' import TitleAndSubCell from 'components/TitleAndSubCell' import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider' -import { ASSETS } from 'constants/assets' import { BN_ZERO } from 'constants/math' +import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds' import useCurrentAccount from 'hooks/useCurrentAccount' import useHealthComputer from 'hooks/useHealthComputer' import useToggle from 'hooks/useToggle' +import { useUpdatedAccount } from 'hooks/useUpdatedAccount' +import { getDepositsAndLendsAfterCoinSpent } from 'hooks/useUpdatedAccount/functions' import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' import { formatPercent, formatValue } from 'utils/formatters' @@ -51,15 +53,17 @@ function BorrowModal(props: Props) { const { modal, account } = props const [percentage, setPercentage] = useState(0) const [amount, setAmount] = useState(BN_ZERO) - const [change, setChange] = useState() const [isConfirming, setIsConfirming] = useToggle() const [borrowToWallet, setBorrowToWallet] = useToggle() const borrow = useStore((s) => s.borrow) const repay = useStore((s) => s.repay) - const asset = modal.asset ?? ASSETS[0] + const asset = modal.asset const isRepay = modal.isRepay ?? false const [max, setMax] = useState(BN_ZERO) + const { simulateBorrow, simulateRepay } = useUpdatedAccount(account) + const { autoLendEnabledAccountIds } = useAutoLendEnabledAccountIds() + const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id) const { computeMaxBorrowAmount } = useHealthComputer(account) function resetState() { @@ -69,19 +73,24 @@ function BorrowModal(props: Props) { } async function onConfirmClick() { - if (!modal.asset) return + if (!asset) return setIsConfirming(true) let result + const { lends } = getDepositsAndLendsAfterCoinSpent( + BNCoin.fromDenomAndBigNumber(asset.denom, amount), + account, + ) if (isRepay) { result = await repay({ accountId: account.id, - coin: BNCoin.fromDenomAndBigNumber(modal.asset.denom, amount), + coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount), accountBalance: percentage === 100, + lends, }) } else { result = await borrow({ accountId: account.id, - coin: { denom: modal.asset.denom, amount: amount.toString() }, + coin: { denom: asset.denom, amount: amount.toString() }, borrowToWallet, }) } @@ -108,6 +117,15 @@ function BorrowModal(props: Props) { decimals: 6, }) + const handleChange = useCallback( + (newAmount: BigNumber) => { + const coin = BNCoin.fromDenomAndBigNumber(asset.denom, newAmount) + if (!amount.isEqualTo(newAmount)) setAmount(newAmount) + if (isRepay) simulateRepay(coin) + }, + [asset, amount, isRepay, simulateRepay], + ) + useEffect(() => { if (isRepay) { setMax(BN(getDebtAmount(modal))) @@ -123,25 +141,20 @@ function BorrowModal(props: Props) { }, [isRepay, modal, asset.denom, computeMaxBorrowAmount, borrowToWallet]) useEffect(() => { - if (!modal.asset) return + if (amount.isGreaterThan(max)) { + handleChange(max) + setAmount(max) + } + }, [amount, max, handleChange]) - setChange({ - deposits: [ - { - amount: isRepay ? BN_ZERO.minus(amount).toString() : BN_ZERO.plus(amount).toString(), - denom: modal.asset.denom, - }, - ], - debts: [ - { - amount: isRepay ? BN_ZERO.minus(amount).toString() : BN_ZERO.plus(amount).toString(), - denom: modal.asset.denom, - }, - ], - }) - }, [amount, modal.asset, account, isRepay]) + useEffect(() => { + if (isRepay) return + const coin = BNCoin.fromDenomAndBigNumber(asset.denom, amount.isGreaterThan(max) ? max : amount) + const target = borrowToWallet ? 'wallet' : isAutoLendEnabled ? 'lend' : 'deposit' + simulateBorrow(target, coin) + }, [isRepay, borrowToWallet, isAutoLendEnabled, simulateBorrow, asset, amount, max]) - if (!modal) return null + if (!modal || !asset) return null return ( } /> - +
) diff --git a/src/components/Modals/FundWithdraw/FundAccount.tsx b/src/components/Modals/FundWithdraw/FundAccount.tsx index 1fcf0e66..b8012cbf 100644 --- a/src/components/Modals/FundWithdraw/FundAccount.tsx +++ b/src/components/Modals/FundWithdraw/FundAccount.tsx @@ -9,6 +9,7 @@ import WalletBridges from 'components/Wallet/WalletBridges' import { BN_ZERO } from 'constants/math' import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds' import useToggle from 'hooks/useToggle' +import { useUpdatedAccount } from 'hooks/useUpdatedAccount' import useWalletBalances from 'hooks/useWalletBalances' import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' @@ -19,11 +20,10 @@ import { BN } from 'utils/helpers' interface Props { account: Account - setChange: (change: AccountChange | undefined) => void } export default function FundAccount(props: Props) { - const { account, setChange } = props + const { account } = props const accountId = account.id const address = useStore((s) => s.address) const deposit = useStore((s) => s.deposit) @@ -37,6 +37,7 @@ export default function FundAccount(props: Props) { fundingAssets.length > 0 && fundingAssets.every((a) => a.toCoin().amount !== '0') const { autoLendEnabledAccountIds } = useAutoLendEnabledAccountIds() const isAutoLendEnabled = autoLendEnabledAccountIds.includes(accountId) + const { simulateDeposits } = useUpdatedAccount(account) const baseBalance = useMemo( () => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0', @@ -86,21 +87,18 @@ export default function FundAccount(props: Props) { const updateFundingAssets = useCallback( (amount: BigNumber, denom: string) => { - setFundingAssets((prevAssets) => { - const assetToUpdateIdx = prevAssets.findIndex(byDenom(denom)) - if (assetToUpdateIdx > -1) { - prevAssets[assetToUpdateIdx].amount = amount - } - setChange({ [isAutoLendEnabled ? 'lends' : 'deposits']: prevAssets }) - return prevAssets - }) + const assetToUpdate = fundingAssets.find(byDenom(denom)) + if (assetToUpdate) { + assetToUpdate.amount = amount + setFundingAssets([...fundingAssets.filter((a) => a.denom !== denom), assetToUpdate]) + } }, - [setChange, isAutoLendEnabled], + [fundingAssets], ) useEffect(() => { - setChange({ [isAutoLendEnabled ? 'lends' : 'deposits']: fundingAssets }) - }, [isAutoLendEnabled, fundingAssets, setChange]) + simulateDeposits(isAutoLendEnabled ? 'lend' : 'deposit', fundingAssets) + }, [isAutoLendEnabled, fundingAssets, simulateDeposits]) useEffect(() => { if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) { diff --git a/src/components/Modals/FundWithdraw/FundAndWithdrawModalContent.tsx b/src/components/Modals/FundWithdraw/FundAndWithdrawModalContent.tsx index 75da6f4a..8dd6e603 100644 --- a/src/components/Modals/FundWithdraw/FundAndWithdrawModalContent.tsx +++ b/src/components/Modals/FundWithdraw/FundAndWithdrawModalContent.tsx @@ -4,6 +4,7 @@ import AccountSummary from 'components/Account/AccountSummary' import Card from 'components/Card' import FundAccount from 'components/Modals/FundWithdraw/FundAccount' import WithdrawFromAccount from 'components/Modals/FundWithdraw/WithdrawFromAccount' +import useStore from 'store' interface Props { account: Account @@ -12,21 +13,15 @@ interface Props { export default function FundWithdrawModalContent(props: Props) { const { account, isFunding } = props - const [change, setChange] = useState() - return (
- {isFunding ? ( - - ) : ( - - )} + {isFunding ? : } - +
) } diff --git a/src/components/Modals/FundWithdraw/WithdrawFromAccount.tsx b/src/components/Modals/FundWithdraw/WithdrawFromAccount.tsx index fa56cac7..996f34bb 100644 --- a/src/components/Modals/FundWithdraw/WithdrawFromAccount.tsx +++ b/src/components/Modals/FundWithdraw/WithdrawFromAccount.tsx @@ -1,5 +1,5 @@ import BigNumber from 'bignumber.js' -import { useEffect, useState } from 'react' +import { useState } from 'react' import Button from 'components/Button' import Divider from 'components/Divider' @@ -18,18 +18,19 @@ import { byDenom } from 'utils/array' interface Props { account: Account - setChange: (change: AccountChange | undefined) => void } export default function WithdrawFromAccount(props: Props) { - const { account, setChange } = props - const defaultAsset = ASSETS.find(byDenom(account.deposits[0].denom)) ?? ASSETS[0] + const { account } = props + const defaultAsset = + ASSETS.find(byDenom(account.deposits[0]?.denom || account.lends[0]?.denom)) ?? ASSETS[0] const withdraw = useStore((s) => s.withdraw) const [withdrawWithBorrowing, setWithdrawWithBorrowing] = useToggle() const [isConfirming, setIsConfirming] = useToggle() const [currentAsset, setCurrentAsset] = useState(defaultAsset) const [amount, setAmount] = useState(BN_ZERO) - const { updatedAccount, removeDepositByDenom } = useUpdatedAccount(account) + const { updatedAccount, removeDepositAndLendsByDenom, removeDeposits, addDebts } = + useUpdatedAccount(account) const { computeMaxWithdrawAmount } = useHealthComputer(account) const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount) const maxWithdrawAmount = computeMaxWithdrawAmount(currentAsset.denom) @@ -37,27 +38,19 @@ export default function WithdrawFromAccount(props: Props) { maxWithdrawAmount, ) const isWithinBalance = amount.isLessThan(maxWithdrawAmount) - const depositAmount = BN_ZERO.minus(isWithinBalance ? amount : maxWithdrawAmount) + const withdrawAmount = isWithinBalance ? amount : maxWithdrawAmount const debtAmount = isWithinBalance ? BN_ZERO : amount.minus(maxWithdrawAmount) const max = withdrawWithBorrowing ? maxWithdrawWithBorrowAmount : maxWithdrawAmount function onChangeAmount(val: BigNumber) { setAmount(val) - setChange({ - deposits: [ - { - amount: depositAmount.toString(), - denom: currentAsset.denom, - }, - ], - debts: [{ amount: debtAmount.toString(), denom: currentAsset.denom }], - }) + removeDeposits([BNCoin.fromDenomAndBigNumber(currentAsset.denom, withdrawAmount)]) + addDebts([BNCoin.fromDenomAndBigNumber(currentAsset.denom, debtAmount)]) } function resetState() { setCurrentAsset(defaultAsset) setAmount(BN_ZERO) - setChange(undefined) } async function onConfirm() { @@ -77,13 +70,9 @@ export default function WithdrawFromAccount(props: Props) { } } - useEffect(() => { - removeDepositByDenom(currentAsset.denom) - }, [currentAsset.denom, removeDepositByDenom]) - return ( <> -
+
-
-
- Withdraw with borrowing +
+
+ Withdraw with borrowing Borrow assets from your credit account to withdraw to your wallet diff --git a/src/components/Modals/LendAndReclaim/index.tsx b/src/components/Modals/LendAndReclaim/index.tsx index 53cb6186..7f8303ee 100644 --- a/src/components/Modals/LendAndReclaim/index.tsx +++ b/src/components/Modals/LendAndReclaim/index.tsx @@ -1,13 +1,13 @@ -import { useCallback, useState } from 'react' +import { useCallback } from 'react' -import useStore from 'store' -import useToggle from 'hooks/useToggle' +import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal' +import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader' import useCurrentAccount from 'hooks/useCurrentAccount' import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal' -import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader' -import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal' -import { BNCoin } from 'types/classes/BNCoin' +import useToggle from 'hooks/useToggle' import { useUpdatedAccount } from 'hooks/useUpdatedAccount' +import useStore from 'store' +import { BNCoin } from 'types/classes/BNCoin' function LendAndReclaimModalController() { const currentAccount = useCurrentAccount() @@ -28,9 +28,7 @@ function LendAndReclaimModal({ currentAccount, config }: Props) { const reclaim = useStore((s) => s.reclaim) const { close } = useLendAndReclaimModal() const [isConfirming, setIsConfirming] = useToggle() - const [accountChange, setAccountChange] = useState() - const { setAddedLends, setRemovedLends, removeDeposits, addDeposits } = - useUpdatedAccount(currentAccount) + const { simulateLending } = useUpdatedAccount(currentAccount) const { data, action } = config const { asset } = data @@ -41,18 +39,10 @@ function LendAndReclaimModal({ currentAccount, config }: Props) { const handleAmountChange = useCallback( (value: BigNumber) => { - setAccountChange(getAccountChange(isLendAction, value, asset.denom)) - const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value) - if (isLendAction) { - setAddedLends([coin]) - removeDeposits([coin]) - } else { - addDeposits([coin]) - setRemovedLends([coin]) - } + simulateLending(isLendAction, coin) }, - [addDeposits, asset.denom, isLendAction, removeDeposits, setAddedLends, setRemovedLends], + [asset.denom, isLendAction, simulateLending], ) const handleAction = useCallback( @@ -80,7 +70,6 @@ function LendAndReclaimModal({ currentAccount, config }: Props) { coinBalances={coinBalances} actionButtonText={actionText} showProgressIndicator={isConfirming} - accountSummaryChange={accountChange} title={`${actionText} ${asset.symbol}`} onClose={close} onAction={handleAction} @@ -89,18 +78,4 @@ function LendAndReclaimModal({ currentAccount, config }: Props) { ) } -const getAccountChange = (isLend: boolean, value: BigNumber, denom: string): AccountChange => { - const makeCoin = (denom: string, shouldNegate: boolean) => [ - { - amount: (shouldNegate ? value.negated() : value).toString(), - denom, - }, - ] - - return { - deposits: makeCoin(denom, isLend), - lends: makeCoin(denom, !isLend), - } -} - export default LendAndReclaimModalController diff --git a/src/components/Modals/Vault/VaultBorrowings.tsx b/src/components/Modals/Vault/VaultBorrowings.tsx index 55fd0dcf..a3b269b5 100644 --- a/src/components/Modals/Vault/VaultBorrowings.tsx +++ b/src/components/Modals/Vault/VaultBorrowings.tsx @@ -20,7 +20,6 @@ import { findCoinByDenom, getAssetByDenom } from 'utils/assets' import { formatPercent } from 'utils/formatters' export interface VaultBorrowingsProps { - updatedAccount: Account borrowings: BNCoin[] deposits: BNCoin[] primaryAsset: Asset @@ -37,7 +36,8 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) { const vaultModal = useStore((s) => s.vaultModal) const depositIntoVault = useStore((s) => s.depositIntoVault) const [isConfirming, setIsConfirming] = useState(false) - const { computeMaxBorrowAmount } = useHealthComputer(props.updatedAccount) + const updatedAccount = useStore((s) => s.updatedAccount) + const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount) const maxBorrowAmounts: BNCoin[] = useMemo(() => { return props.borrowings.map((borrowing) => { @@ -141,9 +141,10 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) { } async function onConfirm() { + if (!updatedAccount) return setIsConfirming(true) const isSuccess = await depositIntoVault({ - accountId: props.updatedAccount.id, + accountId: updatedAccount.id, actions: props.depositActions, }) setIsConfirming(false) diff --git a/src/components/Modals/Vault/VaultModalContent.tsx b/src/components/Modals/Vault/VaultModalContent.tsx index d274b5ff..1aabb11e 100644 --- a/src/components/Modals/Vault/VaultModalContent.tsx +++ b/src/components/Modals/Vault/VaultModalContent.tsx @@ -10,8 +10,8 @@ import { BN_ZERO } from 'constants/math' import useDepositVault from 'hooks/broadcast/useDepositVault' import useIsOpenArray from 'hooks/useIsOpenArray' import { useUpdatedAccount } from 'hooks/useUpdatedAccount' -import { byDenom } from 'utils/array' import { BNCoin } from 'types/classes/BNCoin' +import { byDenom } from 'utils/array' interface Props { vault: Vault | DepositedVault @@ -23,12 +23,12 @@ interface Props { export default function VaultModalContent(props: Props) { const { - addDebt, + addDebts, removeDeposits, - addedDebt, + addedDebts, removedDeposits, removedLends, - setRemovedLends, + removeLends, updatedAccount, addVaultValues, } = useUpdatedAccount(props.account) @@ -41,7 +41,7 @@ export default function VaultModalContent(props: Props) { vault: props.vault, reclaims: removedLends, deposits: removedDeposits, - borrowings: addedDebt, + borrowings: addedDebts, }) const handleDepositSelect = useCallback( @@ -67,11 +67,11 @@ export default function VaultModalContent(props: Props) { } }) - setRemovedLends(reclaims) + removeLends(reclaims) removeDeposits(deposits) setSelectedCoins(selectedCoins) }, - [props.account.deposits, removeDeposits, setRemovedLends], + [props.account.deposits, removeDeposits, removeLends], ) useEffect(() => { @@ -115,11 +115,11 @@ export default function VaultModalContent(props: Props) { if (isOpen[1]) return null - return + return } return ( -
+
( diff --git a/src/components/Trade/TradeModule/SwapForm/index.tsx b/src/components/Trade/TradeModule/SwapForm/index.tsx index 242e8480..451d5129 100644 --- a/src/components/Trade/TradeModule/SwapForm/index.tsx +++ b/src/components/Trade/TradeModule/SwapForm/index.tsx @@ -47,7 +47,7 @@ export default function SwapForm(props: Props) { const [estimatedFee, setEstimatedFee] = useState(defaultFee) const throttledEstimateExactIn = useMemo(() => asyncThrottle(estimateExactIn, 250), []) - const { removeDeposits, addDeposits, addDebt } = useUpdatedAccount(account) + const { removeDeposits, addDeposits, addDebts } = useUpdatedAccount(account) const borrowAsset = useMemo( () => borrowAssets.find(byDenom(sellAsset.denom)), @@ -163,9 +163,9 @@ export default function SwapForm(props: Props) { debounce((removeCoin: BNCoin, addCoin: BNCoin, debtCoin: BNCoin) => { removeDeposits([removeCoin]) addDeposits([addCoin]) - if (debtCoin.amount.isGreaterThan(BN_ZERO)) addDebt([debtCoin]) + if (debtCoin.amount.isGreaterThan(BN_ZERO)) addDebts([debtCoin]) }, 1000), - [removeDeposits, addDeposits, addDebt], + [removeDeposits, addDeposits, addDebts], ) useEffect(() => { @@ -173,8 +173,8 @@ export default function SwapForm(props: Props) { setSellAssetAmount(BN_ZERO) removeDeposits([]) addDeposits([]) - addDebt([]) - }, [buyAsset.denom, sellAsset.denom, removeDeposits, addDeposits, addDebt]) + addDebts([]) + }, [buyAsset.denom, sellAsset.denom, removeDeposits, addDeposits, addDebts]) useEffect(() => { const removeDepositAmount = sellAssetAmount.isGreaterThanOrEqualTo(sellSideMarginThreshold) diff --git a/src/hooks/useHealthComputer.tsx b/src/hooks/useHealthComputer.tsx index 86114a22..2cfb3040 100644 --- a/src/hooks/useHealthComputer.tsx +++ b/src/hooks/useHealthComputer.tsx @@ -1,13 +1,21 @@ import { useCallback, useEffect, useMemo, useState } from 'react' -import usePrices from 'hooks/usePrices' +import { BN_ZERO } from 'constants/math' import useAssetParams from 'hooks/useAssetParams' +import usePrices from 'hooks/usePrices' +import useVaultConfigs from 'hooks/useVaultConfigs' +import useStore from 'store' +import { + Positions, + VaultPositionValue, +} from 'types/generated/mars-credit-manager/MarsCreditManager.types' +import { VaultConfigBaseForString } from 'types/generated/mars-params/MarsParams.types' import { AssetParamsBaseForAddr, HealthComputer, } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types' -import { VaultConfigBaseForString } from 'types/generated/mars-params/MarsParams.types' -import useVaultConfigs from 'hooks/useVaultConfigs' +import { convertAccountToPositions } from 'utils/accounts' +import { HEALTH_BUFFER } from 'utils/constants' import { BorrowTarget, compute_health_js, @@ -16,15 +24,7 @@ import { max_withdraw_estimate_js, SwapKind, } from 'utils/health_computer' -import { convertAccountToPositions } from 'utils/accounts' -import { - Positions, - VaultPositionValue, -} from 'types/generated/mars-credit-manager/MarsCreditManager.types' -import useStore from 'store' -import { BN_ZERO } from 'constants/math' import { BN } from 'utils/helpers' -import { HEALTH_BUFFER } from 'utils/constants' export default function useHealthComputer(account?: Account) { const { data: prices } = usePrices() diff --git a/src/hooks/useUpdatedAccount/functions.ts b/src/hooks/useUpdatedAccount/functions.ts index adfb9c42..2b053bae 100644 --- a/src/hooks/useUpdatedAccount/functions.ts +++ b/src/hooks/useUpdatedAccount/functions.ts @@ -1,8 +1,10 @@ -import { BNCoin } from 'types/classes/BNCoin' -import { BN } from 'utils/helpers' -import { VaultValue } from 'hooks/useUpdatedAccount' -import { getVaultMetaData } from 'utils/vaults' +import { BN_ZERO } from 'constants/math' import { MOCK_DEPOSITED_VAULT_POSITION } from 'constants/vaults' +import { VaultValue } from 'hooks/useUpdatedAccount' +import { BNCoin } from 'types/classes/BNCoin' +import { byDenom } from 'utils/array' +import { BN } from 'utils/helpers' +import { getVaultMetaData } from 'utils/vaults' export function addCoins(additionalCoins: BNCoin[], currentCoins: BNCoin[]) { const currentDenoms = currentCoins.map((coin) => coin.denom) @@ -67,3 +69,26 @@ export function addValueToVaults( return vaults } + +export function getDepositsAndLendsAfterCoinSpent(coin: BNCoin, account?: Account) { + const makeOutput = (depositsAmount: BigNumber, lendsAmount: BigNumber) => ({ + deposits: BNCoin.fromDenomAndBigNumber(coin.denom, depositsAmount), + lends: BNCoin.fromDenomAndBigNumber(coin.denom, lendsAmount), + }) + + if (!account) return makeOutput(BN_ZERO, BN_ZERO) + + const accountDepositAmount = account.deposits.find(byDenom(coin.denom))?.amount ?? BN_ZERO + const accountLendsAmount = account.lends.find(byDenom(coin.denom))?.amount ?? BN_ZERO + const accountDepositAndLendAmount = accountDepositAmount.plus(accountLendsAmount) + + if (coin.amount.isLessThanOrEqualTo(accountDepositAmount)) { + return makeOutput(coin.amount, BN_ZERO) + } + + if (coin.amount.isGreaterThanOrEqualTo(accountDepositAndLendAmount)) { + return makeOutput(accountDepositAmount, accountLendsAmount) + } + + return makeOutput(accountDepositAmount, coin.amount.minus(accountDepositAmount)) +} diff --git a/src/hooks/useUpdatedAccount/index.ts b/src/hooks/useUpdatedAccount/index.ts index acc79aac..ce78c188 100644 --- a/src/hooks/useUpdatedAccount/index.ts +++ b/src/hooks/useUpdatedAccount/index.ts @@ -1,9 +1,15 @@ import { useCallback, useEffect, useState } from 'react' -import { addCoins, addValueToVaults, removeCoins } from 'hooks/useUpdatedAccount/functions' +import { + addCoins, + addValueToVaults, + getDepositsAndLendsAfterCoinSpent, + removeCoins, +} from 'hooks/useUpdatedAccount/functions' +import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' import { cloneAccount } from 'utils/accounts' -import useStore from 'store' +import { byDenom } from 'utils/array' export interface VaultValue { address: string @@ -16,16 +22,17 @@ export function useUpdatedAccount(account?: Account) { ) const [addedDeposits, addDeposits] = useState([]) const [removedDeposits, removeDeposits] = useState([]) - const [addedDebt, addDebt] = useState([]) - const [removedDebt, removeDebt] = useState([]) + const [addedDebts, addDebts] = useState([]) + const [removedDebts, removeDebts] = useState([]) const [addedVaultValues, addVaultValues] = useState([]) - const [addedLends, setAddedLends] = useState([]) - const [removedLends, setRemovedLends] = useState([]) + const [addedLends, addLends] = useState([]) + const [removedLends, removeLends] = useState([]) - const removeDepositByDenom = useCallback( + const removeDepositAndLendsByDenom = useCallback( (denom: string) => { if (!account) return - const deposit = account.deposits.find((deposit) => deposit.denom === denom) + const deposit = account.deposits.find(byDenom(denom)) + const lend = account.lends.find(byDenom(denom)) if (deposit) { removeDeposits((prevRemovedDeposits) => { @@ -35,19 +42,84 @@ export function useUpdatedAccount(account?: Account) { ] }) } + + if (lend) { + removeLends((prevRemovedLends) => { + return [...prevRemovedLends.filter((removedLends) => removedLends.denom !== denom), lend] + }) + } }, [account, removeDeposits], ) + const simulateBorrow = useCallback( + (target: 'wallet' | 'deposit' | 'lend', coin: BNCoin) => { + if (!account) return + resetAccount() + addDebts([coin]) + if (target === 'deposit') addDeposits([coin]) + if (target === 'lend') addLends([coin]) + }, + [account, addDebts, addDeposits, addLends], + ) + + const simulateLending = useCallback( + (isLendAction: boolean, coin: BNCoin) => { + if (!account) return + + resetAccount() + + if (isLendAction) { + addLends([coin]) + removeDeposits([coin]) + return + } + + removeLends([coin]) + addDeposits([coin]) + }, + [account, addDeposits, addLends, removeDeposits, removeLends], + ) + + const simulateRepay = useCallback( + (coin: BNCoin) => { + if (!account) return + removeDebts([coin]) + const { deposits, lends } = getDepositsAndLendsAfterCoinSpent(coin, account) + removeDeposits([deposits]) + removeLends([lends]) + }, + [account, removeDebts, removeDeposits, removeLends], + ) + + const simulateDeposits = useCallback( + (target: 'deposit' | 'lend', coins: BNCoin[]) => { + if (!account) return + resetAccount() + if (target === 'deposit') addDeposits(coins) + if (target === 'lend') addLends(coins) + }, + [account, addDeposits, addLends], + ) + + const resetAccount = () => { + addDeposits([]) + removeDeposits([]) + addDebts([]) + removeDebts([]) + addVaultValues([]) + addLends([]) + removeLends([]) + } useEffect(() => { if (!account) return const accountCopy = cloneAccount(account) accountCopy.deposits = addCoins(addedDeposits, [...accountCopy.deposits]) - accountCopy.debts = addCoins(addedDebt, [...accountCopy.debts]) + accountCopy.debts = addCoins(addedDebts, [...accountCopy.debts]) accountCopy.vaults = addValueToVaults(addedVaultValues, [...accountCopy.vaults]) accountCopy.deposits = removeCoins(removedDeposits, [...accountCopy.deposits]) - accountCopy.debts = removeCoins(removedDebt, [...accountCopy.debts]) + accountCopy.debts = removeCoins(removedDebts, [...accountCopy.debts]) accountCopy.lends = addCoins(addedLends, [...accountCopy.lends]) accountCopy.lends = removeCoins(removedLends, [...accountCopy.lends]) setUpdatedAccount(accountCopy) @@ -56,8 +128,8 @@ export function useUpdatedAccount(account?: Account) { return () => useStore.setState({ updatedAccount: undefined }) }, [ account, - addedDebt, - removedDebt, + addedDebts, + removedDebts, addedDeposits, removedDeposits, addedVaultValues, @@ -69,17 +141,21 @@ export function useUpdatedAccount(account?: Account) { updatedAccount, addDeposits, removeDeposits, - removeDepositByDenom, - addDebt, - removeDebt, + removeDepositAndLendsByDenom, + addDebts, + removeDebts, + addLends, + removeLends, addVaultValues, addedDeposits, - addedDebt, - removedDeposits, - removedDebt, + addedDebts, addedLends, - setAddedLends, + removedDeposits, + removedDebts, removedLends, - setRemovedLends, + simulateBorrow, + simulateDeposits, + simulateLending, + simulateRepay, } } diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx index dde3a7e3..61738724 100644 --- a/src/pages/_layout.tsx +++ b/src/pages/_layout.tsx @@ -9,6 +9,7 @@ import DesktopHeader from 'components/Header/DesktopHeader' import ModalsContainer from 'components/Modals/ModalsContainer' import PageMetadata from 'components/PageMetadata' import Toaster from 'components/Toaster' +import useCurrentAccount from 'hooks/useCurrentAccount' import useStore from 'store' interface Props { @@ -38,6 +39,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { const location = useLocation() const focusComponent = useStore((s) => s.focusComponent) const isFullWidth = location.pathname.includes('trade') || location.pathname === '/' + const account = useCurrentAccount() return ( <> @@ -49,6 +51,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { 'lg:min-h-[calc(100vh-65px)]', 'lg:mt-[65px]', 'min-h-screen gap-6 p-6 w-full relative', + account && 'pr-18', focusComponent || isMobile ? 'flex justify-center' : 'grid grid-cols-[auto_min-content] place-items-start', diff --git a/src/store/slices/broadcast.ts b/src/store/slices/broadcast.ts index 3ddee1c6..54917e30 100644 --- a/src/store/slices/broadcast.ts +++ b/src/store/slices/broadcast.ts @@ -333,17 +333,27 @@ export default function createBroadcastSlice( ) return !!response.result }, - repay: async (options: { accountId: string; coin: BNCoin; accountBalance?: boolean }) => { + repay: async (options: { + accountId: string + coin: BNCoin + accountBalance?: boolean + lends?: BNCoin + }) => { + const actions: Action[] = [ + { + repay: { + coin: options.coin.toActionCoin(options.accountBalance), + }, + }, + ] + + if (options.lends && options.lends.amount.isGreaterThan(0)) + actions.unshift({ reclaim: options.lends.toActionCoin() }) + const msg: CreditManagerExecuteMsg = { update_credit_account: { account_id: options.accountId, - actions: [ - { - repay: { - coin: options.coin.toActionCoin(options.accountBalance), - }, - }, - ], + actions, }, } diff --git a/src/types/interfaces/store/broadcast.d.ts b/src/types/interfaces/store/broadcast.d.ts index 320561ee..c448377c 100644 --- a/src/types/interfaces/store/broadcast.d.ts +++ b/src/types/interfaces/store/broadcast.d.ts @@ -24,6 +24,7 @@ interface BroadcastSlice { accountId: string coin: BNCoin accountBalance?: boolean + lends?: BNCoin }) => Promise swap: (options: { accountId: string diff --git a/src/utils/accounts.ts b/src/utils/accounts.ts index e414f71c..75de0fe2 100644 --- a/src/utils/accounts.ts +++ b/src/utils/accounts.ts @@ -6,6 +6,7 @@ import { Positions, VaultPosition, } from 'types/generated/mars-credit-manager/MarsCreditManager.types' +import { byDenom } from 'utils/array' import { getAssetByDenom } from 'utils/assets' import { BN } from 'utils/helpers' import { convertApyToApr } from 'utils/parsers' @@ -58,12 +59,12 @@ export const calculateAccountValue = ( } export const calculateAccountApr = ( - account: Account | AccountChange, - totalValue: BigNumber, + account: Account, borrowAssetsData: BorrowMarketTableData[], lendingAssetsData: LendingMarketTableData[], prices: BNCoin[], ): BigNumber => { + const totalValue = calculateAccountBalanceValue(account, prices) if (totalValue.isZero()) return BN_ZERO const { vaults, lends, debts } = account @@ -74,7 +75,7 @@ export const calculateAccountApr = ( lends?.forEach((lend) => { const asset = getAssetByDenom(lend.denom) if (!asset) return BN_ZERO - const price = prices.find((price) => price.denom === lend.denom)?.amount ?? 0 + const price = prices.find(byDenom(lend.denom))?.amount ?? 0 const amount = BN(lend.amount).shiftedBy(-asset.decimals) const apr = lendingAssetsData.find((lendingAsset) => lendingAsset.asset.denom === lend.denom) @@ -86,7 +87,7 @@ export const calculateAccountApr = ( vaults?.forEach((vault) => { const asset = getAssetByDenom(vault.denoms.lp) if (!asset) return BN_ZERO - const price = prices.find((price) => price.denom === vault.denoms.lp)?.amount ?? 0 + const price = prices.find(byDenom(vault.denoms.lp))?.amount ?? 0 const amount = BN(vault.amounts.locked).shiftedBy(-asset.decimals) const positionInterest = amount .multipliedBy(price) @@ -97,20 +98,19 @@ export const calculateAccountApr = ( debts?.forEach((debt) => { const asset = getAssetByDenom(debt.denom) if (!asset) return BN_ZERO - const price = prices.find((price) => price.denom === debt.denom)?.amount ?? 0 + const price = prices.find(byDenom(debt.denom))?.amount ?? 0 const amount = BN(debt.amount).shiftedBy(-asset.decimals) const apr = - borrowAssetsData.find((borrowAsset) => borrowAsset.asset.denom === debt.denom) - ?.marketLiquidityRate ?? 0 + borrowAssetsData.find((borrowAsset) => borrowAsset.asset.denom === debt.denom)?.borrowRate ?? + 0 const positionInterest = amount.multipliedBy(price).multipliedBy(apr) totalDeptsInterestValue = totalDeptsInterestValue.plus(positionInterest) }) - const totalPositiveInterestValue = totalLendsInterestValue + const totalInterstValue = totalLendsInterestValue .plus(totalVaultsInterestValue) .minus(totalDeptsInterestValue) - - const totalApr = totalPositiveInterestValue.dividedBy(totalValue).times(100) + const totalApr = totalInterstValue.dividedBy(totalValue).times(100) return totalApr } diff --git a/tailwind.config.js b/tailwind.config.js index 15b3ed14..5d235bf2 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -174,6 +174,7 @@ module.exports = { }, padding: { 5.5: '22px', + 18: '72px', 21: '84px', }, screens: {