MP-3414: support unborrowable assets (#479)

* MP-3414: support unborrowable assets

* tidy: refactor

* MP-3414: added Rate Tooltip to BalancesTable
This commit is contained in:
Linkie Link 2023-09-18 16:24:16 +02:00 committed by GitHub
parent dc9a757224
commit b04f244d3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 132 additions and 52 deletions

View File

@ -13,6 +13,7 @@ import { useLocation, useNavigate } from 'react-router-dom'
import { getAmountChangeColor } from 'components/Account/AccountBalancesTable/functions'
import useAccountBalanceData from 'components/Account/AccountBalancesTable/useAccountBalanceData'
import AccountFundFullPage from 'components/Account/AccountFund/AccountFundFullPage'
import AssetRate from 'components/Asset/AssetRate'
import ActionButton from 'components/Button/ActionButton'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
@ -21,8 +22,10 @@ import Text from 'components/Text'
import { ASSETS } from 'constants/assets'
import { ORACLE_DENOM } from 'constants/oracle'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useMarketAssets from 'hooks/useMarketAssets'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { getAssetByDenom } from 'utils/assets'
import { demagnify, formatAmountToPrecision } from 'utils/formatters'
import { getPage, getRoute } from 'utils/route'
@ -36,6 +39,7 @@ interface Props {
export default function Index(props: Props) {
const { account, lendingData, borrowingData, tableBodyClassName } = props
const { data: markets } = useMarketAssets()
const currentAccount = useCurrentAccount()
const navigate = useNavigate()
const { pathname } = useLocation()
@ -126,18 +130,20 @@ export default function Index(props: Props) {
cell: ({ row }) => {
if (row.original.type === 'deposits')
return <span className='w-full text-xs text-center'>&ndash;</span>
const isEnabled = markets.find(byDenom(row.original.denom))?.borrowEnabled ?? false
return (
<FormattedNumber
className='text-xs'
amount={row.original.apy}
options={{ maxDecimals: 2, minDecimals: 2, suffix: '%' }}
animate
<AssetRate
className='justify-end text-xs'
rate={row.original.apy}
isEnabled={row.original.type !== 'lending' || isEnabled}
type='apy'
orientation='ltr'
/>
)
},
},
],
[baseCurrency.decimals],
[baseCurrency.decimals, markets],
)
const table = useReactTable({

View File

@ -1,4 +1,4 @@
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import Text from 'components/Text'

View File

@ -0,0 +1,53 @@
import classNames from 'classnames'
import { FormattedNumber } from 'components/FormattedNumber'
import { InfoCircle } from 'components/Icons'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
interface Props {
rate: number
isEnabled: boolean
type: 'apy' | 'apr'
orientation: 'ltr' | 'rtl'
className?: string
suffix?: boolean
}
interface TooltipProps {
orientation: Props['orientation']
}
function RateTooltip(props: TooltipProps) {
return (
<Tooltip
content='This asset cannot be borrowed, and thus does not currently generate yield when lending.'
type='info'
className={props.orientation === 'ltr' ? 'mr-1' : 'ml-1'}
>
<InfoCircle className='w-4 h-4' />
</Tooltip>
)
}
export default function AssetRate(props: Props) {
const { rate, isEnabled, type, orientation, className } = props
const suffix = type === 'apy' ? 'APY' : 'APR'
if (!isEnabled)
return (
<Text className={classNames('flex items-center', className)}>
{orientation === 'ltr' && <RateTooltip orientation='ltr' />}N/A
{orientation === 'rtl' && <RateTooltip orientation='rtl' />}
</Text>
)
return (
<FormattedNumber
amount={rate}
className={className}
options={{ suffix: props.suffix ? `% ${suffix}` : '%', maxDecimals: 2 }}
animate
/>
)
}

View File

@ -2,7 +2,7 @@ import { ColumnDef, Row, Table } from '@tanstack/react-table'
import { useCallback, useMemo } from 'react'
import AmountAndValue from 'components/AmountAndValue'
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import BorrowActionButtons from 'components/Borrow/BorrowActionButtons'
import { FormattedNumber } from 'components/FormattedNumber'
import { ChevronDown, ChevronUp } from 'components/Icons'
@ -11,8 +11,8 @@ import AssetListTable from 'components/MarketAssetTable'
import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow'
import MarketDetails from 'components/MarketAssetTable/MarketDetails'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { getEnabledMarketAssets } from 'utils/assets'
import { BN_ZERO } from 'constants/math'
import { getEnabledMarketAssets } from 'utils/assets'
interface Props {
title: string

View File

@ -1,4 +1,4 @@
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import { getAssetByDenom } from 'utils/assets'
interface Props {

View File

@ -2,14 +2,14 @@ import { ColumnDef, Row, Table } from '@tanstack/react-table'
import { useCallback, useMemo } from 'react'
import AmountAndValue from 'components/AmountAndValue'
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import AssetRate from 'components/Asset/AssetRate'
import LendingActionButtons from 'components/Earn/Lend/LendingActionButtons'
import { FormattedNumber } from 'components/FormattedNumber'
import { ChevronDown, ChevronUp } from 'components/Icons'
import AssetListTable from 'components/MarketAssetTable'
import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow'
import MarketDetails from 'components/MarketAssetTable/MarketDetails'
import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { BN_ZERO } from 'constants/math'
import { convertLiquidityRateToAPR } from 'utils/formatters'
@ -83,16 +83,13 @@ export default function LendingMarketsTable(props: Props) {
accessorKey: 'marketLiquidityRate',
header: 'APR',
cell: ({ row }) => {
if (!row.original.borrowEnabled) {
return <Text>-</Text>
}
return (
<FormattedNumber
amount={convertLiquidityRateToAPR(row.original.marketLiquidityRate)}
options={{ minDecimals: 2, maxDecimals: 2, suffix: '%' }}
className='text-xs'
animate
<AssetRate
rate={convertLiquidityRateToAPR(row.original.marketLiquidityRate)}
isEnabled={row.original.borrowEnabled}
className='justify-end text-xs'
type='apr'
orientation='ltr'
/>
)
},

View File

@ -1,10 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
<g clip-path="url(#clip0_2324_198820)">
<path d="M7.99967 10.6673V8.00065M7.99967 5.33398H8.00634M14.6663 8.00065C14.6663 11.6826 11.6816 14.6673 7.99967 14.6673C4.31778 14.6673 1.33301 11.6826 1.33301 8.00065C1.33301 4.31875 4.31778 1.33398 7.99967 1.33398C11.6816 1.33398 14.6663 4.31875 14.6663 8.00065Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_2324_198820">
<rect width="16" height="16" fill="currentColor"/>
</clipPath>
</defs>
</svg>
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.99992 10.6673V8.00065M7.99992 5.33398H8.00659M14.6666 8.00065C14.6666 11.6826 11.6818 14.6673 7.99992 14.6673C4.31802 14.6673 1.33325 11.6826 1.33325 8.00065C1.33325 4.31875 4.31802 1.33398 7.99992 1.33398C11.6818 1.33398 14.6666 4.31875 14.6666 8.00065Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 440 B

View File

@ -17,6 +17,7 @@ interface Props {
modalClassName?: string
onClose: () => void
hideTxLoader?: boolean
dialogId?: string
}
export default function Modal(props: Props) {
@ -57,6 +58,7 @@ export default function Modal(props: Props) {
'backdrop:bg-black/50 backdrop:backdrop-blur-sm',
modalClassName,
)}
id={props.dialogId ? props.dialogId : 'modal'}
>
<Card
className={classNames(

View File

@ -1,7 +1,7 @@
import { useCallback, useMemo } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import AssetBalanceRow from 'components/AssetBalanceRow'
import AssetBalanceRow from 'components/Asset/AssetBalanceRow'
import { ArrowRight } from 'components/Icons'
import AccountDeleteAlertDialog from 'components/Modals/Account/AccountDeleteAlertDialog'
import Text from 'components/Text'

View File

@ -1,7 +1,7 @@
import { useCallback, useState } from 'react'
import CurrentAccountSummary from 'components/Account/CurrentAccountSummary'
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
import Card from 'components/Card'
import Divider from 'components/Divider'

View File

@ -12,6 +12,7 @@ import { useEffect, useMemo, useState } from 'react'
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
import useAssetTableColumns from 'components/Modals/AssetsSelect/useAssetTableColumns'
import Text from 'components/Text'
import useMarketAssets from 'hooks/useMarketAssets'
import useStore from 'store'
import { byDenom } from 'utils/array'
@ -22,6 +23,7 @@ interface Props {
}
export default function AssetSelectTable(props: Props) {
const { data: markets } = useMarketAssets()
const defaultSelected = useMemo(() => {
const assets = props.assets as BorrowAsset[]
return assets.reduce(
@ -44,9 +46,10 @@ export default function AssetSelectTable(props: Props) {
return {
asset,
balance: balancesForAsset?.amount ?? '0',
market: markets.find((market) => market.denom === asset.denom),
}
})
}, [balances, props.assets])
}, [balances, props.assets, markets])
const table = useReactTable({
data: tableData,
@ -79,7 +82,7 @@ export default function AssetSelectTable(props: Props) {
<thead className='border-b border-white/5'>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header, index) => {
{headerGroup.headers.map((header) => {
return (
<th
key={header.id}

View File

@ -1,7 +1,8 @@
import { ColumnDef } from '@tanstack/react-table'
import React from 'react'
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import AssetRate from 'components/Asset/AssetRate'
import Checkbox from 'components/Checkbox'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
@ -9,6 +10,12 @@ import Text from 'components/Text'
import { BNCoin } from 'types/classes/BNCoin'
import { getAssetByDenom } from 'utils/assets'
import { demagnify, formatPercent } from 'utils/formatters'
import { convertAprToApy } from 'utils/parsers'
function showBorrowRate(data: AssetTableRow[]) {
const assetData = data.length && (data[0].asset as BorrowAsset)
return !!(assetData && assetData?.borrowRate)
}
export default function useAssetTableColumns() {
return React.useMemo<ColumnDef<AssetTableRow>[]>(
@ -19,6 +26,9 @@ export default function useAssetTableColumns() {
id: 'symbol',
cell: ({ row }) => {
const asset = getAssetByDenom(row.original.asset.denom) as Asset
const market = row.original.market
const borrowAsset = row.original.asset as BorrowAsset
const showRate = !borrowAsset?.borrowRate
return (
<div className='flex items-center'>
<Checkbox checked={row.getIsSelected()} onChange={row.getToggleSelectedHandler()} />
@ -27,7 +37,18 @@ export default function useAssetTableColumns() {
<Text size='sm' className='mb-0.5 text-white'>
{asset.symbol}
</Text>
<Text size='xs'>{asset.name}</Text>
{showRate && market ? (
<AssetRate
rate={convertAprToApy(market.borrowRate * 100, 365)}
isEnabled={market.borrowEnabled}
className='text-xs'
type='apy'
orientation='rtl'
suffix
/>
) : (
<Text size='xs'>{asset.name}</Text>
)}
</div>
</div>
)
@ -37,8 +58,7 @@ export default function useAssetTableColumns() {
id: 'details',
header: (data) => {
const tableData = data.table.options.data as AssetTableRow[]
const assetData = tableData.length && (tableData[0].asset as BorrowAsset)
if (assetData && assetData?.borrowRate) return 'Borrow Rate'
if (showBorrowRate(tableData)) return 'Borrow Rate'
return 'Balance'
},
cell: ({ row }) => {

View File

@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'
import { useCallback, useEffect, useMemo, useState } from 'react'
import AccountSummary from 'components/Account/AccountSummary'
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
import Card from 'components/Card'
import DisplayCurrency from 'components/DisplayCurrency'

View File

@ -1,7 +1,7 @@
import classNames from 'classnames'
import { useCallback, useMemo, useState } from 'react'
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
import { ArrowCircle, Enter } from 'components/Icons'
import Modal from 'components/Modal'

View File

@ -24,6 +24,7 @@ export default function WalletAssetsModal() {
onClose={onClose}
modalClassName='max-w-modal-xs'
headerClassName='bg-white/10 border-b-white/5 border-b items-center p-4'
dialogId='wallet-assets-modal'
>
<WalletAssetsModalContent onChangeDenoms={setSelectedDenoms} />
<div className='flex w-full p-4'>

View File

@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import AssetBalanceRow from 'components/AssetBalanceRow'
import AssetBalanceRow from 'components/Asset/AssetBalanceRow'
import Button from 'components/Button'
import DisplayCurrency from 'components/DisplayCurrency'
import Divider from 'components/Divider'
@ -10,6 +10,7 @@ import Text from 'components/Text'
import { ASSETS } from 'constants/assets'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { DISPLAY_CURRENCY_KEY } from 'constants/localStore'
import { ORACLE_DENOM } from 'constants/oracle'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
@ -20,7 +21,6 @@ import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { defaultFee } from 'utils/constants'
import { formatAmountWithSymbol, getCoinValue } from 'utils/formatters'
import { ORACLE_DENOM } from 'constants/oracle'
const renderIncentives = (unclaimedRewards: BNCoin[]) => {
if (unclaimedRewards.length === 0)
@ -48,10 +48,6 @@ export default function RewardsCenter() {
const [isConfirming, setIsConfirming] = useState(false)
const [estimatedFee, setEstimatedFee] = useState(defaultFee)
const [showRewardsCenter, setShowRewardsCenter] = useToggle()
const [displayCurrency] = useLocalStorage<string>(
DISPLAY_CURRENCY_KEY,
DEFAULT_SETTINGS.displayCurrency,
)
const claimRewards = useStore((s) => s.claimRewards)
const { data: prices } = usePrices()
const { data: unclaimedRewards } = useUnclaimedRewards()

View File

@ -1,6 +1,6 @@
import classNames from 'classnames'
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import DisplayCurrency from 'components/DisplayCurrency'
import { ChevronDown, ChevronRight } from 'components/Icons'
import Text from 'components/Text'

View File

@ -1,7 +1,7 @@
import BigNumber from 'bignumber.js'
import classNames from 'classnames'
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'

View File

@ -23,9 +23,12 @@ export type TooltipType = 'info' | 'warning' | 'error'
export const Tooltip = (props: Props) => {
const [reduceMotion] = useLocalStorage<boolean>(REDUCE_MOTION_KEY, DEFAULT_SETTINGS.reduceMotion)
const isInWalletAssetModal = document.getElementById('wallet-assets-modal')
const isInModal = document.getElementById('modal')
return (
<Tippy
appendTo={() => document.querySelector('dialog[open]') ?? document.body}
appendTo={() => isInWalletAssetModal ?? isInModal ?? document.body}
interactive={props.interactive}
animation={false}
delay={[props.delay ?? 0, 0]}

View File

@ -1,4 +1,4 @@
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
interface Props {

View File

@ -1,4 +1,4 @@
import AssetImage from 'components/AssetImage'
import AssetImage from 'components/Asset/AssetImage'
import DisplayCurrency from 'components/DisplayCurrency'
import { StarFilled, StarOutlined } from 'components/Icons'
import Text from 'components/Text'

View File

@ -1,4 +1,5 @@
interface AssetTableRow {
balance?: string
asset: BorrowAsset | Asset
market?: Market
}