token table overview updates
This commit is contained in:
parent
bedc864717
commit
b1ef1563fa
|
@ -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}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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')}
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue