181 lines
5.8 KiB
TypeScript
181 lines
5.8 KiB
TypeScript
import {
|
||
HealthType,
|
||
MangoAccount,
|
||
PerpMarket,
|
||
Serum3Market,
|
||
toUiDecimalsForQuote,
|
||
} from '@blockworks-foundation/mango-v4'
|
||
import FormatNumericValue from '@components/shared/FormatNumericValue'
|
||
import HealthImpact from '@components/shared/HealthImpact'
|
||
import Tooltip from '@components/shared/Tooltip'
|
||
import mangoStore from '@store/mangoStore'
|
||
import Decimal from 'decimal.js'
|
||
import useMangoGroup from 'hooks/useMangoGroup'
|
||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||
import { useTranslation } from 'next-i18next'
|
||
import { useMemo } from 'react'
|
||
import Slippage from './Slippage'
|
||
|
||
const TradeSummary = ({
|
||
mangoAccount,
|
||
useMargin,
|
||
}: {
|
||
mangoAccount: MangoAccount | undefined
|
||
useMargin: boolean
|
||
}) => {
|
||
const { t } = useTranslation(['common', 'trade'])
|
||
const { group } = useMangoGroup()
|
||
const tradeForm = mangoStore((s) => s.tradeForm)
|
||
const { selectedMarket, quoteBank } = useSelectedMarket()
|
||
|
||
const maintProjectedHealth = useMemo(() => {
|
||
if (!mangoAccount || !group) return 100
|
||
|
||
let simulatedHealthRatio = 0
|
||
try {
|
||
if (selectedMarket instanceof Serum3Market) {
|
||
simulatedHealthRatio =
|
||
tradeForm.side === 'sell'
|
||
? mangoAccount.simHealthRatioWithSerum3AskUiChanges(
|
||
group,
|
||
Number(tradeForm.baseSize),
|
||
selectedMarket.serumMarketExternal,
|
||
HealthType.maint
|
||
)
|
||
: mangoAccount.simHealthRatioWithSerum3BidUiChanges(
|
||
group,
|
||
Number(tradeForm.quoteSize),
|
||
selectedMarket.serumMarketExternal,
|
||
HealthType.maint
|
||
)
|
||
} else if (selectedMarket instanceof PerpMarket) {
|
||
simulatedHealthRatio =
|
||
tradeForm.side === 'sell'
|
||
? mangoAccount.simHealthRatioWithPerpAskUiChanges(
|
||
group,
|
||
selectedMarket.perpMarketIndex,
|
||
parseFloat(tradeForm.baseSize) || 0
|
||
)
|
||
: mangoAccount.simHealthRatioWithPerpBidUiChanges(
|
||
group,
|
||
selectedMarket.perpMarketIndex,
|
||
parseFloat(tradeForm.baseSize) || 0
|
||
)
|
||
}
|
||
} catch (e) {
|
||
console.warn('Error calculating projected health: ', e)
|
||
}
|
||
|
||
return simulatedHealthRatio > 100
|
||
? 100
|
||
: simulatedHealthRatio < 0
|
||
? 0
|
||
: Math.trunc(simulatedHealthRatio)
|
||
}, [group, mangoAccount, selectedMarket, tradeForm])
|
||
|
||
const balanceBank = useMemo(() => {
|
||
if (
|
||
!group ||
|
||
!selectedMarket ||
|
||
selectedMarket instanceof PerpMarket ||
|
||
!useMargin
|
||
)
|
||
return
|
||
if (tradeForm.side === 'buy') {
|
||
return group.getFirstBankByTokenIndex(selectedMarket.quoteTokenIndex)
|
||
} else {
|
||
return group.getFirstBankByTokenIndex(selectedMarket.baseTokenIndex)
|
||
}
|
||
}, [group, selectedMarket, tradeForm.side])
|
||
|
||
const borrowAmount = useMemo(() => {
|
||
if (!balanceBank || !mangoAccount) return 0
|
||
let borrowAmount
|
||
const balance = mangoAccount.getTokenDepositsUi(balanceBank)
|
||
if (tradeForm.side === 'buy') {
|
||
const remainingBalance = balance - parseFloat(tradeForm.quoteSize)
|
||
borrowAmount = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||
} else {
|
||
const remainingBalance = balance - parseFloat(tradeForm.baseSize)
|
||
borrowAmount = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||
}
|
||
|
||
return borrowAmount
|
||
}, [balanceBank, mangoAccount, tradeForm])
|
||
|
||
const orderValue = useMemo(() => {
|
||
if (
|
||
!quoteBank ||
|
||
!tradeForm.price ||
|
||
!Number.isNaN(tradeForm.price) ||
|
||
!Number.isNaN(tradeForm.baseSize)
|
||
)
|
||
return 0
|
||
const basePriceDecimal = new Decimal(tradeForm.price)
|
||
const quotePriceDecimal = new Decimal(quoteBank.uiPrice)
|
||
const sizeDecimal = new Decimal(tradeForm.baseSize)
|
||
return basePriceDecimal.mul(quotePriceDecimal).mul(sizeDecimal)
|
||
}, [quoteBank, tradeForm])
|
||
|
||
return (
|
||
<div className="space-y-2 px-3 md:px-4">
|
||
<div className="flex justify-between text-xs">
|
||
<p>{t('trade:order-value')}</p>
|
||
<p className="text-th-fgd-2">
|
||
{orderValue ? (
|
||
<FormatNumericValue value={orderValue} decimals={2} isUsd />
|
||
) : (
|
||
'–'
|
||
)}
|
||
</p>
|
||
</div>
|
||
<HealthImpact maintProjectedHealth={maintProjectedHealth} small />
|
||
{borrowAmount ? (
|
||
<div className="flex justify-between text-xs">
|
||
<Tooltip
|
||
content={t('loan-origination-fee-tooltip', {
|
||
fee: `${(
|
||
balanceBank!.loanOriginationFeeRate.toNumber() * 100
|
||
).toFixed(3)}%`,
|
||
})}
|
||
delay={100}
|
||
>
|
||
<p className="tooltip-underline">{t('loan-origination-fee')}</p>
|
||
</Tooltip>
|
||
<p className="text-right font-mono text-th-fgd-2">
|
||
~
|
||
<FormatNumericValue
|
||
value={
|
||
borrowAmount * balanceBank!.loanOriginationFeeRate.toNumber()
|
||
}
|
||
decimals={balanceBank!.mintDecimals}
|
||
/>{' '}
|
||
<span className="font-body text-th-fgd-4">{balanceBank!.name}</span>
|
||
</p>
|
||
</div>
|
||
) : null}
|
||
<div className="flex justify-between text-xs">
|
||
<Tooltip content="The amount of capital you have to use for trades and loans. When your free collateral reaches $0 you won't be able to trade, borrow or withdraw.">
|
||
<p className="tooltip-underline">{t('free-collateral')}</p>
|
||
</Tooltip>
|
||
<p className="text-th-fgd-2">
|
||
{group && mangoAccount ? (
|
||
<FormatNumericValue
|
||
value={toUiDecimalsForQuote(
|
||
mangoAccount.getCollateralValue(group)
|
||
)}
|
||
decimals={2}
|
||
isUsd
|
||
/>
|
||
) : (
|
||
'–'
|
||
)}
|
||
</p>
|
||
</div>
|
||
<Slippage />
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default TradeSummary
|