handle perp markets

This commit is contained in:
saml33 2023-07-14 23:34:09 +10:00
parent d88f64cc11
commit a3e21ec0d0
6 changed files with 246 additions and 81 deletions

View File

@ -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':

View File

@ -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" />
) : (

View File

@ -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>
</>
)
}

View File

@ -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",

View File

@ -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
}

View File

@ -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"