handle perp markets
This commit is contained in:
parent
d88f64cc11
commit
a3e21ec0d0
|
@ -20,6 +20,7 @@ import useAccountPerformanceData from 'hooks/useAccountPerformanceData'
|
|||
import HealthContributions from './HealthContributions'
|
||||
import { PerformanceDataItem } from 'types'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
|
||||
const TABS = ['account-value', 'account:assets-liabilities']
|
||||
|
||||
|
@ -171,16 +172,17 @@ const AccountView = ({
|
|||
handleViewChange: (view: ViewToShow) => void
|
||||
}) => {
|
||||
const router = useRouter()
|
||||
const { connected } = useWallet()
|
||||
const { address } = router.query
|
||||
const { performanceData } = useAccountPerformanceData()
|
||||
|
||||
const handleHideChart = useCallback(() => {
|
||||
if (address) {
|
||||
router.push(`/?address=${address}`)
|
||||
if (address && !connected) {
|
||||
router.push(`/?address=${address}`, undefined, { shallow: true })
|
||||
} else {
|
||||
router.push('/')
|
||||
router.push('/', undefined, { shallow: true })
|
||||
}
|
||||
}, [router])
|
||||
}, [address, router, connected])
|
||||
|
||||
switch (view) {
|
||||
case 'account-value':
|
||||
|
|
|
@ -15,7 +15,7 @@ import MarketLogos from '@components/trade/MarketLogos'
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import TokensHealthTable from './TokensHealthTable'
|
||||
import MarketsHealthTable from './MarketsHealthTable'
|
||||
import { HealthContribution } from 'types'
|
||||
import { HealthContribution, PerpMarketContribution } from 'types'
|
||||
|
||||
const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
||||
const { t } = useTranslation(['common', 'account', 'trade'])
|
||||
|
@ -30,21 +30,83 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
|
||||
const [initHealthContributions, maintHealthContributions] = useMemo(() => {
|
||||
if (!group || !mangoAccount) return [[], []]
|
||||
const init = mangoAccount
|
||||
.getHealthContributionPerAssetUi(group, HealthType.init)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
contribution: Math.abs(item.contribution),
|
||||
isAsset: item.contribution > 0 ? true : false,
|
||||
}))
|
||||
const maint = mangoAccount
|
||||
.getHealthContributionPerAssetUi(group, HealthType.maint)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
contribution: Math.abs(item.contribution),
|
||||
isAsset: item.contribution > 0 ? true : false,
|
||||
}))
|
||||
return [init, maint]
|
||||
const initAssets = mangoAccount.getHealthContributionPerAssetUi(
|
||||
group,
|
||||
HealthType.init
|
||||
)
|
||||
const initContributions = []
|
||||
for (const item of initAssets) {
|
||||
const contribution = item.contribution
|
||||
if (item.asset === 'USDC') {
|
||||
const hasPerp =
|
||||
!!item.contributionDetails?.perpMarketContributions.find(
|
||||
(perp: PerpMarketContribution) => Math.abs(perp.contributionUi) > 0
|
||||
)
|
||||
initContributions.push({
|
||||
hasPerp: hasPerp,
|
||||
isAsset: contribution > 0 ? true : false,
|
||||
...item,
|
||||
})
|
||||
if (item.contributionDetails) {
|
||||
for (const perpMarket of item.contributionDetails
|
||||
.perpMarketContributions) {
|
||||
const contribution = Math.abs(perpMarket.contributionUi)
|
||||
if (contribution > 0) {
|
||||
initContributions.push({
|
||||
asset: perpMarket.market,
|
||||
contribution: contribution,
|
||||
isAsset: perpMarket.contributionUi > 0 ? true : false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
initContributions.push({
|
||||
isAsset: contribution > 0 ? true : false,
|
||||
...item,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const maintAssets = mangoAccount.getHealthContributionPerAssetUi(
|
||||
group,
|
||||
HealthType.maint
|
||||
)
|
||||
const maintContributions = []
|
||||
for (const item of maintAssets) {
|
||||
const contribution = item.contribution
|
||||
if (item.asset === 'USDC') {
|
||||
const hasPerp =
|
||||
!!item.contributionDetails?.perpMarketContributions.find(
|
||||
(perp: PerpMarketContribution) => Math.abs(perp.contributionUi) > 0
|
||||
)
|
||||
maintContributions.push({
|
||||
hasPerp: hasPerp,
|
||||
isAsset: contribution > 0 ? true : false,
|
||||
...item,
|
||||
})
|
||||
if (item.contributionDetails) {
|
||||
for (const perpMarket of item.contributionDetails
|
||||
.perpMarketContributions) {
|
||||
const contribution = Math.abs(perpMarket.contributionUi)
|
||||
if (contribution > 0) {
|
||||
maintContributions.push({
|
||||
asset: perpMarket.market,
|
||||
contribution: contribution,
|
||||
isAsset: perpMarket.contributionUi > 0 ? true : false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
maintContributions.push({
|
||||
isAsset: contribution > 0 ? true : false,
|
||||
...item,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return [initContributions, maintContributions]
|
||||
}, [group, mangoAccount])
|
||||
|
||||
const [initHealthMarkets, initHealthTokens] = useMemo(() => {
|
||||
|
@ -56,7 +118,8 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
) => {
|
||||
if (obj.asset.includes('/')) {
|
||||
acc.market.push(obj)
|
||||
} else {
|
||||
}
|
||||
if (!obj.asset.includes('PERP')) {
|
||||
acc.token.push(obj)
|
||||
}
|
||||
return acc
|
||||
|
@ -75,7 +138,8 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
) => {
|
||||
if (obj.asset.includes('/')) {
|
||||
acc.market.push(obj)
|
||||
} else {
|
||||
}
|
||||
if (!obj.asset.includes('PERP')) {
|
||||
acc.token.push(obj)
|
||||
}
|
||||
return acc
|
||||
|
@ -111,8 +175,15 @@ const HealthContributions = ({ hideView }: { hideView: () => void }) => {
|
|||
if (!group)
|
||||
return <QuestionMarkCircleIcon className="h-6 w-6 text-th-fgd-3" />
|
||||
const isSpotMarket = asset.includes('/')
|
||||
if (isSpotMarket) {
|
||||
const market = group.getSerum3MarketByName(asset)
|
||||
const isPerpMarket = asset.includes('PERP')
|
||||
const isMarket = isSpotMarket || isPerpMarket
|
||||
if (isMarket) {
|
||||
let market
|
||||
if (isSpotMarket) {
|
||||
market = group.getSerum3MarketByName(asset)
|
||||
} else {
|
||||
market = group.getPerpMarketByName(asset)
|
||||
}
|
||||
return market ? (
|
||||
<MarketLogos market={market} size="small" />
|
||||
) : (
|
||||
|
|
|
@ -10,7 +10,7 @@ import useMangoAccount from 'hooks/useMangoAccount'
|
|||
import { useViewport } from 'hooks/useViewport'
|
||||
import { breakpoints } from 'utils/theme'
|
||||
import { MouseEventHandler } from 'react'
|
||||
import { HealthContribution } from 'types'
|
||||
import { ContributionDetails, HealthContribution } from 'types'
|
||||
|
||||
const TokensHealthTable = ({
|
||||
initTokens,
|
||||
|
@ -30,6 +30,7 @@ const TokensHealthTable = ({
|
|||
const { mangoAccount } = useMangoAccount()
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.sm : false
|
||||
|
||||
return group && mangoAccount ? (
|
||||
!isMobile ? (
|
||||
<Table>
|
||||
|
@ -61,7 +62,13 @@ const TokensHealthTable = ({
|
|||
{maintTokens
|
||||
.sort((a, b) => b.contribution - a.contribution)
|
||||
.map((cont) => {
|
||||
const { asset, contribution, isAsset } = cont
|
||||
const {
|
||||
asset,
|
||||
contribution,
|
||||
contributionDetails,
|
||||
isAsset,
|
||||
hasPerp,
|
||||
} = cont
|
||||
const bank = group.banksMapByName.get(asset)?.[0]
|
||||
|
||||
let initAssetWeight = 0
|
||||
|
@ -85,10 +92,9 @@ const TokensHealthTable = ({
|
|||
}
|
||||
|
||||
const assetOrLiabMultiplier = isAsset ? 1 : -1
|
||||
|
||||
const initToken = initTokens.find((cont) => cont.asset === asset)
|
||||
const initContribution =
|
||||
(initTokens.find((cont) => cont.asset === asset)
|
||||
?.contribution || 0) * assetOrLiabMultiplier
|
||||
(initToken?.contribution || 0) * assetOrLiabMultiplier
|
||||
|
||||
const maintContribution = contribution * assetOrLiabMultiplier
|
||||
|
||||
|
@ -128,14 +134,23 @@ const TokensHealthTable = ({
|
|||
)}
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="text-right">
|
||||
<p>
|
||||
<FormatNumericValue
|
||||
value={initContribution}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
<div className="flex flex-col items-end text-right">
|
||||
<Tooltip
|
||||
className={!hasPerp ? 'hidden' : ''}
|
||||
content={
|
||||
<UsdcTooltipContent
|
||||
contributions={initToken?.contributionDetails}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p className={hasPerp ? 'tooltip-underline' : ''}>
|
||||
<FormatNumericValue
|
||||
value={initContribution}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="text-th-fgd-3">
|
||||
{initContribution > 0
|
||||
? initAssetWeight.toFixed(2)
|
||||
|
@ -147,14 +162,23 @@ const TokensHealthTable = ({
|
|||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="text-right">
|
||||
<p>
|
||||
<FormatNumericValue
|
||||
value={maintContribution}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
<div className="flex flex-col items-end text-right">
|
||||
<Tooltip
|
||||
className={!hasPerp ? 'hidden' : ''}
|
||||
content={
|
||||
<UsdcTooltipContent
|
||||
contributions={contributionDetails}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p className={hasPerp ? 'tooltip-underline' : ''}>
|
||||
<FormatNumericValue
|
||||
value={maintContribution}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="text-th-fgd-3">
|
||||
{maintContribution > 0
|
||||
? maintAssetWeight.toFixed(2)
|
||||
|
@ -175,7 +199,13 @@ const TokensHealthTable = ({
|
|||
{maintTokens
|
||||
.sort((a, b) => b.contribution - a.contribution)
|
||||
.map((cont) => {
|
||||
const { asset, contribution, isAsset } = cont
|
||||
const {
|
||||
asset,
|
||||
contribution,
|
||||
contributionDetails,
|
||||
isAsset,
|
||||
hasPerp,
|
||||
} = cont
|
||||
const bank = group.banksMapByName.get(asset)?.[0]
|
||||
|
||||
let initAssetWeight = 0
|
||||
|
@ -198,9 +228,9 @@ const TokensHealthTable = ({
|
|||
|
||||
const assetOrLiabMultiplier = isAsset ? 1 : -1
|
||||
|
||||
const initToken = initTokens.find((cont) => cont.asset === asset)
|
||||
const initContribution =
|
||||
(initTokens.find((cont) => cont.asset === asset)?.contribution ||
|
||||
0) * assetOrLiabMultiplier
|
||||
(initToken?.contribution || 0) * assetOrLiabMultiplier
|
||||
|
||||
const maintContribution = contribution * assetOrLiabMultiplier
|
||||
|
||||
|
@ -260,21 +290,28 @@ const TokensHealthTable = ({
|
|||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
<Tooltip
|
||||
content={t('account:tooltip-init-health')}
|
||||
{t('account:init-health-contribution')}
|
||||
</p>
|
||||
<Tooltip
|
||||
className={!hasPerp ? 'hidden' : ''}
|
||||
content={
|
||||
<UsdcTooltipContent
|
||||
contributions={initToken?.contributionDetails}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p
|
||||
className={`font-mono text-th-fgd-2 ${
|
||||
hasPerp ? 'tooltip-underline' : ''
|
||||
}`}
|
||||
>
|
||||
<span className="tooltip-underline">
|
||||
{t('account:init-health-contribution')}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue
|
||||
value={initContribution}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
<FormatNumericValue
|
||||
value={initContribution}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="font-mono text-th-fgd-3">
|
||||
{initContribution > 0
|
||||
? initAssetWeight.toFixed(2)
|
||||
|
@ -286,21 +323,28 @@ const TokensHealthTable = ({
|
|||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
<Tooltip
|
||||
content={t('account:tooltip-maint-health')}
|
||||
{t('account:maint-health-contribution')}
|
||||
</p>
|
||||
<Tooltip
|
||||
className={!hasPerp ? 'hidden' : ''}
|
||||
content={
|
||||
<UsdcTooltipContent
|
||||
contributions={contributionDetails}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p
|
||||
className={`font-mono text-th-fgd-2 ${
|
||||
hasPerp ? 'tooltip-underline' : ''
|
||||
}`}
|
||||
>
|
||||
<span className="tooltip-underline">
|
||||
{t('account:maint-health-contribution')}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</p>
|
||||
<p className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue
|
||||
value={maintContribution}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
<FormatNumericValue
|
||||
value={maintContribution}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
</Tooltip>
|
||||
<p className="font-mono text-th-fgd-3">
|
||||
{maintContribution > 0
|
||||
? maintAssetWeight.toFixed(2)
|
||||
|
@ -324,3 +368,39 @@ const TokensHealthTable = ({
|
|||
}
|
||||
|
||||
export default TokensHealthTable
|
||||
|
||||
const UsdcTooltipContent = ({
|
||||
contributions,
|
||||
}: {
|
||||
contributions: ContributionDetails | undefined
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
if (!contributions) return null
|
||||
const { perpMarketContributions, spotUi } = contributions
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between">
|
||||
<p className="mr-3">{t('spot')}</p>
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue value={spotUi} decimals={2} isUsd />
|
||||
</span>
|
||||
</div>
|
||||
{perpMarketContributions
|
||||
.filter((cont) => Math.abs(cont.contributionUi) > 0.01)
|
||||
.map((perp) => (
|
||||
<div className="flex justify-between" key={perp.market}>
|
||||
<p className="mr-3">{perp.market}</p>
|
||||
<span className="font-mono text-th-fgd-2">
|
||||
<FormatNumericValue
|
||||
value={perp.contributionUi}
|
||||
decimals={2}
|
||||
isUsd
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-feeds": "0.1.6",
|
||||
"@blockworks-foundation/mango-v4": "^0.17.20",
|
||||
"@blockworks-foundation/mango-v4": "^0.17.24",
|
||||
"@headlessui/react": "1.6.6",
|
||||
"@heroicons/react": "2.0.10",
|
||||
"@metaplex-foundation/js": "0.18.3",
|
||||
|
|
|
@ -417,5 +417,17 @@ export type TickerData = {
|
|||
export interface HealthContribution {
|
||||
asset: string
|
||||
contribution: number
|
||||
contributionDetails?: ContributionDetails
|
||||
hasPerp?: boolean
|
||||
isAsset: boolean
|
||||
}
|
||||
|
||||
export interface PerpMarketContribution {
|
||||
market: string
|
||||
contributionUi: number
|
||||
}
|
||||
|
||||
export interface ContributionDetails {
|
||||
perpMarketContributions: PerpMarketContribution[]
|
||||
spotUi: number
|
||||
}
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
dependencies:
|
||||
ws "^8.13.0"
|
||||
|
||||
"@blockworks-foundation/mango-v4@0.17.20":
|
||||
version "0.17.20"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.17.20.tgz#335e49fc98bbc96dc2b7fbcb57cd22262fc026e9"
|
||||
integrity sha512-O0LwCHLcR+hB4ejXXxtaix1G0E5qK3Op82S32J/3pIqmmsB5OW0wYVZMplSszDnVJ7O26RV02xw9M6uuHJCFsA==
|
||||
"@blockworks-foundation/mango-v4@^0.17.24":
|
||||
version "0.17.24"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.17.24.tgz#fd00ed93aa5540b35478f4e5422dc386107606dc"
|
||||
integrity sha512-bR2C7n45HNJXsvQmuQDwCuPCk5gaAropNnI1KGdsUzrWfJmKjWcTPIeOLvAGO0r4WdYJS7GF6DMC6KIf6VFrag==
|
||||
dependencies:
|
||||
"@coral-xyz/anchor" "^0.27.0"
|
||||
"@project-serum/serum" "0.13.65"
|
||||
|
|
Loading…
Reference in New Issue