token table overview updates

This commit is contained in:
saml33 2024-05-23 13:51:07 +10:00
parent bedc864717
commit b1ef1563fa
8 changed files with 221 additions and 108 deletions

View File

@ -1,15 +1,15 @@
import Change from '@components/shared/Change' import Change from '@components/shared/Change'
import FormatNumericValue from '@components/shared/FormatNumericValue' import FormatNumericValue from '@components/shared/FormatNumericValue'
import TokenLogo from '@components/shared/TokenLogo'
import useListedMarketsWithMarketData, { import useListedMarketsWithMarketData, {
SerumMarketWithMarketData, SerumMarketWithMarketData,
} from 'hooks/useListedMarketsWithMarketData' } from 'hooks/useListedMarketsWithMarketData'
import useMangoGroup from 'hooks/useMangoGroup' import useMangoGroup from 'hooks/useMangoGroup'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useMemo } from 'react' import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { goToTokenPage } from '@components/stats/tokens/TokenOverviewTable' import { goToTokenPage } from '@components/stats/tokens/TokenOverviewTable'
import { import {
ArrowDownTrayIcon,
BoltIcon, BoltIcon,
ChevronRightIcon, ChevronRightIcon,
FaceFrownIcon, FaceFrownIcon,
@ -25,6 +25,13 @@ import mangoStore from '@store/mangoStore'
import { goToPerpMarketDetails } from '@components/stats/perps/PerpMarketDetailsTable' import { goToPerpMarketDetails } from '@components/stats/perps/PerpMarketDetailsTable'
import MarketLogos from '@components/trade/MarketLogos' import MarketLogos from '@components/trade/MarketLogos'
import { TOKEN_REDUCE_ONLY_OPTIONS } from 'utils/constants' import { TOKEN_REDUCE_ONLY_OPTIONS } from 'utils/constants'
import TableTokenName from '@components/shared/TableTokenName'
import { IconButton } from '@components/shared/Button'
import DepositWithdrawModal from '@components/modals/DepositWithdrawModal'
import useMangoAccount from 'hooks/useMangoAccount'
import { useWallet } from '@solana/wallet-adapter-react'
import CreateAccountModal from '@components/modals/CreateAccountModal'
import Tooltip from '@components/shared/Tooltip'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
export type BankWithMarketData = { export type BankWithMarketData = {
@ -39,6 +46,8 @@ const RecentGainersLosers = () => {
const { t } = useTranslation(['common', 'explore', 'trade']) const { t } = useTranslation(['common', 'explore', 'trade'])
const router = useRouter() const router = useRouter()
const { group } = useMangoGroup() const { group } = useMangoGroup()
const { mangoAccountAddress } = useMangoAccount()
const { connected } = useWallet()
const { banks } = useBanks() const { banks } = useBanks()
const { const {
serumMarketsWithData, serumMarketsWithData,
@ -46,6 +55,8 @@ const RecentGainersLosers = () => {
isLoading: loadingSerumMarkets, isLoading: loadingSerumMarkets,
} = useListedMarketsWithMarketData() } = useListedMarketsWithMarketData()
const groupLoaded = mangoStore((s) => s.groupLoaded) const groupLoaded = mangoStore((s) => s.groupLoaded)
const [showDepositModal, setShowDepositModal] = useState('')
const [showCreateAccountModal, setShowCreateAccountModal] = useState(false)
const banksWithMarketData = useMemo(() => { const banksWithMarketData = useMemo(() => {
if (!banks.length || !group || !serumMarketsWithData.length) return [] if (!banks.length || !group || !serumMarketsWithData.length) return []
@ -134,6 +145,14 @@ const RecentGainersLosers = () => {
return [gainers, losers] return [gainers, losers]
}, [banksWithMarketData, perpMarketsWithData]) }, [banksWithMarketData, perpMarketsWithData])
const handleDepositModal = (token: string) => {
if (mangoAccountAddress) {
setShowDepositModal(token)
} else {
setShowCreateAccountModal(true)
}
}
return ( return (
<> <>
<div className="grid grid-cols-12 gap-4 px-4 md:px-6"> <div className="grid grid-cols-12 gap-4 px-4 md:px-6">
@ -152,37 +171,36 @@ const RecentGainersLosers = () => {
{groupLoaded ? ( {groupLoaded ? (
<div className="border-t border-th-bkg-3"> <div className="border-t border-th-bkg-3">
{newlyListed.map((token) => { {newlyListed.map((token) => {
const mintInfo = newlyListedMintInfo.find(
(info) => info.tokenIndex === token.tokenIndex,
)
let timeSinceListing = ''
if (mintInfo) {
timeSinceListing = dayjs().to(
mintInfo.registrationTime.toNumber() * 1000,
)
}
return ( return (
<div <div className="relative" key={token.tokenIndex}>
className="default-transition flex h-16 cursor-pointer items-center justify-between border-b border-th-bkg-3 px-4 md:hover:bg-th-bkg-2" <div
key={token.tokenIndex} className="default-transition flex h-16 cursor-pointer items-center justify-between border-b border-th-bkg-3 px-4 md:hover:bg-th-bkg-2"
onClick={() => onClick={() =>
goToTokenPage(token.name.split(' ')[0], router) goToTokenPage(token.name.split(' ')[0], router)
} }
> >
<div className="flex items-center"> <div className="flex items-center">
<TokenLogo bank={token} showRewardsLogo /> <TableTokenName
<p className="ml-3 font-body text-th-fgd-2"> bank={token}
{token.name} symbol={token.name}
</p> showLeverage
</div> hideReduceDesc
<div className="flex items-center"> />
<div className="mr-3">
<span className="text-th-fgd-3">
{timeSinceListing}
</span>
</div> </div>
<ChevronRightIcon className="h-5 w-5 text-th-fgd-3" /> <ChevronRightIcon className="h-5 w-5 text-th-fgd-3" />
</div> </div>
{connected ? (
<div className="absolute right-12 top-4">
<Tooltip content={`${t('deposit')} ${token.name}`}>
<IconButton
onClick={() => handleDepositModal(token.name)}
size="small"
>
<ArrowDownTrayIcon className="h-4 w-4" />
</IconButton>
</Tooltip>
</div>
) : null}
</div> </div>
) )
})} })}
@ -219,18 +237,23 @@ const RecentGainersLosers = () => {
} }
onClick={onClick} onClick={onClick}
> >
<div className="flex items-center"> {bank ? (
{bank ? ( <div className="mr-3">
<div className="mr-3"> <TableTokenName
<TokenLogo bank={bank} showRewardsLogo /> bank={bank}
</div> symbol={bank.name}
) : ( showLeverage
hideReduceDesc
/>
</div>
) : (
<div className="flex items-center">
<MarketLogos market={gainer?.market} size="large" /> <MarketLogos market={gainer?.market} size="large" />
)} <p className="font-body text-th-fgd-2">
<p className="font-body text-th-fgd-2"> {gainer?.market?.name}
{bank?.name || gainer?.market?.name} </p>
</p> </div>
</div> )}
<div className="flex items-center"> <div className="flex items-center">
<div className="mr-3 flex flex-col items-end"> <div className="mr-3 flex flex-col items-end">
<span className="font-mono"> <span className="font-mono">
@ -282,16 +305,23 @@ const RecentGainersLosers = () => {
} }
onClick={onClick} onClick={onClick}
> >
<div className="flex items-center"> {bank ? (
{bank ? ( <div className="mr-3">
<TokenLogo bank={bank} showRewardsLogo /> <TableTokenName
) : ( bank={bank}
<MarketLogos market={loser?.market} /> symbol={bank.name}
)} showLeverage
<p className="ml-3 font-body text-th-fgd-2"> hideReduceDesc
{bank?.name || loser?.market?.name} />
</p> </div>
</div> ) : (
<div className="flex items-center">
<MarketLogos market={loser?.market} size="large" />
<p className="font-body text-th-fgd-2">
{loser?.market?.name}
</p>
</div>
)}
<div className="flex items-center"> <div className="flex items-center">
<div className="mr-3 flex flex-col items-end"> <div className="mr-3 flex flex-col items-end">
<span className="font-mono"> <span className="font-mono">
@ -316,6 +346,20 @@ const RecentGainersLosers = () => {
)} )}
</div> </div>
</div> </div>
{showDepositModal ? (
<DepositWithdrawModal
action="deposit"
isOpen={!!showDepositModal}
onClose={() => setShowDepositModal('')}
token={showDepositModal}
/>
) : null}
{showCreateAccountModal ? (
<CreateAccountModal
isOpen={showCreateAccountModal}
onClose={() => setShowCreateAccountModal(false)}
/>
) : null}
</> </>
) )
} }

View File

@ -20,7 +20,8 @@ import useBanks from 'hooks/useBanks'
import SheenLoader from '@components/shared/SheenLoader' import SheenLoader from '@components/shared/SheenLoader'
import { useViewport } from 'hooks/useViewport' import { useViewport } from 'hooks/useViewport'
import useLocalStorageState from 'hooks/useLocalStorageState' import useLocalStorageState from 'hooks/useLocalStorageState'
import { TOKEN_WATCHLIST_KEY } from 'utils/constants' import { TOKEN_REDUCE_ONLY_OPTIONS, TOKEN_WATCHLIST_KEY } from 'utils/constants'
import { DEFAULT_WATCHLIST } from './WatchlistButton'
export type BankWithMarketData = { export type BankWithMarketData = {
bank: Bank bank: Bank
@ -61,14 +62,31 @@ const sortTokens = (
return tokens.sort((a: BankWithMarketData, b: BankWithMarketData) => { return tokens.sort((a: BankWithMarketData, b: BankWithMarketData) => {
const aInWatchlist = watchlist.includes(a.bank.tokenIndex) const aInWatchlist = watchlist.includes(a.bank.tokenIndex)
const bInWatchlist = watchlist.includes(b.bank.tokenIndex) const bInWatchlist = watchlist.includes(b.bank.tokenIndex)
const aIsReduce = a.bank.reduceOnly === TOKEN_REDUCE_ONLY_OPTIONS.ENABLED
const bIsReduce = b.bank.reduceOnly === TOKEN_REDUCE_ONLY_OPTIONS.ENABLED
const aIsNoBorrow =
a.bank.reduceOnly === TOKEN_REDUCE_ONLY_OPTIONS.NO_BORROWS
const bIsNoBorrow =
b.bank.reduceOnly === TOKEN_REDUCE_ONLY_OPTIONS.NO_BORROWS
// Prioritize tokens in the watchlist // prioritize tokens in the watchlist
if (aInWatchlist && !bInWatchlist) { if (aInWatchlist && !bInWatchlist) {
return -1 // a should come before b return -1 // a should come before b
} else if (!aInWatchlist && bInWatchlist) { } else if (!aInWatchlist && bInWatchlist) {
return 1 // b should come before a return 1 // b should come before a
} }
if (!aIsReduce && bIsReduce) {
return -1 // a should come before b
} else if (aIsReduce && !bIsReduce) {
return 1 // b should come before a
}
if (!aIsNoBorrow && bIsNoBorrow) {
return -1 // a should come before b
} else if (aIsNoBorrow && !bIsNoBorrow) {
return 1 // b should come before a
}
let aValue: number | undefined let aValue: number | undefined
let bValue: number | undefined let bValue: number | undefined
if (sortByKey === 'change_24h') { if (sortByKey === 'change_24h') {
@ -94,7 +112,10 @@ const sortTokens = (
} }
const Spot = () => { const Spot = () => {
const [watchlist] = useLocalStorageState(TOKEN_WATCHLIST_KEY, []) const [watchlist] = useLocalStorageState(
TOKEN_WATCHLIST_KEY,
DEFAULT_WATCHLIST,
)
const { t } = useTranslation(['common', 'explore', 'trade']) const { t } = useTranslation(['common', 'explore', 'trade'])
const { group } = useMangoGroup() const { group } = useMangoGroup()
const { banks } = useBanks() const { banks } = useBanks()

View File

@ -4,11 +4,9 @@ import Change from '@components/shared/Change'
import FormatNumericValue from '@components/shared/FormatNumericValue' import FormatNumericValue from '@components/shared/FormatNumericValue'
import TokenLogo from '@components/shared/TokenLogo' import TokenLogo from '@components/shared/TokenLogo'
import { goToTokenPage } from '@components/stats/tokens/TokenOverviewTable' import { goToTokenPage } from '@components/stats/tokens/TokenOverviewTable'
import Decimal from 'decimal.js'
import useMangoGroup from 'hooks/useMangoGroup'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { numberCompacter } from 'utils/numbers' import { floorToDecimal, numberCompacter } from 'utils/numbers'
import { BankWithMarketData } from './Spot' import { BankWithMarketData } from './Spot'
import Tooltip from '@components/shared/Tooltip' import Tooltip from '@components/shared/Tooltip'
import SimpleAreaChart from '@components/shared/SimpleAreaChart' import SimpleAreaChart from '@components/shared/SimpleAreaChart'
@ -18,10 +16,10 @@ import TokenReduceOnlyDesc from '@components/shared/TokenReduceOnlyDesc'
import CollateralWeightDisplay from '@components/shared/CollateralWeightDisplay' import CollateralWeightDisplay from '@components/shared/CollateralWeightDisplay'
import WatchlistButton from './WatchlistButton' import WatchlistButton from './WatchlistButton'
import TableRatesDisplay from '@components/shared/TableRatesDisplay' import TableRatesDisplay from '@components/shared/TableRatesDisplay'
import { LeverageMaxDisplay } from './SpotTable'
const SpotCards = ({ tokens }: { tokens: BankWithMarketData[] }) => { const SpotCards = ({ tokens }: { tokens: BankWithMarketData[] }) => {
const { t } = useTranslation(['common', 'explore', 'trade']) const { t } = useTranslation(['common', 'explore', 'trade'])
const { group } = useMangoGroup()
const { theme } = useThemeWrapper() const { theme } = useThemeWrapper()
const router = useRouter() const router = useRouter()
return ( return (
@ -29,14 +27,6 @@ const SpotCards = ({ tokens }: { tokens: BankWithMarketData[] }) => {
{tokens.map((token) => { {tokens.map((token) => {
const { bank } = token const { bank } = token
const availableVaultBalance = group
? group.getTokenVaultBalanceByMintUi(bank.mint) -
bank.uiDeposits() * bank.minVaultToDepositsRatio
: 0
const available = Decimal.max(
0,
availableVaultBalance.toFixed(bank.mintDecimals),
).mul(bank.uiPrice)
const depositRate = bank.getDepositRateUi() const depositRate = bank.getDepositRateUi()
const borrowRate = bank.getBorrowRateUi() const borrowRate = bank.getBorrowRateUi()
const chartData = token?.market?.priceHistory?.length const chartData = token?.market?.priceHistory?.length
@ -49,6 +39,10 @@ const SpotCards = ({ tokens }: { tokens: BankWithMarketData[] }) => {
const change = token.market?.rollingChange || 0 const change = token.market?.rollingChange || 0
const weight = bank.scaledInitAssetWeight(bank.price)
const leverageFactor = 1 / (1 - weight.toNumber())
const leverageMax = floorToDecimal(leverageFactor, 1).toNumber()
return ( return (
<div <div
className="col-span-12 rounded-lg border border-th-bkg-3 p-6 md:col-span-6 xl:col-span-4 2xl:col-span-3" className="col-span-12 rounded-lg border border-th-bkg-3 p-6 md:col-span-6 xl:col-span-4 2xl:col-span-3"
@ -114,15 +108,8 @@ const SpotCards = ({ tokens }: { tokens: BankWithMarketData[] }) => {
</p> </p>
</div> </div>
<div> <div>
<Tooltip <p className="mb-1">{t('trade:max-leverage')}</p>
content={t('tooltip-available', { token: bank.name })} <LeverageMaxDisplay leverageMax={leverageMax} />
placement="top-start"
>
<p className="tooltip-underline mb-1">{t('available')}</p>
</Tooltip>
<span className="font-mono text-th-fgd-2">
<FormatNumericValue value={available} isUsd />
</span>
</div> </div>
<div> <div>
<Tooltip <Tooltip

View File

@ -10,7 +10,7 @@ import {
TrHead, TrHead,
} from '@components/shared/TableElements' } from '@components/shared/TableElements'
import FormatNumericValue from '@components/shared/FormatNumericValue' import FormatNumericValue from '@components/shared/FormatNumericValue'
import { numberCompacter } from 'utils/numbers' import { floorToDecimal, numberCompacter } from 'utils/numbers'
import SimpleAreaChart from '@components/shared/SimpleAreaChart' import SimpleAreaChart from '@components/shared/SimpleAreaChart'
import { Disclosure, Transition } from '@headlessui/react' import { Disclosure, Transition } from '@headlessui/react'
import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/20/solid' import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/20/solid'
@ -26,7 +26,6 @@ import TokenLogo from '@components/shared/TokenLogo'
import { goToTokenPage } from '@components/stats/tokens/TokenOverviewTable' import { goToTokenPage } from '@components/stats/tokens/TokenOverviewTable'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import Decimal from 'decimal.js' import Decimal from 'decimal.js'
import BankAmountWithValue from '@components/shared/BankAmountWithValue'
import { BankWithMarketData } from './Spot' import { BankWithMarketData } from './Spot'
import { SerumMarketWithMarketData } from 'hooks/useListedMarketsWithMarketData' import { SerumMarketWithMarketData } from 'hooks/useListedMarketsWithMarketData'
import Tooltip from '@components/shared/Tooltip' import Tooltip from '@components/shared/Tooltip'
@ -39,6 +38,9 @@ import useLocalStorageState from 'hooks/useLocalStorageState'
import { TOKEN_WATCHLIST_KEY } from 'utils/constants' import { TOKEN_WATCHLIST_KEY } from 'utils/constants'
import TableRatesDisplay from '@components/shared/TableRatesDisplay' import TableRatesDisplay from '@components/shared/TableRatesDisplay'
export const GRADIENT_TEXT =
'bg-gradient-to-bl from-yellow-500 to-red-500 bg-clip-text text-transparent'
type TableData = { type TableData = {
assetWeight: string assetWeight: string
available: Decimal available: Decimal
@ -55,6 +57,7 @@ type TableData = {
}[] }[]
volume: number volume: number
isUp: boolean isUp: boolean
leverageMax: number
} }
const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => { const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
@ -91,6 +94,7 @@ const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
let depositRate = 0 let depositRate = 0
let borrowRate = 0 let borrowRate = 0
let assetWeight = '0' let assetWeight = '0'
let leverageMax = 0
if (baseBank) { if (baseBank) {
availableVaultBalance = group availableVaultBalance = group
@ -107,6 +111,10 @@ const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
assetWeight = baseBank assetWeight = baseBank
.scaledInitAssetWeight(baseBank.price) .scaledInitAssetWeight(baseBank.price)
.toFixed(2) .toFixed(2)
const weight = baseBank.scaledInitAssetWeight(baseBank.price)
const leverageFactor = 1 / (1 - weight.toNumber())
leverageMax = floorToDecimal(leverageFactor, 1).toNumber()
} }
const isUp = const isUp =
@ -126,6 +134,7 @@ const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
tokenName, tokenName,
volume, volume,
isUp, isUp,
leverageMax,
} }
formatted.push(data) formatted.push(data)
} }
@ -188,15 +197,12 @@ const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
</Th> </Th>
<Th> <Th>
<div className="flex justify-end"> <div className="flex justify-end">
<Tooltip content={t('tooltip-available', { token: '' })}> <SortableColumnHeader
<SortableColumnHeader sortKey="leverageMax"
sortKey="availableValue" sort={() => requestSort('leverageMax')}
sort={() => requestSort('availableValue')} sortConfig={sortConfig}
sortConfig={sortConfig} title={t('trade:max-leverage')}
title={t('available')} />
titleClass="tooltip-underline"
/>
</Tooltip>
</div> </div>
</Th> </Th>
<Th> <Th>
@ -246,7 +252,6 @@ const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
}) })
.map((data) => { .map((data) => {
const { const {
available,
baseBank, baseBank,
borrowRate, borrowRate,
change, change,
@ -257,6 +262,7 @@ const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
tokenName, tokenName,
volume, volume,
isUp, isUp,
leverageMax,
} = data } = data
if (!baseBank) return null if (!baseBank) return null
@ -272,7 +278,7 @@ const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
</td> </td>
</tr> </tr>
<TrBody <TrBody
className="default-transition border-t-0 md:hover:cursor-pointer md:hover:bg-th-bkg-2" className="default-transition h-16 border-t-0 md:hover:cursor-pointer md:hover:bg-th-bkg-2"
onClick={() => onClick={() =>
goToTokenPage(tokenName.split(' ')[0], router) goToTokenPage(tokenName.split(' ')[0], router)
} }
@ -352,12 +358,7 @@ const SpotTable = ({ tokens }: { tokens: BankWithMarketData[] }) => {
</Td> </Td>
<Td> <Td>
<div className="flex flex-col text-right"> <div className="flex flex-col text-right">
<BankAmountWithValue <LeverageMaxDisplay leverageMax={leverageMax} />
amount={available}
bank={baseBank}
fixDecimals={false}
stacked
/>
</div> </div>
</Td> </Td>
<Td className="font-mono"> <Td className="font-mono">
@ -417,7 +418,6 @@ const MobileSpotItem = ({ data }: { data: TableData }) => {
const router = useRouter() const router = useRouter()
const { const {
available,
baseBank, baseBank,
borrowRate, borrowRate,
change, change,
@ -428,6 +428,7 @@ const MobileSpotItem = ({ data }: { data: TableData }) => {
tokenName, tokenName,
volume, volume,
isUp, isUp,
leverageMax,
} = data } = data
return ( return (
@ -521,12 +522,10 @@ const MobileSpotItem = ({ data }: { data: TableData }) => {
</p> </p>
</div> </div>
<div className="col-span-1"> <div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('available')}</p> <p className="text-xs text-th-fgd-3">
<BankAmountWithValue {t('trade:max-leverage')}
amount={available} </p>
bank={baseBank} <LeverageMaxDisplay leverageMax={leverageMax} />
fixDecimals={false}
/>
</div> </div>
<div className="col-span-1"> <div className="col-span-1">
<p className="text-xs text-th-fgd-3"> <p className="text-xs text-th-fgd-3">
@ -566,3 +565,24 @@ const MobileSpotItem = ({ data }: { data: TableData }) => {
</Disclosure> </Disclosure>
) )
} }
export const LeverageMaxDisplay = ({
leverageMax,
}: {
leverageMax: number | undefined
}) => {
return (
<span className="font-mono">
{leverageMax && leverageMax !== Infinity ? (
<span className={`${leverageMax >= 5 ? GRADIENT_TEXT : ''}`}>
{leverageMax < 2 && leverageMax > 1
? leverageMax.toFixed(1)
: leverageMax.toFixed()}
x
</span>
) : (
''
)}
</span>
)
}

View File

@ -3,6 +3,8 @@ import { TOKEN_WATCHLIST_KEY } from 'utils/constants'
import PinFill from '@components/icons/PinFill' import PinFill from '@components/icons/PinFill'
import PinOutline from '@components/icons/PinOutline' import PinOutline from '@components/icons/PinOutline'
export const DEFAULT_WATCHLIST = [4, 0]
const WatchlistButton = ({ const WatchlistButton = ({
tokenIndex, tokenIndex,
className, className,
@ -12,7 +14,7 @@ const WatchlistButton = ({
}) => { }) => {
const [watchlist, setWatchlist] = useLocalStorageState( const [watchlist, setWatchlist] = useLocalStorageState(
TOKEN_WATCHLIST_KEY, TOKEN_WATCHLIST_KEY,
[], DEFAULT_WATCHLIST,
) )
const toggleWatchlist = (tokenIndex: number) => { const toggleWatchlist = (tokenIndex: number) => {

View File

@ -2,7 +2,7 @@ import { Bank } from '@blockworks-foundation/mango-v4'
import TokenLogo from './TokenLogo' import TokenLogo from './TokenLogo'
import TokenReduceOnlyDesc from './TokenReduceOnlyDesc' import TokenReduceOnlyDesc from './TokenReduceOnlyDesc'
import { useVaultLimits } from '@components/swap/useVaultLimits' import { useVaultLimits } from '@components/swap/useVaultLimits'
import { ExclamationTriangleIcon } from '@heroicons/react/20/solid' import { Battery100Icon } from '@heroicons/react/20/solid'
import Tooltip from './Tooltip' import Tooltip from './Tooltip'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { floorToDecimal } from 'utils/numbers' import { floorToDecimal } from 'utils/numbers'
@ -12,10 +12,12 @@ const TableTokenName = ({
bank, bank,
symbol, symbol,
showLeverage, showLeverage,
hideReduceDesc,
}: { }: {
bank: Bank bank: Bank
symbol: string symbol: string
showLeverage?: boolean showLeverage?: boolean
hideReduceDesc?: boolean
}) => { }) => {
const { t } = useTranslation(['common', 'trade']) const { t } = useTranslation(['common', 'trade'])
const { vaultFull } = useVaultLimits(bank) const { vaultFull } = useVaultLimits(bank)
@ -31,8 +33,8 @@ const TableTokenName = ({
<div> <div>
<div className="flex items-center"> <div className="flex items-center">
<p className="font-body leading-none text-th-fgd-2">{symbol}</p> <p className="font-body leading-none text-th-fgd-2">{symbol}</p>
{showLeverage && leverageMax > 1 && leverageMax < Infinity ? ( {showLeverage && leverageMax < Infinity ? (
<div className="ml-1"> <div className="ml-1.5">
<Tooltip content={t('trade:max-leverage')}> <Tooltip content={t('trade:max-leverage')}>
<LeverageBadge leverage={leverageMax} /> <LeverageBadge leverage={leverageMax} />
</Tooltip> </Tooltip>
@ -40,11 +42,11 @@ const TableTokenName = ({
) : null} ) : null}
{vaultFull ? ( {vaultFull ? (
<Tooltip content={t('warning-deposits-full', { token: bank.name })}> <Tooltip content={t('warning-deposits-full', { token: bank.name })}>
<ExclamationTriangleIcon className="ml-1 h-4 w-4 text-th-warning" /> <Battery100Icon className="ml-1.5 h-5 w-5 text-th-warning" />
</Tooltip> </Tooltip>
) : null} ) : null}
</div> </div>
<TokenReduceOnlyDesc bank={bank} /> {hideReduceDesc ? null : <TokenReduceOnlyDesc bank={bank} />}
</div> </div>
</div> </div>
) )

View File

@ -25,6 +25,8 @@ import { useSortableData } from 'hooks/useSortableData'
import TableTokenName from '@components/shared/TableTokenName' import TableTokenName from '@components/shared/TableTokenName'
import CollateralWeightDisplay from '@components/shared/CollateralWeightDisplay' import CollateralWeightDisplay from '@components/shared/CollateralWeightDisplay'
import OracleProvider from '@components/shared/OracleProvider' import OracleProvider from '@components/shared/OracleProvider'
import { floorToDecimal } from 'utils/numbers'
import { LeverageMaxDisplay } from '@components/explore/SpotTable'
const TokenDetailsTable = () => { const TokenDetailsTable = () => {
const { t } = useTranslation([ const { t } = useTranslation([
@ -56,6 +58,9 @@ const TokenDetailsTable = () => {
const [oracleProvider, oracleLinkPath] = getOracleProvider(bank) const [oracleProvider, oracleLinkPath] = getOracleProvider(bank)
const symbol = bank.name const symbol = bank.name
const collateralFeeRate = bank.collateralFeePerDay * 100 const collateralFeeRate = bank.collateralFeePerDay * 100
const weight = bank.scaledInitAssetWeight(bank.price)
const leverageFactor = 1 / (1 - weight.toNumber())
const leverageMax = floorToDecimal(leverageFactor, 1).toNumber()
const data = { const data = {
bank, bank,
@ -69,6 +74,7 @@ const TokenDetailsTable = () => {
oracleProvider, oracleProvider,
symbol, symbol,
collateralFeeRate, collateralFeeRate,
leverageMax,
} }
formatted.push(data) formatted.push(data)
} }
@ -111,6 +117,16 @@ const TokenDetailsTable = () => {
</Tooltip> </Tooltip>
</div> </div>
</Th> </Th>
<Th>
<div className="flex justify-end">
<SortableColumnHeader
sortKey="leverageMax"
sort={() => requestSort('leverageMax')}
sortConfig={sortConfig}
title={t('trade:max-leverage')}
/>
</div>
</Th>
<Th> <Th>
<div className="flex justify-end"> <div className="flex justify-end">
<Tooltip content={t('tooltip-borrow-fee')}> <Tooltip content={t('tooltip-borrow-fee')}>
@ -204,6 +220,7 @@ const TokenDetailsTable = () => {
loanOriginationFee, loanOriginationFee,
symbol, symbol,
collateralFeeRate, collateralFeeRate,
leverageMax,
} = data } = data
return ( return (
@ -226,6 +243,11 @@ const TokenDetailsTable = () => {
<p>{initLiabWeight.toFixed(2)}x</p> <p>{initLiabWeight.toFixed(2)}x</p>
</div> </div>
</Td> </Td>
<Td>
<div className="flex flex-col text-right">
<LeverageMaxDisplay leverageMax={leverageMax} />
</div>
</Td>
<Td> <Td>
<p className="text-right"> <p className="text-right">
{loanOriginationFee.toFixed(2)}% {loanOriginationFee.toFixed(2)}%
@ -268,6 +290,7 @@ const TokenDetailsTable = () => {
liquidationFee, liquidationFee,
loanOriginationFee, loanOriginationFee,
collateralFeeRate, collateralFeeRate,
leverageMax,
} = data } = data
return ( return (
<Disclosure key={bank.name}> <Disclosure key={bank.name}>
@ -313,6 +336,12 @@ const TokenDetailsTable = () => {
<span>{initLiabWeight.toFixed(2)}x</span> <span>{initLiabWeight.toFixed(2)}x</span>
</div> </div>
</div> </div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">
{t('trade:max-leverage')}
</p>
<LeverageMaxDisplay leverageMax={leverageMax} />
</div>
<div className="col-span-1"> <div className="col-span-1">
<Tooltip <Tooltip
content={t('tooltip-borrow-fee')} content={t('tooltip-borrow-fee')}

View File

@ -41,6 +41,7 @@ import { TOKEN_REDUCE_ONLY_OPTIONS } from 'utils/constants'
import { isBankVisibleForUser } from 'utils/bank' import { isBankVisibleForUser } from 'utils/bank'
import Decimal from 'decimal.js' import Decimal from 'decimal.js'
import useMangoAccount from 'hooks/useMangoAccount' import useMangoAccount from 'hooks/useMangoAccount'
import { GRADIENT_TEXT } from '@components/explore/SpotTable'
type Currencies = { type Currencies = {
[key: string]: string [key: string]: string
@ -526,8 +527,15 @@ export default MarketSelectDropdown
export const LeverageBadge = ({ leverage }: { leverage: number }) => { export const LeverageBadge = ({ leverage }: { leverage: number }) => {
return ( return (
<div className="rounded bg-th-bkg-3 px-1 py-0.5 font-mono text-xxs leading-none text-th-fgd-2"> <div
<span>{leverage < 2 ? leverage.toFixed(1) : leverage.toFixed()}x</span> className={`rounded bg-th-bkg-3 px-1 py-0.5 font-mono text-xs leading-none`}
>
<span className={leverage >= 5 ? GRADIENT_TEXT : 'text-th-fgd-3'}>
{leverage > 1 && leverage < 2
? leverage.toFixed(1)
: leverage.toFixed()}
x
</span>
</div> </div>
) )
} }