MP-2894: withdraw from account (#339)

This commit is contained in:
Linkie Link 2023-08-04 15:10:30 +02:00 committed by GitHub
parent 103c8bed9a
commit 267b968c4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 429 additions and 272 deletions

View File

@ -132,7 +132,7 @@ export default function AccountBalancesTable(props: Props) {
return ( return (
<FormattedNumber <FormattedNumber
className='text-right text-xs' className='text-right text-xs'
amount={Number(BN(amount).abs().toPrecision(2))} amount={Number(BN(amount).abs())}
options={{ maxDecimals: 2, abbreviated: true }} options={{ maxDecimals: 2, abbreviated: true }}
animate animate
/> />

View File

@ -1,5 +1,6 @@
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import classNames from 'classnames' import classNames from 'classnames'
import { useMemo } from 'react'
import DisplayCurrency from 'components/DisplayCurrency' import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
@ -13,9 +14,8 @@ import {
calculateAccountApr, calculateAccountApr,
calculateAccountBalanceValue, calculateAccountBalanceValue,
calculateAccountBorrowRate, calculateAccountBorrowRate,
calculateAccountDebtValue,
calculateAccountDepositsValue,
calculateAccountPnL, calculateAccountPnL,
getAccountPositionValues,
} from 'utils/accounts' } from 'utils/accounts'
interface Props { interface Props {
@ -28,24 +28,37 @@ interface ItemProps {
current: BigNumber current: BigNumber
change: BigNumber change: BigNumber
className?: string className?: string
isBadIncrease?: boolean isDecrease?: boolean
isPercentage?: boolean isPercentage?: boolean
} }
export default function AccountComposition(props: Props) { export default function AccountComposition(props: Props) {
const { account, change } = props
const { data: prices } = usePrices() const { data: prices } = usePrices()
const balance = calculateAccountDepositsValue(props.account, prices)
const balanceChange = props.change ? calculateAccountDepositsValue(props.change, prices) : BN_ZERO const [depositsBalance, lendsBalance, debtsBalance] = useMemo(
const debtBalance = calculateAccountDebtValue(props.account, prices) () => getAccountPositionValues(account, prices),
const debtBalanceChange = props.change ? calculateAccountDebtValue(props.change, prices) : BN_ZERO [account, prices],
const totalBalance = calculateAccountBalanceValue(props.account, prices) )
const totalBalanceChange = props.change const [depositsBalanceChange, lendsBalanceChange, debtsBalanceChange] = useMemo(
? calculateAccountBalanceValue(props.change, prices) () => (change ? getAccountPositionValues(change, prices) : [BN_ZERO, BN_ZERO, BN_ZERO]),
: BN_ZERO [change, prices],
const apr = calculateAccountApr(props.account, prices) )
const aprChange = props.change ? calculateAccountPnL(props.change, prices) : BN_ZERO const totalBalance = useMemo(
const borrowRate = calculateAccountBorrowRate(props.account, prices) () => calculateAccountBalanceValue(account, prices),
const borrowRateChange = props.change ? calculateAccountPnL(props.change, prices) : BN_ZERO [account, prices],
)
const totalBalanceChange = useMemo(
() => (change ? calculateAccountBalanceValue(change, prices) : BN_ZERO),
[change, prices],
)
const balance = depositsBalance.plus(lendsBalance)
const balanceChange = depositsBalanceChange.plus(lendsBalanceChange)
const apr = calculateAccountApr(account, prices)
const aprChange = change ? calculateAccountPnL(change, prices) : BN_ZERO
const borrowRate = calculateAccountBorrowRate(account, prices)
const borrowRateChange = change ? calculateAccountPnL(change, prices) : BN_ZERO
return ( return (
<div className='w-full flex-wrap p-4'> <div className='w-full flex-wrap p-4'>
@ -57,10 +70,10 @@ export default function AccountComposition(props: Props) {
/> />
<Item <Item
title='Total Debt' title='Total Debt'
current={debtBalance} current={debtsBalance}
change={debtBalance.plus(debtBalanceChange)} change={debtsBalance.plus(debtsBalanceChange)}
className='pb-3' className='pb-3'
isBadIncrease isDecrease
/> />
<Item <Item
title='Total Balance' title='Total Balance'
@ -74,16 +87,16 @@ export default function AccountComposition(props: Props) {
current={borrowRate} current={borrowRate}
change={borrowRate.plus(borrowRateChange)} change={borrowRate.plus(borrowRateChange)}
isPercentage isPercentage
isBadIncrease isDecrease
/> />
</div> </div>
) )
} }
function Item(props: ItemProps) { function Item(props: ItemProps) {
const increase = props.isBadIncrease const { current, change } = props
? props.current.isGreaterThan(props.change) const increase = props.isDecrease ? current.isGreaterThan(change) : current.isLessThan(change)
: props.current.isLessThan(props.change)
return ( return (
<div className={classNames('flex w-full flex-nowrap', props.className)}> <div className={classNames('flex w-full flex-nowrap', props.className)}>
<div className='flex flex-shrink items-center'> <div className='flex flex-shrink items-center'>
@ -94,32 +107,32 @@ function Item(props: ItemProps) {
<div className='flex flex-1 items-center justify-end gap-2'> <div className='flex flex-1 items-center justify-end gap-2'>
{props.isPercentage ? ( {props.isPercentage ? (
<FormattedNumber <FormattedNumber
amount={props.current.toNumber()} amount={current.toNumber()}
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }} options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }}
className='text-sm' className='text-sm'
animate animate
/> />
) : ( ) : (
<DisplayCurrency <DisplayCurrency
coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, props.current)} coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, current)}
className='text-sm' className='text-sm'
/> />
)} )}
{!props.current.isEqualTo(props.change) && ( {!current.isEqualTo(change) && (
<> <>
<span className={classNames('w-3', increase ? 'text-profit' : 'text-loss')}> <span className={classNames('w-3', increase ? 'text-profit' : 'text-loss')}>
<ArrowRight /> <ArrowRight />
</span> </span>
{props.isPercentage ? ( {props.isPercentage ? (
<FormattedNumber <FormattedNumber
amount={props.change.toNumber()} amount={change.toNumber()}
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }} options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }}
className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')} className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')}
animate animate
/> />
) : ( ) : (
<DisplayCurrency <DisplayCurrency
coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, props.change)} coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, change)}
className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')} className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')}
/> />
)} )}

View File

@ -7,7 +7,7 @@ import FullOverlayContent from 'components/FullOverlayContent'
import { Plus } from 'components/Icons' import { Plus } from 'components/Icons'
import SwitchWithLabel from 'components/SwitchWithLabel' import SwitchWithLabel from 'components/SwitchWithLabel'
import Text from 'components/Text' import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInputWithSlider' import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import WalletBridges from 'components/Wallet/WalletBridges' import WalletBridges from 'components/Wallet/WalletBridges'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useAccounts from 'hooks/useAccounts' import useAccounts from 'hooks/useAccounts'
@ -16,6 +16,7 @@ import useCurrentAccount from 'hooks/useCurrentAccount'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
import useWalletBalances from 'hooks/useWalletBalances' import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store' import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { getAssetByDenom, getBaseAsset } from 'utils/assets' import { getAssetByDenom, getBaseAsset } from 'utils/assets'
import { hardcodedFee } from 'utils/constants' import { hardcodedFee } from 'utils/constants'
@ -30,13 +31,14 @@ export default function AccountFund() {
const currentAccount = useCurrentAccount() const currentAccount = useCurrentAccount()
const [isFunding, setIsFunding] = useToggle(false) const [isFunding, setIsFunding] = useToggle(false)
const [selectedAccountId, setSelectedAccountId] = useState<null | string>(null) const [selectedAccountId, setSelectedAccountId] = useState<null | string>(null)
const [fundingAssets, setFundingAssets] = useState<Coin[]>([]) const [fundingAssets, setFundingAssets] = useState<BNCoin[]>([])
const { data: walletBalances } = useWalletBalances(address) const { data: walletBalances } = useWalletBalances(address)
const baseAsset = getBaseAsset() const baseAsset = getBaseAsset()
const { autoLendEnabledAccountIds, toggleAutoLend } = useAutoLendEnabledAccountIds() const { autoLendEnabledAccountIds, toggleAutoLend } = useAutoLendEnabledAccountIds()
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(accountId ?? '0') const isAutoLendEnabled = autoLendEnabledAccountIds.includes(accountId ?? '0')
const hasAssetSelected = fundingAssets.length > 0 const hasAssetSelected = fundingAssets.length > 0
const hasFundingAssets = fundingAssets.length > 0 && fundingAssets.every((a) => a.amount !== '0') const hasFundingAssets =
fundingAssets.length > 0 && fundingAssets.every((a) => a.toCoin().amount !== '0')
const baseBalance = useMemo( const baseBalance = useMemo(
() => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0', () => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0',
@ -77,10 +79,9 @@ export default function AccountFund() {
) )
return return
const newFundingAssets = selectedDenoms.map((denom) => ({ const newFundingAssets = selectedDenoms.map((denom) =>
denom, BNCoin.fromDenomAndBigNumber(denom, BN(fundingAssets.find(byDenom(denom))?.amount ?? '0')),
amount: fundingAssets.find((asset) => asset.denom === denom)?.amount ?? '0', )
}))
setFundingAssets(newFundingAssets) setFundingAssets(newFundingAssets)
}, [selectedDenoms, fundingAssets]) }, [selectedDenoms, fundingAssets])
@ -89,7 +90,7 @@ export default function AccountFund() {
(amount: BigNumber, denom: string) => { (amount: BigNumber, denom: string) => {
const assetToUpdate = fundingAssets.find((asset) => asset.denom === denom) const assetToUpdate = fundingAssets.find((asset) => asset.denom === denom)
if (assetToUpdate) { if (assetToUpdate) {
assetToUpdate.amount = amount.toString() assetToUpdate.amount = amount
setFundingAssets([...fundingAssets.filter((a) => a.denom !== denom), assetToUpdate]) setFundingAssets([...fundingAssets.filter((a) => a.denom !== denom), assetToUpdate])
} }
}, },

View File

@ -14,7 +14,7 @@ import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds'
import useCurrentAccount from 'hooks/useCurrentAccount' import useCurrentAccount from 'hooks/useCurrentAccount'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import useStore from 'store' import useStore from 'store'
import { calculateAccountDepositsValue } from 'utils/accounts' import { calculateAccountValue } from 'utils/accounts'
import { getPage, getRoute } from 'utils/route' import { getPage, getRoute } from 'utils/route'
interface Props { interface Props {
@ -54,7 +54,7 @@ export default function AccountList(props: Props) {
return ( return (
<div className='flex w-full flex-wrap p-4'> <div className='flex w-full flex-wrap p-4'>
{props.accounts.map((account) => { {props.accounts.map((account) => {
const positionBalance = calculateAccountDepositsValue(account, prices) const positionBalance = calculateAccountValue('deposits', account, prices)
const isActive = accountId === account.id const isActive = accountId === account.id
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id) const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id)

View File

@ -4,7 +4,7 @@ import { ORACLE_DENOM } from 'constants/oracle'
import useHealthComputer from 'hooks/useHealthComputer' import useHealthComputer from 'hooks/useHealthComputer'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountDepositsValue } from 'utils/accounts' import { calculateAccountValue } from 'utils/accounts'
import { formatHealth } from 'utils/formatters' import { formatHealth } from 'utils/formatters'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
@ -14,7 +14,7 @@ interface Props {
export default function AccountStats(props: Props) { export default function AccountStats(props: Props) {
const { data: prices } = usePrices() const { data: prices } = usePrices()
const positionBalance = calculateAccountDepositsValue(props.account, prices) const positionBalance = calculateAccountValue('deposits', props.account, prices)
const { health } = useHealthComputer(props.account) const { health } = useHealthComputer(props.account)
const healthFactor = BN(100).minus(formatHealth(health)).toNumber() const healthFactor = BN(100).minus(formatHealth(health)).toNumber()
return ( return (

View File

@ -13,7 +13,7 @@ import useIsOpenArray from 'hooks/useIsOpenArray'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData' import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountDepositsValue } from 'utils/accounts' import { calculateAccountValue } from 'utils/accounts'
interface Props { interface Props {
account?: Account account?: Account
@ -24,7 +24,7 @@ export default function AccountSummary(props: Props) {
const [isOpen, toggleOpen] = useIsOpenArray(2, true) const [isOpen, toggleOpen] = useIsOpenArray(2, true)
const { data: prices } = usePrices() const { data: prices } = usePrices()
const accountBalance = props.account const accountBalance = props.account
? calculateAccountDepositsValue(props.account, prices) ? calculateAccountValue('deposits', props.account, prices)
: BN_ZERO : BN_ZERO
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } = const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
useBorrowMarketAssetsTableData() useBorrowMarketAssetsTableData()

View File

@ -8,10 +8,10 @@ import Divider from 'components/Divider'
import { ArrowRight } from 'components/Icons' import { ArrowRight } from 'components/Icons'
import Modal from 'components/Modal' import Modal from 'components/Modal'
import Text from 'components/Text' import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInputWithSlider' import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import { BN_ZERO } from 'constants/math'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import { BN_ZERO } from 'constants/math'
interface Props { interface Props {
asset: Asset asset: Asset

View File

@ -24,15 +24,12 @@ interface Props {
export default function AssetSelectTable(props: Props) { export default function AssetSelectTable(props: Props) {
const defaultSelected = useMemo(() => { const defaultSelected = useMemo(() => {
const assets = props.assets as BorrowAsset[] const assets = props.assets as BorrowAsset[]
return assets.reduce( return assets.reduce((acc, asset, index) => {
(acc, asset, index) => { if (props.selectedDenoms?.includes(asset.denom)) {
if (props.selectedDenoms?.includes(asset.denom)) { acc[index] = true
acc[index] = true }
} return acc
return acc }, {} as { [key: number]: boolean })
},
{} as { [key: number]: boolean },
)
}, [props.selectedDenoms, props.assets]) }, [props.selectedDenoms, props.assets])
const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }]) const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }])
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected) const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { useEffect, useState } from 'react'
import AccountSummary from 'components/Account/AccountSummary' import AccountSummary from 'components/Account/AccountSummary'
import AssetImage from 'components/AssetImage' import AssetImage from 'components/AssetImage'
@ -11,17 +11,17 @@ import Modal from 'components/Modal'
import Switch from 'components/Switch' import Switch from 'components/Switch'
import Text from 'components/Text' import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell' import TitleAndSubCell from 'components/TitleAndSubCell'
import TokenInputWithSlider from 'components/TokenInputWithSlider' import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import { ASSETS } from 'constants/assets' import { ASSETS } from 'constants/assets'
import { BN_ZERO } from 'constants/math'
import useCurrentAccount from 'hooks/useCurrentAccount' import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer'
import useToggle from 'hooks/useToggle' import useToggle from 'hooks/useToggle'
import useStore from 'store' import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { hardcodedFee } from 'utils/constants' import { hardcodedFee } from 'utils/constants'
import { formatPercent, formatValue } from 'utils/formatters' import { formatPercent, formatValue } from 'utils/formatters'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import useHealthComputer from 'hooks/useHealthComputer'
import { BN_ZERO } from 'constants/math'
function getDebtAmount(modal: BorrowModal | null) { function getDebtAmount(modal: BorrowModal | null) {
return BN((modal?.marketData as BorrowMarketTableData)?.debt ?? 0).toString() return BN((modal?.marketData as BorrowMarketTableData)?.debt ?? 0).toString()

View File

@ -0,0 +1,88 @@
import BigNumber from 'bignumber.js'
import { useState } from 'react'
import Button from 'components/Button'
import { ArrowRight } from 'components/Icons'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import { ASSETS } from 'constants/assets'
import { BN_ZERO } from 'constants/math'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { getAmount } from 'utils/accounts'
import { byDenom } from 'utils/array'
import { hardcodedFee } from 'utils/constants'
interface Props {
account: Account
setChange: (change: AccountChange | undefined) => void
}
export default function FundAccount(props: Props) {
const { account, setChange } = props
const deposit = useStore((s) => s.deposit)
const balances = useStore((s) => s.balances)
const defaultAsset = ASSETS.find(byDenom(balances[0].denom)) ?? ASSETS[0]
const [isConfirming, setIsConfirming] = useToggle()
const [currentAsset, setCurrentAsset] = useState(defaultAsset)
const [amount, setAmount] = useState(BN_ZERO)
const depositAmount = BN_ZERO.plus(amount)
const max = getAmount(currentAsset.denom, balances ?? [])
function onChangeAmount(val: BigNumber) {
setAmount(val)
setChange({
deposits: [
{
amount: depositAmount.toString(),
denom: currentAsset.denom,
},
],
})
}
function resetState() {
setCurrentAsset(defaultAsset)
setAmount(BN_ZERO)
setChange(undefined)
}
async function onConfirm() {
setIsConfirming(true)
const result = await deposit({
fee: hardcodedFee,
accountId: account.id,
coins: [BNCoin.fromDenomAndBigNumber(currentAsset.denom, amount)],
})
setIsConfirming(false)
if (result) {
resetState()
useStore.setState({ fundAndWithdrawModal: null })
}
}
return (
<>
<TokenInputWithSlider
asset={currentAsset}
onChange={onChangeAmount}
onChangeAsset={setCurrentAsset}
amount={amount}
max={max}
className='w-full'
balances={balances}
hasSelect
maxText='Max'
disabled={isConfirming}
/>
<Button
onClick={onConfirm}
showProgressIndicator={isConfirming}
className='w-full'
text={'Fund'}
rightIcon={<ArrowRight />}
/>
</>
)
}

View File

@ -1,17 +1,9 @@
import BigNumber from 'bignumber.js'
import { useState } from 'react' import { useState } from 'react'
import AccountSummary from 'components/Account/AccountSummary' import AccountSummary from 'components/Account/AccountSummary'
import Button from 'components/Button'
import Card from 'components/Card' import Card from 'components/Card'
import Divider from 'components/Divider' import FundAccount from 'components/Modals/FundWithdraw/FundAccount'
import { ArrowRight } from 'components/Icons' import WithdrawFromAccount from 'components/Modals/FundWithdraw/WithdrawFromAccount'
import TokenInputWithSlider from 'components/TokenInputWithSlider'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { getAmount } from 'utils/accounts'
import { hardcodedFee } from 'utils/constants'
import { BN_ZERO } from 'constants/math'
interface Props { interface Props {
account: Account account: Account
@ -19,103 +11,22 @@ interface Props {
} }
export default function FundWithdrawModalContent(props: Props) { export default function FundWithdrawModalContent(props: Props) {
const baseCurrency = useStore((s) => s.baseCurrency) const { account, isFunding } = props
const withdraw = useStore((s) => s.withdraw)
const deposit = useStore((s) => s.deposit)
const balances = useStore((s) => s.balances)
const [isConfirming, setIsConfirming] = useToggle()
const [currentAsset, setCurrentAsset] = useState(baseCurrency)
const [amount, setAmount] = useState(BN_ZERO)
const [change, setChange] = useState<AccountChange | undefined>() const [change, setChange] = useState<AccountChange | undefined>()
const max = props.isFunding
? getAmount(currentAsset.denom, balances ?? [])
: props.account
? getAmount(currentAsset.denom, props.account.deposits)
: BN_ZERO
function onChangeAmount(val: BigNumber) {
setAmount(val)
setChange({
deposits: [
{
amount: props.isFunding
? BN_ZERO.plus(amount).toString()
: BN_ZERO.minus(amount).toString(),
denom: currentAsset.denom,
},
],
})
}
function resetState() {
setCurrentAsset(baseCurrency)
setAmount(BN_ZERO)
setChange(undefined)
}
async function onConfirm() {
setIsConfirming(true)
let result
if (props.isFunding) {
result = await deposit({
fee: hardcodedFee,
accountId: props.account.id,
coins: [
{
denom: currentAsset.denom,
amount: amount.toString(),
},
],
})
} else {
result = await withdraw({
fee: hardcodedFee,
accountId: props.account.id,
coins: [
{
denom: currentAsset.denom,
amount: amount.toString(),
},
],
})
}
setIsConfirming(false)
if (result) {
resetState()
useStore.setState({ fundAndWithdrawModal: null })
}
}
return ( return (
<div className='flex flex-1 items-start gap-6 p-6'> <div className='flex flex-1 items-start gap-6 p-6'>
<Card <Card
className='flex flex-1 bg-white/5 p-4' className='flex flex-1 bg-white/5 p-4'
contentClassName='gap-6 flex flex-col justify-between h-full' contentClassName='gap-6 flex flex-col justify-between h-full min-h-[380px] '
> >
<TokenInputWithSlider {isFunding ? (
asset={currentAsset} <FundAccount account={account} setChange={setChange} />
onChange={onChangeAmount} ) : (
onChangeAsset={setCurrentAsset} <WithdrawFromAccount account={account} setChange={setChange} />
amount={amount} )}
max={max}
balances={props.isFunding ? balances : props.account.deposits}
accountId={!props.isFunding ? props.account.id : undefined}
hasSelect
maxText='Max'
disabled={isConfirming}
/>
<Divider />
<Button
onClick={onConfirm}
showProgressIndicator={isConfirming}
className='w-full'
text={props.isFunding ? 'Fund' : 'Withdraw'}
rightIcon={<ArrowRight />}
/>
</Card> </Card>
<AccountSummary account={props.account} change={change} /> <AccountSummary account={account} change={change} />
</div> </div>
) )
} }

View File

@ -0,0 +1,129 @@
import BigNumber from 'bignumber.js'
import { useEffect, useState } from 'react'
import Button from 'components/Button'
import Divider from 'components/Divider'
import { ArrowRight } from 'components/Icons'
import Switch from 'components/Switch'
import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import { ASSETS } from 'constants/assets'
import { BN_ZERO } from 'constants/math'
import useHealthComputer from 'hooks/useHealthComputer'
import useToggle from 'hooks/useToggle'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { hardcodedFee } from 'utils/constants'
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 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 { computeMaxWithdrawAmount } = useHealthComputer(account)
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
const maxWithdrawAmount = computeMaxWithdrawAmount(currentAsset.denom)
const maxWithdrawWithBorrowAmount = computeMaxBorrowAmount(currentAsset.denom, 'wallet').plus(
maxWithdrawAmount,
)
const isWithinBalance = amount.isLessThan(maxWithdrawAmount)
const depositAmount = BN_ZERO.minus(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 }],
})
}
function resetState() {
setCurrentAsset(defaultAsset)
setAmount(BN_ZERO)
setChange(undefined)
}
async function onConfirm() {
setIsConfirming(true)
const result = await withdraw({
fee: hardcodedFee,
accountId: account.id,
coins: [BNCoin.fromDenomAndBigNumber(currentAsset.denom, amount)],
borrow: debtAmount.isZero()
? []
: [BNCoin.fromDenomAndBigNumber(currentAsset.denom, debtAmount)],
})
setIsConfirming(false)
if (result) {
resetState()
useStore.setState({ fundAndWithdrawModal: null })
}
}
useEffect(() => {
removeDepositByDenom(currentAsset.denom)
}, [currentAsset.denom, removeDepositByDenom])
return (
<>
<div className='flex w-full flex-wrap'>
<TokenInputWithSlider
asset={currentAsset}
onChange={onChangeAmount}
onChangeAsset={setCurrentAsset}
amount={amount}
max={max}
className='w-full'
balances={account.deposits}
accountId={account.id}
hasSelect={account.deposits.length > 1}
maxText='Max'
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>
<Text size='xs' className='text-white/50'>
Borrow assets from your credit account to withdraw to your wallet
</Text>
</div>
<div className='flex flex-wrap items-center justify-end'>
<Switch
name='borrow-to-wallet'
checked={withdrawWithBorrowing}
onChange={setWithdrawWithBorrowing}
disabled={isConfirming}
/>
</div>
</div>
</div>
<Button
onClick={onConfirm}
showProgressIndicator={isConfirming}
className='w-full'
text={'Withdraw'}
rightIcon={<ArrowRight />}
/>
</>
)
}

View File

@ -157,8 +157,9 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
<div className='flex flex-1 flex-col gap-4 p-4'> <div className='flex flex-1 flex-col gap-4 p-4'>
{props.borrowings.map((coin) => { {props.borrowings.map((coin) => {
const asset = getAssetByDenom(coin.denom) const asset = getAssetByDenom(coin.denom)
const maxAmount = maxBorrowAmounts.find((maxAmount) => maxAmount.denom === coin.denom) const maxAmount = maxBorrowAmounts.find(
?.amount (maxAmount) => maxAmount.denom === coin.denom,
)?.amount
if (!asset || !maxAmount) if (!asset || !maxAmount)
return <React.Fragment key={`input-${coin.denom}`}></React.Fragment> return <React.Fragment key={`input-${coin.denom}`}></React.Fragment>
return ( return (

View File

@ -1,4 +1,3 @@
import BigNumber from 'bignumber.js'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import Accordion from 'components/Accordion' import Accordion from 'components/Accordion'
@ -7,10 +6,10 @@ import VaultBorrowings from 'components/Modals/Vault/VaultBorrowings'
import VaultBorrowingsSubTitle from 'components/Modals/Vault/VaultBorrowingsSubTitle' import VaultBorrowingsSubTitle from 'components/Modals/Vault/VaultBorrowingsSubTitle'
import VaultDeposit from 'components/Modals/Vault/VaultDeposits' import VaultDeposit from 'components/Modals/Vault/VaultDeposits'
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle' import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
import useIsOpenArray from 'hooks/useIsOpenArray'
import useDepositVault from 'hooks/broadcast/useDepositVault'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useDepositVault from 'hooks/broadcast/useDepositVault'
import useIsOpenArray from 'hooks/useIsOpenArray'
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
interface Props { interface Props {
vault: Vault | DepositedVault vault: Vault | DepositedVault

View File

@ -75,9 +75,10 @@ export default function Option(props: Props) {
<Text size='sm' className='col-span-2 text-white/50'> <Text size='sm' className='col-span-2 text-white/50'>
{formatValue(5, { maxDecimals: 2, minDecimals: 0, prefix: 'APY ', suffix: '%' })} {formatValue(5, { maxDecimals: 2, minDecimals: 0, prefix: 'APY ', suffix: '%' })}
</Text> </Text>
<Text size='sm' className='col-span-2 text-right text-white/50'> <DisplayCurrency
<DisplayCurrency coin={new BNCoin({ denom: asset.denom, amount: balance })} /> className='col-span-2 text-right text-sm text-white/50'
</Text> coin={new BNCoin({ denom: asset.denom, amount: balance })}
/>
</div> </div>
) )
} }

View File

@ -150,7 +150,7 @@ export default function Slider(props: Props) {
{(showTooltip || isDragging) && ( {(showTooltip || isDragging) && (
<div className='absolute -top-8 left-1/2 -translate-x-1/2 rounded-xs bg-martian-red px-2 py-[2px] text-xs'> <div className='absolute -top-8 left-1/2 -translate-x-1/2 rounded-xs bg-martian-red px-2 py-[2px] text-xs'>
<OverlayMark className='absolute -bottom-2 left-1/2 -z-1 h-2 -translate-x-1/2 text-martian-red' /> <OverlayMark className='absolute -bottom-2 left-1/2 -z-1 h-2 -translate-x-1/2 text-martian-red' />
{props.value}% {props.value.toFixed(0)}%
</div> </div>
)} )}
</div> </div>

View File

@ -1,10 +1,10 @@
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { useState } from 'react' import { useEffect, useState } from 'react'
import Slider from 'components/Slider' import Slider from 'components/Slider'
import TokenInput from 'components/TokenInput' import TokenInput from 'components/TokenInput'
import { BN } from 'utils/helpers'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import { BN } from 'utils/helpers'
interface Props { interface Props {
amount: BigNumber amount: BigNumber
@ -44,6 +44,13 @@ export default function TokenInputWithSlider(props: Props) {
props.onChangeAsset(newAsset) props.onChangeAsset(newAsset)
} }
useEffect(() => {
const newAmount = props.amount.isLessThan(props.max) ? props.amount : props.max
const newPercentage = newAmount.dividedBy(props.max).multipliedBy(100).toNumber()
if (!amount.isEqualTo(newAmount)) setAmount(newAmount)
if (percentage !== newPercentage) setPercentage(newPercentage)
}, [props.max, props.amount, amount, percentage])
return ( return (
<div className={props.className}> <div className={props.className}>
<TokenInput <TokenInput

View File

@ -43,66 +43,57 @@ export default function useHealthComputer(account?: Account) {
const vaultPositionValues = useMemo(() => { const vaultPositionValues = useMemo(() => {
if (!account?.vaults) return null if (!account?.vaults) return null
return account.vaults.reduce( return account.vaults.reduce((prev, curr) => {
(prev, curr) => { const baseCoinPrice = prices.find((price) => price.denom === curr.denoms.lp)?.amount || 0
const baseCoinPrice = prices.find((price) => price.denom === curr.denoms.lp)?.amount || 0 prev[curr.address] = {
prev[curr.address] = { base_coin: {
base_coin: { amount: '0', // Not used by healthcomputer
amount: '0', // Not used by healthcomputer denom: curr.denoms.lp,
denom: curr.denoms.lp, value: curr.amounts.unlocking.times(baseCoinPrice).integerValue().toString(),
value: curr.amounts.unlocking.times(baseCoinPrice).integerValue().toString(), },
}, vault_coin: {
vault_coin: { amount: '0', // Not used by healthcomputer
amount: '0', // Not used by healthcomputer denom: curr.denoms.vault,
denom: curr.denoms.vault, value: curr.values.primary
value: curr.values.primary .div(baseCurrencyPrice)
.div(baseCurrencyPrice) .plus(curr.values.secondary.div(baseCurrencyPrice))
.plus(curr.values.secondary.div(baseCurrencyPrice)) .integerValue()
.integerValue() .toString(),
.toString(), },
}, }
} return prev
return prev }, {} as { [key: string]: VaultPositionValue })
},
{} as { [key: string]: VaultPositionValue },
)
}, [account?.vaults, prices, baseCurrencyPrice]) }, [account?.vaults, prices, baseCurrencyPrice])
const priceData = useMemo(() => { const priceData = useMemo(() => {
const baseCurrencyPrice = const baseCurrencyPrice =
prices.find((price) => price.denom === baseCurrency.denom)?.amount || 0 prices.find((price) => price.denom === baseCurrency.denom)?.amount || 0
return prices.reduce( return prices.reduce((prev, curr) => {
(prev, curr) => { prev[curr.denom] = curr.amount.div(baseCurrencyPrice).decimalPlaces(18).toString()
prev[curr.denom] = curr.amount.div(baseCurrencyPrice).decimalPlaces(18).toString() return prev
return prev }, {} as { [key: string]: string })
},
{} as { [key: string]: string },
)
}, [prices, baseCurrency.denom]) }, [prices, baseCurrency.denom])
const denomsData = useMemo( const denomsData = useMemo(
() => () =>
assetParams.reduce( assetParams.reduce((prev, curr) => {
(prev, curr) => { const params: AssetParamsBaseForAddr = {
const params: AssetParamsBaseForAddr = { ...curr,
...curr, // The following overrides are required as testnet is 'broken' and new contracts are not updated yet
// The following overrides are required as testnet is 'broken' and new contracts are not updated yet // These overrides are not used by the healthcomputer internally, so they're not important anyways.
// These overrides are not used by the healthcomputer internally, so they're not important anyways. protocol_liquidation_fee: '1',
protocol_liquidation_fee: '1', liquidation_bonus: {
liquidation_bonus: { max_lb: '1',
max_lb: '1', min_lb: '1',
min_lb: '1', slope: '1',
slope: '1', starting_lb: '1',
starting_lb: '1', },
}, }
} prev[params.denom] = params
prev[params.denom] = params
return prev return prev
}, }, {} as { [key: string]: AssetParamsBaseForAddr }),
{} as { [key: string]: AssetParamsBaseForAddr },
),
[assetParams], [assetParams],
) )
@ -112,13 +103,10 @@ export default function useHealthComputer(account?: Account) {
const vaultPositionDenoms = positions.vaults.map((vault) => vault.vault.address) const vaultPositionDenoms = positions.vaults.map((vault) => vault.vault.address)
return vaultConfigs return vaultConfigs
.filter((config) => vaultPositionDenoms.includes(config.addr)) .filter((config) => vaultPositionDenoms.includes(config.addr))
.reduce( .reduce((prev, curr) => {
(prev, curr) => { prev[curr.addr] = curr
prev[curr.addr] = curr return prev
return prev }, {} as { [key: string]: VaultConfigBaseForString })
},
{} as { [key: string]: VaultConfigBaseForString },
)
}, [vaultConfigs, positions]) }, [vaultConfigs, positions])
const healthComputer: HealthComputer | null = useMemo(() => { const healthComputer: HealthComputer | null = useMemo(() => {

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { BNCoin } from 'types/classes/BNCoin'
import { addCoins, addValueToVaults, removeCoins } from 'hooks/useUpdatedAccount/functions' import { addCoins, addValueToVaults, removeCoins } from 'hooks/useUpdatedAccount/functions'
import { BNCoin } from 'types/classes/BNCoin'
import { cloneAccount } from 'utils/accounts' import { cloneAccount } from 'utils/accounts'
export interface VaultValue { export interface VaultValue {
@ -17,6 +17,16 @@ export function useUpdatedAccount(account: Account) {
const [removedDebt, removeDebt] = useState<BNCoin[]>([]) const [removedDebt, removeDebt] = useState<BNCoin[]>([])
const [addedVaultValues, addVaultValues] = useState<VaultValue[]>([]) const [addedVaultValues, addVaultValues] = useState<VaultValue[]>([])
const removeDepositByDenom = useCallback(
(denom: string) => {
const deposit = account.deposits.find((deposit) => deposit.denom === denom)
if (deposit) {
removeDeposits([...removedDeposits, deposit])
}
},
[account, removedDeposits],
)
useEffect(() => { useEffect(() => {
async function updateAccount() { async function updateAccount() {
const accountCopy = cloneAccount(account) const accountCopy = cloneAccount(account)
@ -35,6 +45,7 @@ export function useUpdatedAccount(account: Account) {
updatedAccount, updatedAccount,
addDeposits, addDeposits,
removeDeposits, removeDeposits,
removeDepositByDenom,
addDebt, addDebt,
removeDebt, removeDebt,
addVaultValues, addVaultValues,

View File

@ -147,24 +147,26 @@ export default function createBroadcastSlice(
return !!response.result return !!response.result
}, },
deposit: async (options: { fee: StdFee; accountId: string; coins: Coin[] }) => { deposit: async (options: { fee: StdFee; accountId: string; coins: BNCoin[] }) => {
const msg: CreditManagerExecuteMsg = { const msg: CreditManagerExecuteMsg = {
update_credit_account: { update_credit_account: {
account_id: options.accountId, account_id: options.accountId,
actions: options.coins.map((coin) => ({ actions: options.coins.map((coin) => ({
deposit: coin, deposit: coin.toCoin(),
})), })),
}, },
} }
const funds = options.coins.map((coin) => coin.toCoin())
const response = await get().executeMsg({ const response = await get().executeMsg({
messages: [ messages: [generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, funds)],
generateExecutionMessage(get().address, ENV.ADDRESS_CREDIT_MANAGER, msg, options.coins),
],
fee: options.fee, fee: options.fee,
}) })
const depositString = options.coins.map((coin) => formatAmountWithSymbol(coin)).join('and ') const depositString = options.coins
.map((coin) => formatAmountWithSymbol(coin.toCoin()))
.join('and ')
handleResponseMessages(response, `Deposited ${depositString} to Account ${options.accountId}`) handleResponseMessages(response, `Deposited ${depositString} to Account ${options.accountId}`)
return !!response.result return !!response.result
}, },
@ -247,13 +249,23 @@ export default function createBroadcastSlice(
handleResponseMessages(response, `Deposited into vault`) handleResponseMessages(response, `Deposited into vault`)
return !!response.result return !!response.result
}, },
withdraw: async (options: { fee: StdFee; accountId: string; coins: Coin[] }) => { withdraw: async (options: {
fee: StdFee
accountId: string
coins: BNCoin[]
borrow: BNCoin[]
}) => {
const withdrawActions = options.coins.map((coin) => ({
withdraw: coin.toCoin(),
}))
const borrowActions = options.borrow.map((coin) => ({
borrow: coin.toCoin(),
}))
const msg: CreditManagerExecuteMsg = { const msg: CreditManagerExecuteMsg = {
update_credit_account: { update_credit_account: {
account_id: options.accountId, account_id: options.accountId,
actions: options.coins.map((coin) => ({ actions: [...borrowActions, ...withdrawActions],
withdraw: coin,
})),
}, },
} }
@ -262,7 +274,9 @@ export default function createBroadcastSlice(
fee: options.fee, fee: options.fee,
}) })
const withdrawString = options.coins.map((coin) => formatAmountWithSymbol(coin)).join('and ') const withdrawString = options.coins
.map((coin) => formatAmountWithSymbol(coin.toCoin()))
.join('and ')
handleResponseMessages( handleResponseMessages(
response, response,
`Withdrew ${withdrawString} from Account ${options.accountId}`, `Withdrew ${withdrawString} from Account ${options.accountId}`,

View File

@ -4,8 +4,7 @@
@font-face { @font-face {
font-family: Inter; font-family: Inter;
src: src: url('../fonts/Inter-ExtraLight.woff2') format('woff2'),
url('../fonts/Inter-ExtraLight.woff2') format('woff2'),
url('../fonts/Inter-ExtraLight.woff') format('woff'); url('../fonts/Inter-ExtraLight.woff') format('woff');
font-weight: 300; font-weight: 300;
font-style: normal; font-style: normal;
@ -14,8 +13,7 @@
@font-face { @font-face {
font-family: Inter; font-family: Inter;
src: src: url('../fonts/Inter-Regular.woff2') format('woff2'),
url('../fonts/Inter-Regular.woff2') format('woff2'),
url('../fonts/Inter-Regular.woff') format('woff'); url('../fonts/Inter-Regular.woff') format('woff');
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
@ -24,8 +22,7 @@
@font-face { @font-face {
font-family: Inter; font-family: Inter;
src: src: url('../fonts/Inter-SemiBold.woff2') format('woff2'),
url('../fonts/Inter-SemiBold.woff2') format('woff2'),
url('../fonts/Inter-SemiBold.woff') format('woff'); url('../fonts/Inter-SemiBold.woff') format('woff');
font-weight: 600; font-weight: 600;
font-style: normal; font-style: normal;

View File

@ -16,7 +16,7 @@ interface BroadcastSlice {
}) => Promise<boolean> }) => Promise<boolean>
createAccount: (options: { fee: StdFee }) => Promise<string | null> createAccount: (options: { fee: StdFee }) => Promise<string | null>
deleteAccount: (options: { fee: StdFee; accountId: string; lends: BNCoin[] }) => Promise<boolean> deleteAccount: (options: { fee: StdFee; accountId: string; lends: BNCoin[] }) => Promise<boolean>
deposit: (options: { fee: StdFee; accountId: string; coins: Coin[] }) => Promise<boolean> deposit: (options: { fee: StdFee; accountId: string; coins: BNCoin[] }) => Promise<boolean>
unlock: (options: { unlock: (options: {
fee: StdFee fee: StdFee
accountId: string accountId: string
@ -33,7 +33,12 @@ interface BroadcastSlice {
accountId: string accountId: string
actions: Action[] actions: Action[]
}) => Promise<boolean> }) => Promise<boolean>
withdraw: (options: { fee: StdFee; accountId: string; coins: Coin[] }) => Promise<boolean> withdraw: (options: {
fee: StdFee
accountId: string
coins: BNCoin[]
borrow: BNCoin[]
}) => Promise<boolean>
lend: (options: { lend: (options: {
fee: StdFee fee: StdFee
accountId: string accountId: string

View File

@ -13,39 +13,34 @@ export const calculateAccountBalanceValue = (
account: Account | AccountChange, account: Account | AccountChange,
prices: BNCoin[], prices: BNCoin[],
): BigNumber => { ): BigNumber => {
const totalDepositValue = calculateAccountDepositsValue(account, prices) const depositsValue = calculateAccountValue('deposits', account, prices)
const totalDebtValue = calculateAccountDebtValue(account, prices) const lendsValue = calculateAccountValue('lends', account, prices)
const debtsValue = calculateAccountValue('debts', account, prices)
return totalDepositValue.minus(totalDebtValue) return depositsValue.plus(lendsValue).minus(debtsValue)
} }
export const calculateAccountDepositsValue = ( export const getAccountPositionValues = (account: Account | AccountChange, prices: BNCoin[]) => {
const deposits = calculateAccountValue('deposits', account, prices)
const lends = calculateAccountValue('lends', account, prices)
const debts = calculateAccountValue('debts', account, prices)
return [deposits, lends, debts]
}
export const calculateAccountValue = (
type: 'deposits' | 'lends' | 'debts',
account: Account | AccountChange, account: Account | AccountChange,
prices: BNCoin[], prices: BNCoin[],
): BigNumber => { ): BigNumber => {
if (!account.deposits) return BN_ZERO if (!account[type]) return BN_ZERO
return account.deposits.reduce((acc, deposit) => { return account[type]?.reduce((acc, position) => {
const asset = getAssetByDenom(deposit.denom) const asset = getAssetByDenom(position.denom)
if (!asset) return acc if (!asset) return acc
const price = prices.find((price) => price.denom === deposit.denom)?.amount ?? 0 const price = prices.find((price) => price.denom === position.denom)?.amount ?? 0
const amount = BN(deposit.amount).shiftedBy(-asset.decimals) const amount = BN(position.amount).shiftedBy(-asset.decimals)
const depositValue = amount.multipliedBy(price) const positionValue = amount.multipliedBy(price)
return acc.plus(depositValue) return acc.plus(positionValue)
}, BN_ZERO)
}
export const calculateAccountDebtValue = (
account: Account | AccountChange,
prices: BNCoin[],
): BigNumber => {
if (!account.debts) return BN_ZERO
return account.debts.reduce((acc, debt) => {
const asset = getAssetByDenom(debt.denom)
if (!asset) return acc
const price = prices.find((price) => price.denom === debt.denom)?.amount ?? 0
const debtAmount = BN(debt.amount).shiftedBy(-asset.decimals)
const debtValue = debtAmount.multipliedBy(price)
return acc.plus(debtValue)
}, BN_ZERO) }, BN_ZERO)
} }
@ -105,7 +100,7 @@ export function convertAccountToPositions(account: Account): Positions {
], ],
}, },
}, },
}) as VaultPosition, } as VaultPosition),
), ),
} }
} }