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:
parent
dc9a757224
commit
b04f244d3e
@ -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'>–</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({
|
||||
|
@ -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'
|
53
src/components/Asset/AssetRate.tsx
Normal file
53
src/components/Asset/AssetRate.tsx
Normal 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
|
||||
/>
|
||||
)
|
||||
}
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AssetImage from 'components/AssetImage'
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
|
||||
interface Props {
|
||||
|
@ -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'
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
@ -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 |
@ -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(
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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}
|
||||
|
@ -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 }) => {
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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'>
|
||||
|
@ -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()
|
||||
|
@ -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'
|
||||
|
@ -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'
|
||||
|
@ -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]}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import AssetImage from 'components/AssetImage'
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import Button from 'components/Button'
|
||||
|
||||
interface Props {
|
||||
|
@ -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'
|
||||
|
@ -1,4 +1,5 @@
|
||||
interface AssetTableRow {
|
||||
balance?: string
|
||||
asset: BorrowAsset | Asset
|
||||
market?: Market
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user