Merge pull request #26 from blockworks-foundation/trade-health-impact
add health impact to trade form
This commit is contained in:
commit
33695f047a
|
@ -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
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)}
|
||||
/>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue