add health impact to trade form

This commit is contained in:
saml33 2022-11-04 18:55:21 +00:00
parent 9943911efc
commit c1254c9186
7 changed files with 111 additions and 74 deletions

View File

@ -0,0 +1,38 @@
import { HealthType } from '@blockworks-foundation/mango-v4'
import { PublicKey } from '@solana/web3.js'
import { useMemo } from 'react'
import mangoStore from '@store/mangoStore'
import HealthImpact from './shared/HealthImpact'
const HealthImpactTokenChange = ({
uiAmount,
isDeposit,
mintPk,
}: {
uiAmount: number
isDeposit?: boolean
mintPk: PublicKey
}) => {
const maintProjectedHealth = useMemo(() => {
const mangoAccount = mangoStore.getState().mangoAccount.current
const group = mangoStore.getState().group
if (!group || !mangoAccount) return 0
const uiTokenAmount = isDeposit ? uiAmount : uiAmount * -1
const projectedHealth =
mangoAccount.simHealthRatioWithTokenPositionUiChanges(
group,
[{ mintPk, uiTokenAmount }],
HealthType.maint
)
return projectedHealth! > 100
? 100
: projectedHealth! < 0
? 0
: Math.trunc(projectedHealth!)
}, [mintPk, uiAmount, isDeposit])
return <HealthImpact maintProjectedHealth={maintProjectedHealth} />
}
export default HealthImpactTokenChange

View File

@ -17,8 +17,7 @@ import { floorToDecimal, formatFixedDecimals } from '../../utils/numbers'
import ActionTokenList from '../account/ActionTokenList'
import ButtonGroup from '../forms/ButtonGroup'
import Label from '../forms/Label'
import Button, { LinkButton } from '../shared/Button'
import HealthImpact from '../shared/HealthImpact'
import Button from '../shared/Button'
import InlineNotification from '../shared/InlineNotification'
import Loading from '../shared/Loading'
import Modal from '../shared/Modal'
@ -26,6 +25,7 @@ import { EnterBottomExitBottom, FadeInFadeOut } from '../shared/Transitions'
import { withValueLimit } from '../swap/SwapForm'
import { getMaxWithdrawForBank } from '../swap/useTokenMax'
import MaxAmountButton from '@components/shared/MaxAmountButton'
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
interface BorrowModalProps {
token?: string
@ -256,7 +256,10 @@ function BorrowModal({ isOpen, onClose, token }: ModalCombinedProps) {
</div> */}
</div>
<div className="my-6 space-y-2 border-y border-th-bkg-3 px-2 py-4">
<HealthImpact mintPk={bank!.mint} uiAmount={Number(inputAmount)} />
<HealthImpactTokenChange
mintPk={bank!.mint}
uiAmount={Number(inputAmount)}
/>
<div className="flex justify-between">
<p>{t('borrow-value')}</p>
<p className="font-mono text-th-fgd-1">

View File

@ -21,8 +21,6 @@ import ActionTokenList from '../account/ActionTokenList'
import ButtonGroup from '../forms/ButtonGroup'
import Label from '../forms/Label'
import Button from '../shared/Button'
import HealthImpact from '../shared/HealthImpact'
import InfoTooltip from '../shared/InfoTooltip'
import InlineNotification from '../shared/InlineNotification'
import Loading from '../shared/Loading'
import Modal from '../shared/Modal'
@ -30,6 +28,7 @@ import { EnterBottomExitBottom, FadeInFadeOut } from '../shared/Transitions'
import { withValueLimit } from '../swap/SwapForm'
import MaxAmountButton from '@components/shared/MaxAmountButton'
import Tooltip from '@components/shared/Tooltip'
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
interface DepositModalProps {
token?: string
@ -311,7 +310,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
</div>
</div>
<div className="my-6 space-y-1.5 border-y border-th-bkg-3 px-2 py-4 text-sm ">
<HealthImpact
<HealthImpactTokenChange
mintPk={bank!.mint}
uiAmount={Number(inputAmount)}
isDeposit

View File

@ -14,12 +14,11 @@ import mangoStore from '@store/mangoStore'
import { ModalProps } from '../../types/modal'
import { INPUT_TOKEN_DEFAULT } from '../../utils/constants'
import { notify } from '../../utils/notifications'
import { floorToDecimal, formatFixedDecimals } from '../../utils/numbers'
import { formatFixedDecimals } from '../../utils/numbers'
import ActionTokenList from '../account/ActionTokenList'
import ButtonGroup from '../forms/ButtonGroup'
import Label from '../forms/Label'
import Button, { LinkButton } from '../shared/Button'
import HealthImpact from '../shared/HealthImpact'
import Button from '../shared/Button'
import InlineNotification from '../shared/InlineNotification'
import Loading from '../shared/Loading'
import Modal from '../shared/Modal'
@ -27,6 +26,7 @@ import { EnterBottomExitBottom, FadeInFadeOut } from '../shared/Transitions'
import { withValueLimit } from '../swap/SwapForm'
import { getMaxWithdrawForBank } from '../swap/useTokenMax'
import MaxAmountButton from '@components/shared/MaxAmountButton'
import HealthImpactTokenChange from '@components/HealthImpactTokenChange'
interface WithdrawModalProps {
token?: string
@ -255,7 +255,7 @@ function WithdrawModal({ isOpen, onClose, token }: ModalCombinedProps) {
</div>
</div>
<div className="my-6 space-y-2 border-y border-th-bkg-3 px-2 py-4">
<HealthImpact
<HealthImpactTokenChange
mintPk={bank!.mint}
uiAmount={Number(inputAmount)}
/>

View File

@ -1,18 +1,14 @@
import { HealthType } from '@blockworks-foundation/mango-v4'
import { ArrowRightIcon } from '@heroicons/react/20/solid'
import { PublicKey } from '@solana/web3.js'
import { useTranslation } from 'next-i18next'
import { useMemo } from 'react'
import mangoStore from '@store/mangoStore'
import Tooltip from './Tooltip'
const HealthImpact = ({
uiAmount,
isDeposit,
mintPk,
maintProjectedHealth,
}: {
uiAmount: number
isDeposit?: boolean
mintPk: PublicKey
maintProjectedHealth: number
}) => {
const { t } = useTranslation('common')
const group = mangoStore.getState().group
@ -23,27 +19,13 @@ const HealthImpact = ({
return mangoAccount.getHealthRatioUi(group, HealthType.maint)
}, [mangoAccount])
const maintProjectedHealth = useMemo(() => {
const group = mangoStore.getState().group
if (!group || !mangoAccount) return 0
const uiTokenAmount = isDeposit ? uiAmount : uiAmount * -1
const projectedHealth =
mangoAccount.simHealthRatioWithTokenPositionUiChanges(
group,
[{ mintPk, uiTokenAmount }],
HealthType.maint
)
return projectedHealth! > 100
? 100
: projectedHealth! < 0
? 0
: Math.trunc(projectedHealth!)
}, [mangoAccount, mintPk, uiAmount, isDeposit])
return (
<div className="flex justify-between">
<p>{t('health-impact')}</p>
<div className="flex flex-wrap items-start justify-between">
<Tooltip content="Projects the health of your account before you make a trade. The first value is your current account health and the second, your projected account health.">
<p className="tooltip-underline mr-4 mb-1 text-sm">
{t('health-impact')}
</p>
</Tooltip>
<div className="flex items-center space-x-2 font-mono">
<p className="text-th-fgd-1">{currentMaintHealth}%</p>
<ArrowRightIcon className="h-4 w-4 text-th-fgd-4" />

View File

@ -2,11 +2,9 @@ import { useState, useCallback, useEffect, useMemo } from 'react'
import { PublicKey } from '@solana/web3.js'
import {
ArrowDownIcon,
ArrowRightIcon,
Cog8ToothIcon,
MagnifyingGlassIcon,
ExclamationCircleIcon,
HeartIcon,
LinkIcon,
} from '@heroicons/react/20/solid'
import { RouteInfo } from '@jup-ag/core'
@ -22,7 +20,7 @@ import { SwapLeverageSlider } from './LeverageSlider'
import { useTranslation } from 'next-i18next'
import SwapFormTokenList from './SwapFormTokenList'
import { Transition } from '@headlessui/react'
import Button, { IconButton, LinkButton } from '../shared/Button'
import Button, { IconButton } from '../shared/Button'
import ButtonGroup from '../forms/ButtonGroup'
import Loading from '../shared/Loading'
import { EnterBottomExitBottom } from '../shared/Transitions'
@ -35,8 +33,8 @@ import {
OUTPUT_TOKEN_DEFAULT,
} from '../../utils/constants'
import { useTokenMax } from './useTokenMax'
import Tooltip from '@components/shared/Tooltip'
import MaxAmountButton from '@components/shared/MaxAmountButton'
import HealthImpact from '@components/shared/HealthImpact'
const MAX_DIGITS = 11
export const withValueLimit = (values: NumberFormatValues): boolean => {
@ -368,39 +366,7 @@ const SwapForm = () => {
id="swap-step-four"
className={`border-t border-th-bkg-3 px-6 py-4 transition-all`}
>
<div className="flex justify-between">
<div className="flex items-center">
<HeartIcon className="mr-1.5 h-4 w-4 text-th-fgd-4" />
<Tooltip content="Projects the health of your account before you make a trade. The first value is your current account health and the second, your projected account health.">
<p className="tooltip-underline text-sm">{t('health-impact')}</p>
</Tooltip>
</div>
<div className="flex items-center space-x-2 font-mono">
<p className="text-sm text-th-fgd-1">{currentMaintHealth}%</p>
<ArrowRightIcon className="h-4 w-4 text-th-fgd-4" />
<p
className={`${
maintProjectedHealth! < 50 && maintProjectedHealth! > 15
? 'text-th-orange'
: maintProjectedHealth! <= 15
? 'text-th-red'
: 'text-th-green'
} text-sm`}
>
{maintProjectedHealth!}%{' '}
<span
className={`text-xs ${
maintProjectedHealth! >= currentMaintHealth!
? 'text-th-green'
: 'text-th-red'
}`}
>
({maintProjectedHealth! >= currentMaintHealth! ? '+' : ''}
{maintProjectedHealth! - currentMaintHealth!}%)
</span>
</p>
</div>
</div>
<HealthImpact maintProjectedHealth={maintProjectedHealth} />
</div>
</ContentBox>
)

View File

@ -1,4 +1,5 @@
import {
HealthType,
PerpMarket,
PerpOrderSide,
PerpOrderType,
@ -29,6 +30,7 @@ import { Market } from '@project-serum/serum'
import TabUnderline from '@components/shared/TabUnderline'
import { group } from 'console'
import PerpSlider from './PerpSlider'
import HealthImpact from '@components/shared/HealthImpact'
const TABS: [string, number][] = [
['Limit', 0],
@ -287,6 +289,50 @@ const AdvancedTradeForm = () => {
}
}, [t])
const maintProjectedHealth = useMemo(() => {
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount.current
if (!mangoAccount || !group || !tradeForm.baseSize) return 100
let simulatedHealthRatio: number
if (selectedMarket instanceof Serum3Market) {
simulatedHealthRatio =
tradeForm.side === 'sell'
? mangoAccount.simHealthRatioWithSerum3AskUiChanges(
group,
parseFloat(tradeForm.baseSize),
selectedMarket.serumMarketExternal,
HealthType.maint
)
: mangoAccount.simHealthRatioWithSerum3BidUiChanges(
group,
parseFloat(tradeForm.baseSize),
selectedMarket.serumMarketExternal,
HealthType.maint
)
} else {
simulatedHealthRatio =
tradeForm.side === 'sell'
? mangoAccount.simHealthRatioWithPerpAskUiChanges(
group,
selectedMarket!.perpMarketIndex,
parseFloat(tradeForm.baseSize)
)
: mangoAccount.simHealthRatioWithPerpBidUiChanges(
group,
selectedMarket!.perpMarketIndex,
parseFloat(tradeForm.baseSize)
)
}
return simulatedHealthRatio! > 100
? 100
: simulatedHealthRatio! < 0
? 0
: Math.trunc(simulatedHealthRatio!)
}, [selectedMarket, tradeForm])
return (
<div>
<div className="border-b border-th-bkg-3">
@ -478,6 +524,9 @@ const AdvancedTradeForm = () => {
)}
</Button>
</div>
<div className="mt-6 px-4">
<HealthImpact maintProjectedHealth={maintProjectedHealth} />
</div>
</div>
)
}