Mp 3260 finalize account summary (#395)

This commit is contained in:
Linkie Link 2023-08-26 10:30:56 +02:00 committed by GitHub
parent 6d4c09981f
commit 2d146449ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 340 additions and 270 deletions

View File

@ -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 (
<div className='flex-wrap w-full p-4 pb-1'>
<Item
title='Total Position Value'
current={balance}
change={balance.plus(balanceChange)}
current={positionValue}
change={hasChanged ? updatedPositionValue : positionValue}
className='pb-3'
/>
<Item
title='Total Debt'
current={debtsBalance}
change={debtsBalance.plus(debtsBalanceChange)}
change={hasChanged ? updatedDebtsBalance : debtsBalance}
className='pb-3'
isDecrease
/>
<Item
title='Total Balance'
current={totalBalance}
change={totalBalance.plus(totalBalanceChange)}
change={hasChanged ? updatedTotalBalance : totalBalance}
className='py-3 font-bold border border-transparent border-y-white/20'
/>
<Item title='APR' current={apr} change={apr.plus(aprChange)} className='py-3' isPercentage />
<Item
title='APR'
current={apr}
change={hasChanged ? updatedApr : apr}
className='py-3'
isPercentage
/>
</div>
)
}
@ -135,7 +147,7 @@ function Item(props: ItemProps) {
className='text-sm'
/>
)}
{!current.isEqualTo(change) && (
{current.toFixed(2) !== change.toFixed(2) && (
<>
<span className={classNames('w-3', increase ? 'text-profit' : 'text-loss')}>
<ArrowRight />

View File

@ -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 (
<div
@ -116,34 +108,18 @@ function AccountDetails(props: Props) {
options={{ maxDecimals: 0, minDecimals: 0, suffix: '%' }}
animate
/>
{updatedHealth > 0 && health !== updatedHealth && (
<>
<ArrowRight
width={16}
className={classNames(health > updatedHealth ? 'text-loss' : 'text-success')}
/>
<FormattedNumber
className={'w-full text-center text-xs'}
amount={updatedHealth}
options={{ maxDecimals: 0, minDecimals: 0, suffix: '%' }}
animate
/>
</>
)}
</div>
</div>
<div className='w-full py-4 border-t border-white/20'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Leverage
</Text>
<Text size='2xs' className='text-center'>
<FormattedNumber
className={'w-full text-center text-2xs'}
amount={leverage.toNumber()}
options={{ maxDecimals: 2, minDecimals: 2, suffix: 'x' }}
animate
/>
</Text>
<FormattedNumber
className={'w-full text-center text-2xs'}
amount={leverage.toNumber()}
options={{ maxDecimals: 2, minDecimals: 2, suffix: 'x' }}
animate
/>
</div>
<div className='w-full py-4 border-t border-white/20'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
@ -163,7 +139,7 @@ function AccountDetails(props: Props) {
/>
</div>
</div>
<div className='flex w-80'>
<div className='flex w-80 backdrop-blur-sticky'>
<Card className='w-full bg-white/5' title={`Account ${account.id}`}>
<AccountComposition account={account} />
<Text className='w-full px-4 py-2 bg-white/10 text-white/40'>Balances</Text>

View File

@ -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 (
<div className='flex flex-wrap w-full p-4'>
{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={<ArrowUpLine />}
onClick={() => {
setShowMenu(false)
if (positionBalance.isLessThanOrEqualTo(0)) {
useStore.setState({
focusComponent: {
@ -108,6 +111,7 @@ export default function AccountList(props: Props) {
leftIcon={<ArrowDownLine />}
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={<TrashBin />}
text='Delete'
onClick={() => deleteAccountHandler()}
onClick={() => {
setShowMenu(false)
deleteAccountHandler()
}}
/>
<Button
className='w-full'

View File

@ -138,7 +138,9 @@ export default function AccountMenuContent(props: Props) {
<CircularProgress size={40} />
</div>
)}
{hasCreditAccounts && !isLoadingAccount && <AccountList accounts={props.accounts} />}
{hasCreditAccounts && !isLoadingAccount && (
<AccountList accounts={props.accounts} setShowMenu={setShowMenu} />
)}
</div>
</Overlay>
</div>

View File

@ -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 (
<div className='flex-wrap w-full'>

View File

@ -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 ? (
<AccountComposition account={props.account} change={props.change} />
) : null,
props.account ? <AccountComposition account={props.account} /> : null,
isOpen: isOpen[0],
toggleOpen: (index: number) => toggleOpen(index),
renderSubTitle: () => <></>,

View File

@ -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 <AccountSummary account={account} change={change} />
if (!account) return
return <AccountSummary account={account} />
}
export default CurrentAccountSummary

View File

@ -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}
<div className='flex flex-1 items-start gap-6 p-6'>
<div className='flex items-start flex-1 gap-6 p-6'>
<Card
className='flex flex-1 bg-white/5 p-4'
className='flex flex-1 p-4 bg-white/5'
contentClassName='gap-6 flex flex-col justify-between h-full'
>
<TokenInputWithSlider
@ -91,7 +89,7 @@ export default function AssetAmountSelectActionModal(props: Props) {
rightIcon={<ArrowRight />}
/>
</Card>
<CurrentAccountSummary change={accountSummaryChange} />
<CurrentAccountSummary />
</div>
</Modal>
)

View File

@ -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<AccountChange | undefined>()
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 (
<Modal
onClose={onClose}
@ -183,7 +196,7 @@ function BorrowModal(props: Props) {
<div className='flex flex-wrap w-full'>
<TokenInputWithSlider
asset={asset}
onChange={setAmount}
onChange={handleChange}
amount={amount}
max={max}
className='w-full'
@ -219,7 +232,7 @@ function BorrowModal(props: Props) {
rightIcon={<ArrowRight />}
/>
</Card>
<AccountSummary account={account} change={change} />
<AccountSummary account={account} />
</div>
</Modal>
)

View File

@ -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)) {

View File

@ -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<AccountChange | undefined>()
return (
<div className='flex items-start flex-1 gap-6 p-6'>
<Card
className='flex flex-1 p-4 bg-white/5'
contentClassName='gap-6 flex flex-col justify-between h-full min-h-[380px]'
>
{isFunding ? (
<FundAccount account={account} setChange={setChange} />
) : (
<WithdrawFromAccount account={account} setChange={setChange} />
)}
{isFunding ? <FundAccount account={account} /> : <WithdrawFromAccount account={account} />}
</Card>
<AccountSummary account={account} change={change} />
<AccountSummary account={account} />
</div>
)
}

View File

@ -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 (
<>
<div className='flex w-full flex-wrap'>
<div className='flex flex-wrap w-full'>
<TokenInputWithSlider
asset={currentAsset}
onChange={onChangeAmount}
@ -98,9 +87,9 @@ export default function WithdrawFromAccount(props: Props) {
disabled={isConfirming}
/>
<Divider className='my-6' />
<div className='flex w-full flex-wrap'>
<div className='flex flex-1 flex-wrap'>
<Text className='mb-1 w-full'>Withdraw with borrowing</Text>
<div className='flex flex-wrap w-full'>
<div className='flex flex-wrap flex-1'>
<Text className='w-full mb-1'>Withdraw with borrowing</Text>
<Text size='xs' className='text-white/50'>
Borrow assets from your credit account to withdraw to your wallet
</Text>

View File

@ -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<AccountChange | undefined>()
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

View File

@ -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)

View File

@ -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 <VaultBorrowingsSubTitle borrowings={addedDebt} />
return <VaultBorrowingsSubTitle borrowings={addedDebts} />
}
return (
<div className='flex flex-1 items-start gap-6 p-6'>
<div className='flex items-start flex-1 gap-6 p-6'>
<Accordion
className='h-[546px] overflow-y-scroll scrollbar-hide'
items={[
@ -144,12 +144,11 @@ export default function VaultModalContent(props: Props) {
{
renderContent: () => (
<VaultBorrowings
updatedAccount={updatedAccount || props.account}
borrowings={addedDebt}
borrowings={addedDebts}
deposits={removedDeposits}
primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset}
onChangeBorrowings={addDebt}
onChangeBorrowings={addDebts}
vault={props.vault}
depositActions={depositActions}
/>

View File

@ -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)

View File

@ -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()

View File

@ -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))
}

View File

@ -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<BNCoin[]>([])
const [removedDeposits, removeDeposits] = useState<BNCoin[]>([])
const [addedDebt, addDebt] = useState<BNCoin[]>([])
const [removedDebt, removeDebt] = useState<BNCoin[]>([])
const [addedDebts, addDebts] = useState<BNCoin[]>([])
const [removedDebts, removeDebts] = useState<BNCoin[]>([])
const [addedVaultValues, addVaultValues] = useState<VaultValue[]>([])
const [addedLends, setAddedLends] = useState<BNCoin[]>([])
const [removedLends, setRemovedLends] = useState<BNCoin[]>([])
const [addedLends, addLends] = useState<BNCoin[]>([])
const [removedLends, removeLends] = useState<BNCoin[]>([])
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,
}
}

View File

@ -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',

View File

@ -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,
},
}

View File

@ -24,6 +24,7 @@ interface BroadcastSlice {
accountId: string
coin: BNCoin
accountBalance?: boolean
lends?: BNCoin
}) => Promise<boolean>
swap: (options: {
accountId: string

View File

@ -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
}

View File

@ -174,6 +174,7 @@ module.exports = {
},
padding: {
5.5: '22px',
18: '72px',
21: '84px',
},
screens: {