Max margin trade fix (#490)

* fix: margin trading fix

* fix: accountsBalancesTable fix

* fix: show detailed APR where we have the space

* fix: smallerThan not biggerThan

* fix: be more precise on the decimals

* tidy: refactor
This commit is contained in:
Linkie Link 2023-09-20 11:16:47 +02:00 committed by GitHub
parent 9a3fe4dd1e
commit 2ba18d2f05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 55 additions and 24 deletions

View File

@ -20,6 +20,7 @@ import { FormattedNumber } from 'components/FormattedNumber'
import { SortAsc, SortDesc, SortNone } from 'components/Icons' import { SortAsc, SortDesc, SortNone } from 'components/Icons'
import Text from 'components/Text' import Text from 'components/Text'
import { ASSETS } from 'constants/assets' import { ASSETS } from 'constants/assets'
import { MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle' import { ORACLE_DENOM } from 'constants/oracle'
import useCurrentAccount from 'hooks/useCurrentAccount' import useCurrentAccount from 'hooks/useCurrentAccount'
import useMarketAssets from 'hooks/useMarketAssets' import useMarketAssets from 'hooks/useMarketAssets'
@ -99,23 +100,24 @@ export default function Index(props: Props) {
row.original.amount, row.original.amount,
getAssetByDenom(row.original.denom) ?? ASSETS[0], getAssetByDenom(row.original.denom) ?? ASSETS[0],
) )
if (amount >= 0.01) if (amount >= 1)
return ( return (
<FormattedNumber <FormattedNumber
className={className} className={className}
amount={amount} amount={amount}
options={{ abbreviated: true }} options={{ abbreviated: true, maxDecimals: MAX_AMOUNT_DECIMALS }}
animate animate
/> />
) )
const formattedAmount = formatAmountToPrecision(amount, 1) const formattedAmount = formatAmountToPrecision(amount, MAX_AMOUNT_DECIMALS)
return ( return (
<FormattedNumber <FormattedNumber
className={className} className={className}
amount={formattedAmount} smallerThanThreshold={formattedAmount < MIN_AMOUNT}
amount={formattedAmount < MIN_AMOUNT ? MIN_AMOUNT : formattedAmount}
options={{ options={{
maxDecimals: baseCurrency.decimals, maxDecimals: MAX_AMOUNT_DECIMALS,
minDecimals: 0, minDecimals: 0,
}} }}
animate animate

View File

@ -6,7 +6,7 @@ import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
import { ArrowRight } from 'components/Icons' import { ArrowRight } from 'components/Icons'
import Text from 'components/Text' import Text from 'components/Text'
import { BN_ZERO } from 'constants/math' import { BN_ZERO, MAX_AMOUNT_DECIMALS } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle' import { ORACLE_DENOM } from 'constants/oracle'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData' import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData' import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
@ -134,7 +134,7 @@ function Item(props: ItemProps) {
{props.isPercentage ? ( {props.isPercentage ? (
<FormattedNumber <FormattedNumber
amount={current.toNumber()} amount={current.toNumber()}
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }} options={{ suffix: '%', minDecimals: 2, maxDecimals: MAX_AMOUNT_DECIMALS }}
className='text-sm' className='text-sm'
animate animate
/> />
@ -152,7 +152,7 @@ function Item(props: ItemProps) {
{props.isPercentage ? ( {props.isPercentage ? (
<FormattedNumber <FormattedNumber
amount={change.toNumber()} amount={change.toNumber()}
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }} options={{ suffix: '%', minDecimals: 2, maxDecimals: MAX_AMOUNT_DECIMALS }}
className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')} className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')}
animate animate
/> />

View File

@ -1,6 +1,8 @@
import DisplayCurrency from 'components/DisplayCurrency' import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
import { MAX_AMOUNT_DECIMALS, MIN_AMOUNT } from 'constants/math'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { demagnify } from 'utils/formatters'
interface Props { interface Props {
asset: Asset asset: Asset
@ -8,11 +10,13 @@ interface Props {
} }
export default function AmountAndValue(props: Props) { export default function AmountAndValue(props: Props) {
const amount = demagnify(props.amount.toString(), props.asset)
return ( return (
<div className='flex flex-col gap-[0.5] text-xs'> <div className='flex flex-col gap-[0.5] text-xs'>
<FormattedNumber <FormattedNumber
amount={props.amount.toNumber()} amount={amount < MIN_AMOUNT ? MIN_AMOUNT : amount}
options={{ decimals: props.asset.decimals, abbreviated: true }} smallerThanThreshold={amount < MIN_AMOUNT}
options={{ abbreviated: true, maxDecimals: MAX_AMOUNT_DECIMALS }}
animate animate
/> />
<DisplayCurrency <DisplayCurrency

View File

@ -4,13 +4,13 @@ import { useMemo } from 'react'
import { FormattedNumber } from 'components/FormattedNumber' import { FormattedNumber } from 'components/FormattedNumber'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { DISPLAY_CURRENCY_KEY } from 'constants/localStore' import { DISPLAY_CURRENCY_KEY } from 'constants/localStore'
import { ORACLE_DENOM } from 'constants/oracle'
import useLocalStorage from 'hooks/useLocalStorage' import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices' import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin' import { BNCoin } from 'types/classes/BNCoin'
import { getDisplayCurrencies } from 'utils/assets' import { getDisplayCurrencies } from 'utils/assets'
import { getCoinValue } from 'utils/formatters' import { getCoinValue } from 'utils/formatters'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import { ORACLE_DENOM } from 'constants/oracle'
interface Props { interface Props {
coin: BNCoin coin: BNCoin
@ -34,12 +34,6 @@ export default function DisplayCurrency(props: Props) {
) )
const isUSD = displayCurrencyAsset.id === 'USD' const isUSD = displayCurrencyAsset.id === 'USD'
const prefix = isUSD
? `${props.isApproximation ? '~ ' : ''}$`
: `${props.isApproximation ? '~ ' : ''}`
const suffix = isUSD
? ''
: ` ${displayCurrencyAsset.symbol ? ` ${displayCurrencyAsset.symbol}` : ''}`
const amount = useMemo(() => { const amount = useMemo(() => {
const coinValue = getCoinValue(props.coin, prices) const coinValue = getCoinValue(props.coin, prices)
@ -55,13 +49,23 @@ export default function DisplayCurrency(props: Props) {
return coinValue.div(displayPrice).toNumber() return coinValue.div(displayPrice).toNumber()
}, [displayCurrency, displayCurrencyAsset.decimals, prices, props.coin]) }, [displayCurrency, displayCurrencyAsset.decimals, prices, props.coin])
const isLessThanACent = isUSD && amount < 0.01 && amount > 0
const smallerThanPrefix = isLessThanACent ? '< ' : ''
const prefix = isUSD
? `${props.isApproximation ? '~ ' : smallerThanPrefix}$`
: `${props.isApproximation ? '~ ' : ''}`
const suffix = isUSD
? ''
: ` ${displayCurrencyAsset.symbol ? ` ${displayCurrencyAsset.symbol}` : ''}`
return ( return (
<FormattedNumber <FormattedNumber
className={classNames( className={classNames(
props.className, props.className,
props.parentheses && 'before:content-["("] after:content-[")"]', props.parentheses && 'before:content-["("] after:content-[")"]',
)} )}
amount={amount} amount={isLessThanACent ? 0.01 : amount}
options={{ options={{
minDecimals: isUSD ? 2 : 0, minDecimals: isUSD ? 2 : 0,
maxDecimals: 2, maxDecimals: 2,

View File

@ -13,6 +13,7 @@ interface Props {
className?: string className?: string
animate?: boolean animate?: boolean
parentheses?: boolean parentheses?: boolean
smallerThanThreshold?: boolean
} }
export const FormattedNumber = React.memo( export const FormattedNumber = React.memo(
@ -23,6 +24,15 @@ export const FormattedNumber = React.memo(
) )
const prevAmountRef = useRef<number>(0) const prevAmountRef = useRef<number>(0)
let { options, smallerThanThreshold } = props
if (smallerThanThreshold) {
if (!options) options = { prefix: '< ' }
if (options.prefix && options.prefix.substring(0, 1) !== '<')
options.prefix = `< ${options.prefix}`
else options.prefix = '< '
}
useEffect(() => { useEffect(() => {
if (prevAmountRef.current !== props.amount) prevAmountRef.current = props.amount if (prevAmountRef.current !== props.amount) prevAmountRef.current = props.amount
}, [props.amount]) }, [props.amount])
@ -47,7 +57,7 @@ export const FormattedNumber = React.memo(
props.className, props.className,
)} )}
> >
{formatValue(props.amount.toString(), props.options)} {formatValue(props.amount.toString(), options)}
</p> </p>
) )

View File

@ -8,6 +8,7 @@ import { Heart } from 'components/Icons'
import Loading from 'components/Loading' import Loading from 'components/Loading'
import Text from 'components/Text' import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell' import TitleAndSubCell from 'components/TitleAndSubCell'
import { MAX_AMOUNT_DECIMALS } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle' import { ORACLE_DENOM } from 'constants/oracle'
import useAccount from 'hooks/useAccount' import useAccount from 'hooks/useAccount'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData' import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
@ -70,7 +71,11 @@ function Content(props: Props) {
}, },
{ {
title: ( title: (
<FormattedNumber className='text-xl' amount={apr.toNumber()} options={{ suffix: '%' }} /> <FormattedNumber
className='text-xl'
amount={apr.toNumber()}
options={{ suffix: '%', maxDecimals: MAX_AMOUNT_DECIMALS, minDecimals: 2 }}
/>
), ),
sub: STATS[3].sub, sub: STATS[3].sub,
}, },
@ -105,7 +110,7 @@ interface SkeletonProps extends Props {
function Skeleton(props: SkeletonProps) { function Skeleton(props: SkeletonProps) {
return ( return (
<div className='flex flex-col py-8 gap-8'> <div className='flex flex-col gap-8 py-8'>
<div className='flex justify-between'> <div className='flex justify-between'>
<Text size='2xl'>Credit Account {props.accountId}</Text> <Text size='2xl'>Credit Account {props.accountId}</Text>
<div className='flex gap-1 max-w-[300px] flex-grow'> <div className='flex gap-1 max-w-[300px] flex-grow'>
@ -115,7 +120,7 @@ function Skeleton(props: SkeletonProps) {
</div> </div>
<div className='grid grid-cols-2 gap-4 lg:grid-cols-5 md:grid-cols-4 sm:grid-cols-3'> <div className='grid grid-cols-2 gap-4 lg:grid-cols-5 md:grid-cols-4 sm:grid-cols-3'>
{props.stats.map((stat) => ( {props.stats.map((stat) => (
<Card key={stat.sub} className='p-6 bg-white/5 flex-grow-1 text-center'> <Card key={stat.sub} className='p-6 text-center bg-white/5 flex-grow-1'>
<TitleAndSubCell <TitleAndSubCell
title={stat.title || <Loading className='w-20 h-6 mx-auto mb-2' />} title={stat.title || <Loading className='w-20 h-6 mx-auto mb-2' />}
sub={stat.sub} sub={stat.sub}

View File

@ -12,7 +12,7 @@ import { AvailableOrderType } from 'components/Trade/TradeModule/SwapForm/OrderT
import TradeSummary from 'components/Trade/TradeModule/SwapForm/TradeSummary' import TradeSummary from 'components/Trade/TradeModule/SwapForm/TradeSummary'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings' import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { SLIPPAGE_KEY } from 'constants/localStore' import { SLIPPAGE_KEY } from 'constants/localStore'
import { BN_ZERO } from 'constants/math' import { BN_ZERO, MARGIN_TRADE_BUFFER } from 'constants/math'
import useAutoLend from 'hooks/useAutoLend' import useAutoLend from 'hooks/useAutoLend'
import useCurrentAccount from 'hooks/useCurrentAccount' import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer' import useHealthComputer from 'hooks/useHealthComputer'
@ -27,7 +27,7 @@ import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array' import { byDenom } from 'utils/array'
import { defaultFee } from 'utils/constants' import { defaultFee } from 'utils/constants'
import { getCapLeftWithBuffer } from 'utils/generic' import { getCapLeftWithBuffer } from 'utils/generic'
import { asyncThrottle, BN } from 'utils/helpers' import { BN, asyncThrottle } from 'utils/helpers'
interface Props { interface Props {
buyAsset: Asset buyAsset: Asset
@ -108,6 +108,8 @@ export default function SwapForm(props: Props) {
const [maxSellAmount, sellSideMarginThreshold, marginRatio] = useMemo(() => { const [maxSellAmount, sellSideMarginThreshold, marginRatio] = useMemo(() => {
const maxAmount = computeMaxSwapAmount(sellAsset.denom, buyAsset.denom, 'default') const maxAmount = computeMaxSwapAmount(sellAsset.denom, buyAsset.denom, 'default')
const maxAmountOnMargin = computeMaxSwapAmount(sellAsset.denom, buyAsset.denom, 'margin') const maxAmountOnMargin = computeMaxSwapAmount(sellAsset.denom, buyAsset.denom, 'margin')
.multipliedBy(MARGIN_TRADE_BUFFER)
.integerValue()
const marginRatio = maxAmount.dividedBy(maxAmountOnMargin) const marginRatio = maxAmount.dividedBy(maxAmountOnMargin)
estimateExactIn( estimateExactIn(

View File

@ -2,3 +2,7 @@ import { BN } from 'utils/helpers'
export const BN_ZERO = BN(0) export const BN_ZERO = BN(0)
export const BN_ONE = BN(1) export const BN_ONE = BN(1)
export const MARGIN_TRADE_BUFFER = 0.9
export const MIN_AMOUNT = 0.000001
export const MAX_AMOUNT_DECIMALS = 6