Liq price in balances (#679)

* add liquidation price to balances table trade

* add depositcap to HLS

* fix: add width classes to the balances table, remove abbreviation, remove flicker

* fix: fixed the account selection and added a tooltip

* fix wasm file for debt liquidation price

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>
This commit is contained in:
Bob van der Helm
2023-12-07 14:44:31 +01:00
committed by GitHub
co-authored by Linkie Link
parent b82cf37c3d
commit 2effdfadac
20 changed files with 460 additions and 324 deletions
@@ -1,7 +1,7 @@
import AssetRate from 'components/Asset/AssetRate'
import { byDenom } from 'utils/array'
export const APY_META = { accessorKey: 'apy', header: 'APY' }
export const APY_META = { accessorKey: 'apy', header: 'APY', meta: { className: 'w-30' } }
interface Props {
apy: number
@@ -0,0 +1,66 @@
import { useEffect, useMemo, useState } from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { InfoCircle } from 'components/Icons'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import useLiquidationPrice from 'hooks/useLiquidationPrice'
import { BNCoin } from 'types/classes/BNCoin'
import { LiquidationPriceKind } from 'utils/health_computer'
import { BN } from 'utils/helpers'
export const LIQ_META = {
accessorKey: 'symbol',
header: 'Liquidation Price',
id: 'liqPrice',
meta: { className: 'w-40' },
}
interface Props {
amount: number
computeLiquidationPrice: (denom: string, kind: LiquidationPriceKind) => number | null
denom: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
}
export default function LiqPrice(props: Props) {
const { denom, type, amount, computeLiquidationPrice } = props
const [lastLiquidationPrice, setLastLiquidationPrice] = useState<number | null>(null)
const liqPrice = useMemo(() => {
if (type === 'vault' || amount === 0) return 0
return computeLiquidationPrice(denom, type === 'borrowing' ? 'debt' : 'asset')
}, [amount, computeLiquidationPrice, denom, type])
const { liquidationPrice } = useLiquidationPrice(liqPrice)
useEffect(() => {
if (lastLiquidationPrice !== liqPrice && liqPrice !== null) setLastLiquidationPrice(liqPrice)
}, [liqPrice, lastLiquidationPrice])
if (!lastLiquidationPrice || (liquidationPrice === 0 && lastLiquidationPrice === 0))
return (
<Text size='xs' className='flex items-center justify-end number'>
N/A
<Tooltip
content={
type === 'vault'
? 'Liquidation prices cannot be calculated for farm positions. But it a drop in price of the underlying assets can still cause a liquidation.'
: 'The position size is too small to liquidate the account, even if the price goes to $0.00.'
}
type='info'
className='ml-1'
>
<InfoCircle className='w-3.5 h-3.5 text-white/40 hover:text-inherit' />
</Tooltip>
</Text>
)
return (
<DisplayCurrency
className='text-xs text-right number'
coin={BNCoin.fromDenomAndBigNumber('usd', BN(lastLiquidationPrice))}
options={{ abbreviated: false }}
/>
)
}
@@ -0,0 +1,26 @@
import DisplayCurrency from 'components/DisplayCurrency'
import usePrice from 'hooks/usePrice'
import { BNCoin } from 'types/classes/BNCoin'
import { BN } from 'utils/helpers'
export const PRICE_META = { id: 'price', header: 'Price', meta: { className: 'w-30' } }
interface Props {
amount: number
denom: string
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
}
export default function Price(props: Props) {
const price = usePrice(props.denom)
if (props.amount === 0 || props.type === 'vault') return null
return (
<DisplayCurrency
className='text-xs text-right number'
coin={BNCoin.fromDenomAndBigNumber('usd', BN(price))}
options={{ abbreviated: false }}
/>
)
}
@@ -6,7 +6,7 @@ import { FormattedNumber } from 'components/FormattedNumber'
import { MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math'
import { formatAmountToPrecision } from 'utils/formatters'
export const SIZE_META = { accessorKey: 'size', header: 'Size' }
export const SIZE_META = { accessorKey: 'size', header: 'Size', meta: { className: 'w-40' } }
interface Props {
size: number
@@ -1,8 +1,10 @@
import { ColumnDef } from '@tanstack/react-table'
import { ColumnDef, Row } from '@tanstack/react-table'
import { useMemo } from 'react'
import Apy, { APY_META } from 'components/Account/AccountBalancesTable/Columns/Apy'
import Asset, { ASSET_META } from 'components/Account/AccountBalancesTable/Columns/Asset'
import LiqPrice, { LIQ_META } from 'components/Account/AccountBalancesTable/Columns/LiqPrice'
import Price, { PRICE_META } from 'components/Account/AccountBalancesTable/Columns/Price'
import Size, {
SIZE_META,
sizeSortingFn,
@@ -11,10 +13,18 @@ import Value, {
VALUE_META,
valueSortingFn,
} from 'components/Account/AccountBalancesTable/Columns/Value'
import useHealthComputer from 'hooks/useHealthComputer'
import useMarketAssets from 'hooks/useMarketAssets'
import useStore from 'store'
export default function useAccountBalancesColumns() {
export default function useAccountBalancesColumns(
account: Account,
showLiquidationPrice?: boolean,
) {
const { data: markets } = useMarketAssets()
const updatedAccount = useStore((s) => s.updatedAccount)
const { computeLiquidationPrice } = useHealthComputer(updatedAccount ?? account)
return useMemo<ColumnDef<AccountBalanceRow>[]>(() => {
return [
@@ -46,6 +56,36 @@ export default function useAccountBalancesColumns() {
),
sortingFn: sizeSortingFn,
},
...(showLiquidationPrice
? [
{
...PRICE_META,
cell: ({ row }: { row: Row<AccountBalanceRow> }) => (
<Price
type={row.original.type}
amount={row.original.amount.toNumber()}
denom={row.original.denom}
/>
),
},
]
: []),
...(showLiquidationPrice
? [
{
...LIQ_META,
enableSorting: false,
cell: ({ row }: { row: Row<AccountBalanceRow> }) => (
<LiqPrice
denom={row.original.denom}
computeLiquidationPrice={computeLiquidationPrice}
type={row.original.type}
amount={row.original.amount.toNumber()}
/>
),
},
]
: []),
{
...APY_META,
cell: ({ row }) => (
@@ -58,5 +98,5 @@ export default function useAccountBalancesColumns() {
),
},
]
}, [markets])
}, [computeLiquidationPrice, markets, showLiquidationPrice])
}
@@ -19,11 +19,19 @@ interface Props {
borrowingData: BorrowMarketTableData[]
hideCard?: boolean
tableBodyClassName?: string
showLiquidationPrice?: boolean
}
export default function AccountBalancesTable(props: Props) {
const [searchParams] = useSearchParams()
const { account, lendingData, borrowingData, tableBodyClassName, hideCard } = props
const {
account,
lendingData,
borrowingData,
tableBodyClassName,
hideCard,
showLiquidationPrice,
} = props
const currentAccount = useCurrentAccount()
const navigate = useNavigate()
const { pathname } = useLocation()
@@ -37,7 +45,7 @@ export default function AccountBalancesTable(props: Props) {
isHls: props.isHls,
})
const columns = useAccountBalancesColumns()
const columns = useAccountBalancesColumns(account, showLiquidationPrice)
if (accountBalanceData.length === 0)
return (
@@ -15,9 +15,9 @@ export const depositCapSortingFn = (
}
interface Props {
account: HLSAccountWithStrategy
depositCap: DepositCap
}
export default function Name(props: Props) {
return <DepositCapCell depositCap={props.account.strategy.depositCap} />
export default function DepositCap(props: Props) {
return <DepositCapCell depositCap={props.depositCap} />
}
@@ -6,6 +6,7 @@ import ApyRange, {
APY_RANGE_META,
apyRangeSortingFn,
} from 'components/HLS/Staking/Table/Columns/ApyRange'
import DepositCap, { CAP_META } from 'components/HLS/Staking/Table/Columns/DepositCap'
import MaxLeverage, { MAX_LEV_META } from 'components/HLS/Staking/Table/Columns/MaxLeverage'
import MaxLTV, { LTV_MAX_META } from 'components/HLS/Staking/Table/Columns/MaxLTV'
import Name, { NAME_META } from 'components/HLS/Staking/Table/Columns/Name'
@@ -31,6 +32,10 @@ export default function useAvailableColumns(props: Props) {
<MaxLTV strategy={row.original as HLSStrategy} isLoading={props.isLoading} />
),
},
{
...CAP_META,
cell: ({ row }) => <DepositCap depositCap={row.original.depositCap} />,
},
{
...APY_RANGE_META,
cell: ({ row }) => (
@@ -57,7 +57,7 @@ export default function useDepositedColumns(props: Props) {
},
{
...CAP_META,
cell: ({ row }) => <DepositCap account={row.original} />,
cell: ({ row }) => <DepositCap depositCap={row.original.strategy.depositCap} />,
sortingFn: depositCapSortingFn,
},
{
@@ -29,6 +29,7 @@ function Content(props: Props) {
account={account}
borrowingData={borrowAssets}
lendingData={lendingAssets}
showLiquidationPrice
hideCard
/>
</Skeleton>
@@ -55,7 +56,10 @@ function Skeleton(props: SkeletonProps) {
{props.children ? (
props.children
) : (
<TableSkeleton labels={['Asset', 'Value', 'Size', 'APY']} rowCount={3} />
<TableSkeleton
labels={['Asset', 'Value', 'Size', 'Price', 'Liquidation Price', 'APY']}
rowCount={3}
/>
)}
</Card>
</div>
@@ -23,6 +23,7 @@ export default function AccountDetailsCard() {
borrowingData={borrowAssetsData}
lendingData={lendingAssetsData}
tableBodyClassName='gradient-card-content'
showLiquidationPrice
/>
)
}
@@ -1,6 +1,5 @@
import classNames from 'classnames'
import debounce from 'lodash.debounce'
import React, { useEffect, useMemo, useState } from 'react'
import React, { useMemo } from 'react'
import ActionButton from 'components/Button/ActionButton'
import { CircularProgress } from 'components/CircularProgress'
@@ -11,6 +10,7 @@ import { ChevronDown } from 'components/Icons'
import Text from 'components/Text'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLiquidationPrice from 'hooks/useLiquidationPrice'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrice from 'hooks/usePrice'
import useSwapFee from 'hooks/useSwapFee'
@@ -59,24 +59,14 @@ export default function TradeSummary(props: Props) {
const sellAssetPrice = usePrice(sellAsset.denom)
const swapFee = useSwapFee(route.map((r) => r.pool_id))
const [showSummary, setShowSummary] = useToggle()
const [liquidationPrice, setLiquidationPrice] = useState<number | null>(null)
const [isUpdatingLiquidationPrice, setIsUpdatingLiquidationPrice] = useState(false)
const debouncedSetLiqPrice = useMemo(
() => debounce(setLiquidationPrice, 1000, { leading: false }),
[],
const { liquidationPrice, isUpdatingLiquidationPrice } = useLiquidationPrice(
props.liquidationPrice,
)
const minReceive = useMemo(() => {
return buyAmount.times(1 - swapFee).times(1 - slippage)
}, [buyAmount, slippage, swapFee])
useEffect(() => {
setIsUpdatingLiquidationPrice(true)
debouncedSetLiqPrice(props.liquidationPrice)
}, [debouncedSetLiqPrice, props.liquidationPrice])
useEffect(() => setIsUpdatingLiquidationPrice(false), [liquidationPrice])
const swapFeeValue = useMemo(() => {
return sellAssetPrice.times(swapFee).times(sellAmount)
}, [sellAmount, sellAssetPrice, swapFee])
@@ -210,7 +210,7 @@ export default function SwapForm(props: Props) {
)
const liquidationPrice = useMemo(
() => computeLiquidationPrice(props.buyAsset.denom),
() => computeLiquidationPrice(props.buyAsset.denom, 'asset'),
[computeLiquidationPrice, props.buyAsset.denom],
)