Merge pull request #72 from blockworks-foundation/format-numeric-value

format numeric value component
This commit is contained in:
tylersssss 2023-01-27 12:44:49 -05:00 committed by GitHub
commit 8c53d278a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 925 additions and 853 deletions

View File

@ -21,11 +21,6 @@ import {
INPUT_TOKEN_DEFAULT,
} from './../utils/constants'
import { notify } from './../utils/notifications'
import {
floorToDecimal,
formatDecimal,
formatFixedDecimals,
} from './../utils/numbers'
import ActionTokenList from './account/ActionTokenList'
import ButtonGroup from './forms/ButtonGroup'
import Label from './forms/Label'
@ -45,6 +40,8 @@ import TokenVaultWarnings from '@components/shared/TokenVaultWarnings'
import { useWallet } from '@solana/wallet-adapter-react'
import { useEnhancedWallet } from './wallet/EnhancedWalletProvider'
import AmountWithValue from './shared/AmountWithValue'
import FormatNumericValue from './shared/FormatNumericValue'
import { floorToDecimal } from 'utils/numbers'
interface BorrowFormProps {
onSuccess: () => void
@ -85,17 +82,12 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
const group = mangoStore.getState().group
if (!group || !bank || !mangoAccount) return new Decimal(0)
const amount = getMaxWithdrawForBank(group, bank, mangoAccount, true)
return amount && amount.gt(0)
? floorToDecimal(amount, bank.mintDecimals)
: new Decimal(0)
return amount && amount.gt(0) ? new Decimal(amount) : new Decimal(0)
}, [mangoAccount, bank])
const tokenBalance = useMemo(() => {
if (!bank || !mangoAccount) return new Decimal(0)
const balance = floorToDecimal(
mangoAccount.getTokenBalanceUi(bank),
bank.mintDecimals
)
const balance = new Decimal(mangoAccount.getTokenBalanceUi(bank))
return balance.gt(0) ? balance : new Decimal(0)
}, [bank, mangoAccount])
@ -105,17 +97,19 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
(percentage: string) => {
if (!bank) return
setSizePercentage(percentage)
const amount = (Number(percentage) / 100) * (tokenMax.toNumber() || 0)
setInputAmount(floorToDecimal(amount, bank.mintDecimals).toFixed())
const amount = floorToDecimal(
new Decimal(percentage).div(100).mul(tokenMax),
bank.mintDecimals
)
setInputAmount(amount.toString())
},
[tokenMax, bank]
)
const setMax = useCallback(() => {
if (!bank) return
setInputAmount(
floorToDecimal(Number(tokenMax), bank.mintDecimals).toFixed()
)
const max = floorToDecimal(tokenMax, bank.mintDecimals)
setInputAmount(max.toString())
handleSizePercentage('100')
}, [bank, tokenMax, handleSizePercentage])
@ -170,14 +164,11 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
bank,
mangoAccount,
true
)
).toNumber()
return {
key,
value,
maxAmount: floorToDecimal(
maxAmount,
bank.mintDecimals
).toNumber(),
maxAmount,
}
})
: []
@ -255,12 +246,10 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
{bank ? (
<MaxAmountButton
className="mb-2"
decimals={bank.mintDecimals}
label={t('max')}
onClick={setMax}
value={floorToDecimal(
Number(tokenMax),
bank.mintDecimals
).toFixed()}
value={tokenMax}
/>
) : null}
</div>
@ -332,46 +321,33 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
<p>{t('withdraw-amount')}</p>
{isBorrow ? (
<AmountWithValue
amount={formatDecimal(
Number(tokenBalance),
bank.mintDecimals
)}
value={formatFixedDecimals(
bank.uiPrice * tokenBalance.toNumber(),
true
)}
amount={tokenBalance}
amountDecimals={bank.mintDecimals}
value={bank.uiPrice * tokenBalance.toNumber()}
/>
) : inputAmount ? (
<AmountWithValue
amount={formatDecimal(
Number(inputAmount),
bank.mintDecimals
)}
value={formatFixedDecimals(
bank.uiPrice * parseFloat(inputAmount),
true
)}
amount={inputAmount}
amountDecimals={bank.mintDecimals}
value={bank.uiPrice * parseFloat(inputAmount)}
/>
) : (
<AmountWithValue amount="0" value="$0.00" />
<AmountWithValue amount="0" amountDecimals={0} value={0} />
)}
</div>
<div className="flex justify-between">
<p>{t('borrow-amount')}</p>
{isBorrow ? (
<AmountWithValue
amount={formatDecimal(
Number(inputAmount) - Number(tokenBalance),
bank.mintDecimals
)}
value={formatFixedDecimals(
amount={Number(inputAmount) - Number(tokenBalance)}
amountDecimals={bank.mintDecimals}
value={
bank.uiPrice *
(parseFloat(inputAmount) - tokenBalance.toNumber()),
true
)}
(parseFloat(inputAmount) - tokenBalance.toNumber())
}
/>
) : (
<AmountWithValue amount="0" value="$0.00" />
<AmountWithValue amount="0" amountDecimals={0} value={0} />
)}
</div>
<div className="flex justify-between">
@ -383,11 +359,13 @@ function BorrowForm({ onSuccess, token }: BorrowFormProps) {
<p className="font-mono text-th-fgd-2">
{isBorrow ? (
<>
{formatDecimal(
bank.loanOriginationFeeRate.toNumber() *
(parseFloat(inputAmount) - tokenBalance.toNumber()),
bank.mintDecimals
)}{' '}
<FormatNumericValue
value={
bank.loanOriginationFeeRate.toNumber() *
(parseFloat(inputAmount) - tokenBalance.toNumber())
}
decimals={bank.mintDecimals}
/>{' '}
<span className="font-body text-th-fgd-4">
{bank.name}
</span>

View File

@ -7,7 +7,6 @@ import {
LinkIcon,
} from '@heroicons/react/20/solid'
import { useWallet } from '@solana/wallet-adapter-react'
import Decimal from 'decimal.js'
import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import React, { useCallback, useMemo, useState } from 'react'
@ -19,11 +18,6 @@ import {
INPUT_TOKEN_DEFAULT,
} from './../utils/constants'
import { notify } from './../utils/notifications'
import {
floorToDecimal,
formatDecimal,
formatFixedDecimals,
} from './../utils/numbers'
import { TokenAccount } from './../utils/tokens'
import ActionTokenList from './account/ActionTokenList'
import ButtonGroup from './forms/ButtonGroup'
@ -42,6 +36,9 @@ import useMangoGroup from 'hooks/useMangoGroup'
import { useEnhancedWallet } from './wallet/EnhancedWalletProvider'
import useSolBalance from 'hooks/useSolBalance'
import AmountWithValue from './shared/AmountWithValue'
import FormatNumericValue from './shared/FormatNumericValue'
import Decimal from 'decimal.js'
import { floorToDecimal } from 'utils/numbers'
interface DepositFormProps {
onSuccess: () => void
@ -128,19 +125,18 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
}, [walletTokens, selectedToken])
const setMax = useCallback(() => {
setInputAmount(
floorToDecimal(tokenMax.maxAmount, tokenMax.maxDecimals).toFixed()
)
const max = floorToDecimal(tokenMax.maxAmount, tokenMax.maxDecimals)
setInputAmount(max.toString())
setSizePercentage('100')
}, [tokenMax])
const handleSizePercentage = useCallback(
(percentage: string) => {
setSizePercentage(percentage)
let amount = new Decimal(tokenMax.maxAmount).mul(percentage).div(100)
amount = floorToDecimal(amount, tokenMax.maxDecimals)
const amount = floorToDecimal(
new Decimal(tokenMax.maxAmount).mul(percentage).div(100),
tokenMax.maxDecimals
)
setInputAmount(amount.toString())
},
[tokenMax]
@ -197,10 +193,7 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
return {
key,
value,
walletBalance: floorToDecimal(
walletBalance.maxAmount,
walletBalance.maxDecimals
).toNumber(),
walletBalance: walletBalance.maxAmount,
walletBalanceValue: walletBalance.maxAmount * value[0].uiPrice!,
}
})
@ -268,12 +261,10 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
<Label text={`${t('deposit')} ${t('token')}`} />
<MaxAmountButton
className="mb-2"
decimals={tokenMax.maxDecimals}
label={t('wallet-balance')}
onClick={setMax}
value={floorToDecimal(
tokenMax.maxAmount,
tokenMax.maxDecimals
).toFixed()}
value={tokenMax.maxAmount}
/>
</div>
<div className="col-span-1 rounded-lg rounded-r-none border border-r-0 border-th-input-border bg-th-input-bkg">
@ -338,17 +329,12 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
<p>{t('deposit-amount')}</p>
{inputAmount ? (
<AmountWithValue
amount={formatDecimal(
Number(inputAmount),
bank.mintDecimals
)}
value={formatFixedDecimals(
bank.uiPrice * Number(inputAmount),
true
)}
amount={inputAmount}
amountDecimals={bank.mintDecimals}
value={bank.uiPrice * Number(inputAmount)}
/>
) : (
<AmountWithValue amount="0" value="$0.00" />
<AmountWithValue amount="0" amountDecimals={0} value={0} />
)}
</div>
{/* <div className="flex justify-between">
@ -364,12 +350,14 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
<p className="tooltip-underline">{t('collateral-value')}</p>
</Tooltip>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(
bank.uiPrice *
<FormatNumericValue
value={
bank.uiPrice *
Number(inputAmount) *
Number(bank.initAssetWeight),
true
)}
Number(bank.initAssetWeight)
}
isUsd
/>
</p>
</div>
</div>

View File

@ -13,11 +13,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'
import NumberFormat, { NumberFormatValues } from 'react-number-format'
import mangoStore from '@store/mangoStore'
import { notify } from './../utils/notifications'
import {
floorToDecimal,
formatDecimal,
formatFixedDecimals,
} from './../utils/numbers'
import { formatNumericValue } from './../utils/numbers'
import ActionTokenList from './account/ActionTokenList'
import ButtonGroup from './forms/ButtonGroup'
import Label from './forms/Label'
@ -83,16 +79,20 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
}, [walletTokens, selectedToken])
const borrowAmount = useMemo(() => {
if (!mangoAccount || !bank) return '0'
return formatDecimal(
mangoAccount.getTokenBorrowsUi(bank),
bank.mintDecimals
)
if (!mangoAccount || !bank) return new Decimal(0)
const amount = new Decimal(
mangoAccount.getTokenBorrowsUi(bank)
).toDecimalPlaces(bank.mintDecimals, Decimal.ROUND_UP)
return amount
}, [bank, mangoAccount])
const setMax = useCallback(() => {
if (!bank) return
setInputAmount(borrowAmount)
const amount = new Decimal(borrowAmount).toDecimalPlaces(
bank.mintDecimals,
Decimal.ROUND_UP
)
setInputAmount(amount.toString())
setSizePercentage('100')
}, [bank, borrowAmount])
@ -100,13 +100,12 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
(percentage: string) => {
if (!bank) return
setSizePercentage(percentage)
let amount: Decimal | number = new Decimal(borrowAmount)
const amount = new Decimal(borrowAmount)
.mul(percentage)
.div(100)
amount = floorToDecimal(amount, bank.mintDecimals).toNumber()
.toDecimalPlaces(bank.mintDecimals, Decimal.ROUND_UP)
setInputAmount(amount.toFixed(bank.mintDecimals))
setInputAmount(amount.toString())
},
[bank, borrowAmount]
)
@ -240,12 +239,15 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
<div className="grid grid-cols-2">
<div className="col-span-2 flex justify-between">
<Label text={`${t('repay')} ${t('token')}`} />
<MaxAmountButton
className="mb-2"
label={t('amount-owed')}
onClick={setMax}
value={borrowAmount}
/>
{bank ? (
<MaxAmountButton
className="mb-2"
decimals={bank.mintDecimals}
label={t('amount-owed')}
onClick={setMax}
value={borrowAmount}
/>
) : null}
</div>
<div className="col-span-1 rounded-lg rounded-r-none border border-r-0 border-th-input-border bg-th-input-bkg">
<button
@ -306,17 +308,12 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
<p>{t('repayment-amount')}</p>
{inputAmount ? (
<AmountWithValue
amount={formatDecimal(
Number(inputAmount),
bank.mintDecimals
)}
value={formatFixedDecimals(
bank.uiPrice * Number(inputAmount),
true
)}
amount={inputAmount}
amountDecimals={bank.mintDecimals}
value={bank.uiPrice * Number(inputAmount)}
/>
) : (
<AmountWithValue amount="0" value="$0.00" />
<AmountWithValue amount="0" amountDecimals={0} value={0} />
)}
</div>
<div className="flex justify-between">
@ -324,7 +321,10 @@ function RepayForm({ onSuccess, token }: RepayFormProps) {
<p>{t('outstanding-balance')}</p>
</div>
<p className="font-mono text-th-fgd-2">
{Number(borrowAmount) - Number(inputAmount)}{' '}
{formatNumericValue(
Number(borrowAmount) - Number(inputAmount),
bank.mintDecimals
)}{' '}
<span className="font-body text-th-fgd-4">
{selectedToken}
</span>

View File

@ -13,11 +13,6 @@ import { useRouter } from 'next/router'
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'
import { useViewport } from '../hooks/useViewport'
import mangoStore from '@store/mangoStore'
import {
floorToDecimal,
formatDecimal,
formatFixedDecimals,
} from '../utils/numbers'
import { breakpoints } from '../utils/theme'
import Switch from './forms/Switch'
import { IconButton, LinkButton } from './shared/Button'
@ -36,6 +31,7 @@ import { USDC_MINT } from 'utils/constants'
import { PublicKey } from '@solana/web3.js'
import ActionsLinkButton from './account/ActionsLinkButton'
import AmountWithValue from './shared/AmountWithValue'
import FormatNumericValue from './shared/FormatNumericValue'
const TokenList = () => {
const { t } = useTranslation(['common', 'token', 'trade'])
@ -187,59 +183,35 @@ const TokenList = () => {
</div>
</Td>
<Td className="text-right">
{tokenBalance ? (
<AmountWithValue
amount={formatDecimal(tokenBalance, bank.mintDecimals)}
value={formatFixedDecimals(
tokenBalance * oraclePrice,
true,
true
)}
stacked
/>
) : (
<AmountWithValue amount="0" value="$0.00" stacked />
)}
<AmountWithValue
amount={tokenBalance}
amountDecimals={bank.mintDecimals}
value={tokenBalance * oraclePrice}
stacked
/>
</Td>
<Td className="text-right">
{inOrders ? (
<AmountWithValue
amount={formatDecimal(inOrders, bank.mintDecimals)}
value={formatFixedDecimals(
inOrders * oraclePrice,
true,
true
)}
stacked
/>
) : (
<AmountWithValue amount="0" value="$0.00" stacked />
)}
<AmountWithValue
amount={inOrders}
amountDecimals={bank.mintDecimals}
value={inOrders * oraclePrice}
stacked
/>
</Td>
<Td className="text-right">
{unsettled ? (
<AmountWithValue
amount={formatDecimal(unsettled, bank.mintDecimals)}
value={formatFixedDecimals(
unsettled * oraclePrice,
true,
true
)}
stacked
/>
) : (
<AmountWithValue amount="0" value="$0.00" stacked />
)}
<AmountWithValue
amount={unsettled}
amountDecimals={bank.mintDecimals}
value={unsettled * oraclePrice}
stacked
/>
</Td>
<Td>
<div className="flex flex-col text-right">
<AmountWithValue
amount={
interestAmount
? formatDecimal(interestAmount, bank.mintDecimals)
: '0'
}
value={formatFixedDecimals(interestValue, true, true)}
amount={interestAmount}
amountDecimals={bank.mintDecimals}
value={interestValue}
stacked
/>
</div>
@ -247,16 +219,19 @@ const TokenList = () => {
<Td>
<div className="flex justify-end space-x-1.5">
<p className="text-th-up">
{formatDecimal(bank.getDepositRateUi(), 2, {
fixed: true,
})}
<FormatNumericValue
value={bank.getDepositRateUi()}
decimals={2}
/>
%
</p>
<span className="text-th-fgd-4">|</span>
<p className="text-th-down">
{formatDecimal(bank.getBorrowRateUi(), 2, {
fixed: true,
})}
<FormatNumericValue
value={bank.getBorrowRateUi()}
decimals={2}
roundUp
/>
%
</p>
</div>
@ -322,15 +297,10 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
const interestValue = hasInterestEarned
? hasInterestEarned.borrow_interest_usd * -1 +
hasInterestEarned.deposit_interest_usd
: 0.0
const tokenBalance = mangoAccount
? floorToDecimal(
mangoAccount.getTokenBalanceUi(bank),
bank.mintDecimals
).toNumber()
: 0
const tokenBalance = mangoAccount ? mangoAccount.getTokenBalanceUi(bank) : 0
const inOrders = spotBalances[bank.mint.toString()]?.inOrders || 0
const unsettled = spotBalances[bank.mint.toString()]?.unsettled || 0
@ -356,9 +326,10 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
<span className="mr-1 font-body text-th-fgd-4">
{t('balance')}:
</span>
{tokenBalance
? formatDecimal(tokenBalance, bank.mintDecimals)
: '0'}
<FormatNumericValue
value={tokenBalance}
decimals={bank.mintDecimals}
/>
</p>
</div>
</div>
@ -391,41 +362,45 @@ const MobileTokenListItem = ({ bank }: { bank: Bank }) => {
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('trade:in-orders')}</p>
<AmountWithValue
amount={
inOrders ? formatDecimal(inOrders, bank.mintDecimals) : '0'
}
value={formatFixedDecimals(inOrders * oraclePrice, true, true)}
amount={inOrders}
amountDecimals={bank.mintDecimals}
value={inOrders * oraclePrice}
/>
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('trade:unsettled')}</p>
<AmountWithValue
amount={
unsettled ? formatDecimal(unsettled, bank.mintDecimals) : '0'
}
value={formatFixedDecimals(unsettled * oraclePrice, true, true)}
amount={unsettled}
amountDecimals={bank.mintDecimals}
value={unsettled * oraclePrice}
/>
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('interest-earned-paid')}</p>
<AmountWithValue
amount={
interestAmount
? formatDecimal(interestAmount, bank.mintDecimals)
: '0'
}
value={formatFixedDecimals(interestValue, true, true)}
amount={interestAmount}
amountDecimals={bank.mintDecimals}
value={interestValue}
/>
</div>
<div className="col-span-1">
<p className="text-xs text-th-fgd-3">{t('rates')}</p>
<p className="space-x-2 font-mono">
<span className="text-th-up">
{formatDecimal(bank.getDepositRate().toNumber(), 2)}%
<FormatNumericValue
value={bank.getDepositRateUi()}
decimals={2}
/>
%
</span>
<span className="font-normal text-th-fgd-4">|</span>
<span className="text-th-down">
{formatDecimal(bank.getBorrowRate().toNumber(), 2)}%
<FormatNumericValue
value={bank.getBorrowRateUi()}
decimals={2}
roundUp
/>
%
</span>
</p>
</div>

View File

@ -18,11 +18,6 @@ import {
INPUT_TOKEN_DEFAULT,
} from './../utils/constants'
import { notify } from './../utils/notifications'
import {
floorToDecimal,
formatDecimal,
formatFixedDecimals,
} from './../utils/numbers'
import ActionTokenList from './account/ActionTokenList'
import ButtonGroup from './forms/ButtonGroup'
import Label from './forms/Label'
@ -41,6 +36,7 @@ import TokenVaultWarnings from '@components/shared/TokenVaultWarnings'
import { useWallet } from '@solana/wallet-adapter-react'
import { useEnhancedWallet } from './wallet/EnhancedWalletProvider'
import AmountWithValue from './shared/AmountWithValue'
import { floorToDecimal } from 'utils/numbers'
interface WithdrawFormProps {
onSuccess: () => void
@ -81,21 +77,29 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
if (!bank || !mangoAccount || !group) return new Decimal(0)
const amount = getMaxWithdrawForBank(group, bank, mangoAccount)
return amount && amount.gt(0)
? floorToDecimal(amount, bank.mintDecimals)
: new Decimal(0)
return amount
}, [mangoAccount, bank, group])
const handleSizePercentage = useCallback(
(percentage: string) => {
if (!bank) return
setSizePercentage(percentage)
const amount = tokenMax.mul(Number(percentage) / 100)
setInputAmount(floorToDecimal(amount, bank.mintDecimals).toFixed())
const amount = floorToDecimal(
new Decimal(tokenMax).mul(percentage).div(100),
bank.mintDecimals
)
setInputAmount(amount.toString())
},
[bank, tokenMax]
)
const setMax = useCallback(() => {
if (!bank) return
const max = floorToDecimal(tokenMax, bank.mintDecimals)
setInputAmount(max.toString())
setSizePercentage('100')
}, [bank, tokenMax])
const handleWithdraw = useCallback(async () => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
@ -145,16 +149,14 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
group,
bank,
mangoAccount
)
).toNumber()
return {
key,
value,
accountBalance: accountBalance
? floorToDecimal(accountBalance, bank.mintDecimals).toNumber()
: 0,
accountBalance,
accountBalanceValue:
accountBalance && bank.uiPrice
? accountBalance.toNumber() * bank.uiPrice
? accountBalance * bank.uiPrice
: 0,
}
})
@ -225,12 +227,10 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
{bank ? (
<MaxAmountButton
className="mb-2"
decimals={bank.mintDecimals}
label={t('max')}
onClick={() => handleSizePercentage('100')}
value={floorToDecimal(
Number(tokenMax),
bank.mintDecimals
).toFixed()}
onClick={setMax}
value={tokenMax}
/>
) : null}
</div>
@ -295,17 +295,12 @@ function WithdrawForm({ onSuccess, token }: WithdrawFormProps) {
<p>{t('withdraw-amount')}</p>
{inputAmount ? (
<AmountWithValue
amount={formatDecimal(
Number(inputAmount),
bank.mintDecimals
)}
value={formatFixedDecimals(
bank.uiPrice * Number(inputAmount),
true
)}
amount={inputAmount}
amountDecimals={bank.mintDecimals}
value={bank.uiPrice * Number(inputAmount)}
/>
) : (
<AmountWithValue amount="0" value="$0.00" />
<AmountWithValue amount="0" amountDecimals={0} value={0} />
)}
</div>
</div>

View File

@ -6,7 +6,7 @@ import { useTranslation } from 'next-i18next'
import { useEffect, useMemo, useState } from 'react'
import AccountActions from './AccountActions'
import mangoStore, { PerformanceDataItem } from '@store/mangoStore'
import { formatDecimal, formatFixedDecimals } from '../../utils/numbers'
import { formatNumericValue } from '../../utils/numbers'
import FlipNumbers from 'react-flip-numbers'
import dynamic from 'next/dynamic'
const SimpleAreaChart = dynamic(
@ -41,6 +41,7 @@ import { useViewport } from 'hooks/useViewport'
import { breakpoints } from 'utils/theme'
import useMangoGroup from 'hooks/useMangoGroup'
import PnlHistoryModal from '@components/modals/PnlHistoryModal'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const AccountPage = () => {
const { t } = useTranslation(['common', 'account'])
@ -251,7 +252,7 @@ const AccountPage = () => {
play
delay={0.05}
duration={1}
numbers={formatFixedDecimals(accountValue, true, true)}
numbers={formatNumericValue(accountValue, 2, true)}
/>
) : (
<FlipNumbers
@ -264,14 +265,15 @@ const AccountPage = () => {
/>
)
) : (
<span>{formatFixedDecimals(accountValue, true, true)}</span>
<FormatNumericValue
value={accountValue}
isUsd={true}
decimals={2}
/>
)}
</div>
<div className="flex items-center space-x-1.5">
<Change
change={Number(formatDecimal(accountValueChange, 2))}
prefix="$"
/>
<Change change={accountValueChange} prefix="$" />
<p className="text-th-fgd-4">{t('today')}</p>
</div>
</div>
@ -388,15 +390,17 @@ const AccountPage = () => {
</p>
</Tooltip>
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
{group && mangoAccount
? formatFixedDecimals(
toUiDecimalsForQuote(
mangoAccount.getCollateralValue(group).toNumber()
),
false,
true
)
: `$${(0).toFixed(2)}`}
<FormatNumericValue
value={
group && mangoAccount
? toUiDecimalsForQuote(
mangoAccount.getCollateralValue(group)
)
: 0
}
decimals={2}
isUsd={true}
/>
</p>
<span className="text-xs font-normal text-th-fgd-4">
<Tooltip
@ -407,17 +411,17 @@ const AccountPage = () => {
>
<span className="tooltip-underline">{t('total')}</span>:
<span className="ml-1 font-mono text-th-fgd-2">
{group && mangoAccount
? formatFixedDecimals(
toUiDecimalsForQuote(
mangoAccount
.getAssetsValue(group, HealthType.init)
.toNumber()
),
false,
true
)
: `$${(0).toFixed(2)}`}
<FormatNumericValue
value={
group && mangoAccount
? toUiDecimalsForQuote(
mangoAccount.getAssetsValue(group, HealthType.init)
)
: 0
}
decimals={2}
isUsd={true}
/>
</span>
</Tooltip>
</span>
@ -436,7 +440,7 @@ const AccountPage = () => {
</p>
</Tooltip>
<p className="mt-1 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
{leverage.toFixed(2)}x
<FormatNumericValue value={leverage} decimals={2} roundUp />x
</p>
</div>
</div>
@ -478,14 +482,14 @@ const AccountPage = () => {
) : null}
</div>
<p className="mt-1 mb-0.5 text-left text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
{formatFixedDecimals(accountPnl, true, true)}
<FormatNumericValue
value={accountPnl}
decimals={2}
isUsd={true}
/>
</p>
<div className="flex space-x-1">
<Change
change={Number(formatDecimal(oneDayPnlChange, 2))}
prefix="$"
size="small"
/>
<Change change={oneDayPnlChange} prefix="$" size="small" />
<p className="text-xs text-th-fgd-4">{t('today')}</p>
</div>
</div>
@ -518,14 +522,14 @@ const AccountPage = () => {
) : null}
</div>
<p className="mt-1 mb-0.5 text-2xl font-bold text-th-fgd-1 lg:text-xl xl:text-2xl">
{formatFixedDecimals(interestTotalValue, true, true)}
<FormatNumericValue
value={interestTotalValue}
decimals={2}
isUsd={true}
/>
</p>
<div className="flex space-x-1">
<Change
change={Number(formatDecimal(oneDayInterestChange, 2))}
prefix="$"
size="small"
/>
<Change change={oneDayInterestChange} prefix="$" size="small" />
<p className="text-xs text-th-fgd-4">{t('today')}</p>
</div>
</div>

View File

@ -1,23 +1,21 @@
import { Bank } from '@blockworks-foundation/mango-v4'
import Image from 'next/legacy/image'
import { useMemo } from 'react'
import {
formatDecimal,
formatFixedDecimals,
trimDecimals,
} from '../../utils/numbers'
import useJupiterMints from 'hooks/useJupiterMints'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const ActionTokenItem = ({
bank,
customValue,
onSelect,
roundUp,
showBorrowRates,
showDepositRates,
}: {
bank: Bank
customValue: number
onSelect: (x: string) => void
roundUp?: boolean
showBorrowRates?: boolean
showDepositRates?: boolean
}) => {
@ -56,22 +54,24 @@ const ActionTokenItem = ({
{showDepositRates ? (
<div className="w-1/4 text-right font-mono">
<p className="text-th-up">
{formatDecimal(bank.getDepositRate().toNumber(), 2)}%
<FormatNumericValue value={bank.getDepositRateUi()} decimals={2} />%
</p>
</div>
) : null}
{showBorrowRates ? (
<div className="w-1/4 text-right font-mono">
<p className="text-th-down">
{formatDecimal(bank.getBorrowRate().toNumber(), 2)}%
<FormatNumericValue value={bank.getBorrowRateUi()} decimals={2} />%
</p>
</div>
) : null}
<div className="w-1/2 pl-3 text-right">
<p className="truncate font-mono text-th-fgd-1">
{formatFixedDecimals(
trimDecimals(customValue, bank.mintDecimals + 1)
)}
<FormatNumericValue
value={customValue}
decimals={bank.mintDecimals}
roundUp={roundUp}
/>
</p>
</div>
</button>

View File

@ -45,6 +45,7 @@ const ActionTokenList = ({
customValue={bank[valueKey]}
key={bank.value[0].name}
onSelect={onSelect}
roundUp={valueKey === 'borrowAmount'}
showBorrowRates={showBorrowRates}
showDepositRates={showDepositRates}
/>

View File

@ -4,6 +4,7 @@ import Label from '@components/forms/Label'
import MultiSelectDropdown from '@components/forms/MultiSelectDropdown'
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
import Button, { IconButton } from '@components/shared/Button'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import Tooltip from '@components/shared/Tooltip'
import { Disclosure } from '@headlessui/react'
import {
@ -22,7 +23,6 @@ import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
import ActivityFeedTable from './ActivityFeedTable'
interface AdvancedFilters {
@ -428,25 +428,25 @@ const ActivityDetails = ({
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:asset-liquidated')}</p>
<p className="font-mono text-th-fgd-1">
{formatDecimal(asset_amount)}{' '}
<FormatNumericValue value={asset_amount} />{' '}
<span className="font-body">{asset_symbol}</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
{formatFixedDecimals(asset_price, true)}
<FormatNumericValue value={asset_price} isUsd />
</p>
<p className="font-mono text-xs text-th-fgd-3">
{formatFixedDecimals(asset_price * asset_amount, true)}
<FormatNumericValue value={asset_price * asset_amount} isUsd />
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:asset-returned')}</p>
<p className="font-mono text-th-fgd-1">
{formatDecimal(liab_amount)}{' '}
<FormatNumericValue value={liab_amount} />{' '}
<span className="font-body">{liab_symbol}</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
{formatFixedDecimals(liab_price, true)}
<FormatNumericValue value={liab_price} isUsd />
</p>
<p className="font-mono text-xs text-th-fgd-3">
{formatFixedDecimals(liab_price * liab_amount, true)}
<FormatNumericValue value={liab_price * liab_amount} isUsd />
</p>
</div>
</div>

View File

@ -1,6 +1,7 @@
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
import { IconButton, LinkButton } from '@components/shared/Button'
import ConnectEmptyState from '@components/shared/ConnectEmptyState'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import SheenLoader from '@components/shared/SheenLoader'
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
import Tooltip from '@components/shared/Tooltip'
@ -20,7 +21,7 @@ import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { Fragment, useCallback, useState } from 'react'
import { PAGINATION_PAGE_LENGTH, PREFERRED_EXPLORER_KEY } from 'utils/constants'
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
import { formatNumericValue } from 'utils/numbers'
import { breakpoints } from 'utils/theme'
const formatFee = (value: number) => {
@ -74,25 +75,28 @@ const ActivityFeedTable = ({
const { side, liab_amount, liab_symbol, asset_amount, asset_symbol } =
activity.activity_details
if (side === 'liqee') {
credit = { value: formatDecimal(liab_amount), symbol: liab_symbol }
credit = { value: formatNumericValue(liab_amount), symbol: liab_symbol }
debit = {
value: formatDecimal(asset_amount),
value: formatNumericValue(asset_amount),
symbol: asset_symbol,
}
} else {
credit = { value: formatDecimal(asset_amount), symbol: asset_symbol }
debit = { value: formatDecimal(liab_amount), symbol: liab_symbol }
credit = {
value: formatNumericValue(asset_amount),
symbol: asset_symbol,
}
debit = { value: formatNumericValue(liab_amount), symbol: liab_symbol }
}
}
if (activity_type === 'deposit') {
const { symbol, quantity } = activity.activity_details
credit = { value: formatDecimal(quantity), symbol }
credit = { value: formatNumericValue(quantity), symbol }
debit = { value: '0', symbol: '' }
}
if (activity_type === 'withdraw') {
const { symbol, quantity } = activity.activity_details
credit = { value: '0', symbol: '' }
debit = { value: formatDecimal(quantity * -1), symbol }
debit = { value: formatNumericValue(quantity * -1), symbol }
}
if (activity_type === 'swap') {
const {
@ -102,30 +106,36 @@ const ActivityFeedTable = ({
swap_out_symbol,
} = activity.activity_details
credit = {
value: formatDecimal(swap_out_amount),
value: formatNumericValue(swap_out_amount),
symbol: swap_out_symbol,
}
debit = {
value: formatDecimal(swap_in_amount * -1),
value: formatNumericValue(swap_in_amount * -1),
symbol: swap_in_symbol,
}
}
if (activity_type === 'perp_trade') {
const { perp_market_name, price, quantity } = activity.activity_details
credit = { value: quantity, symbol: perp_market_name }
debit = { value: formatDecimal(quantity * price * -1), symbol: 'USDC' }
debit = {
value: formatNumericValue(quantity * price * -1),
symbol: 'USDC',
}
}
if (activity_type === 'openbook_trade') {
const { side, price, size, base_symbol, quote_symbol } =
activity.activity_details
credit =
side === 'buy'
? { value: formatDecimal(size), symbol: base_symbol }
: { value: formatDecimal(size * price), symbol: quote_symbol }
? { value: formatNumericValue(size), symbol: base_symbol }
: { value: formatNumericValue(size * price), symbol: quote_symbol }
debit =
side === 'buy'
? { value: formatDecimal(size * price * -1), symbol: quote_symbol }
: { value: formatDecimal(size * -1), symbol: base_symbol }
? {
value: formatNumericValue(size * price * -1),
symbol: quote_symbol,
}
: { value: formatNumericValue(size * -1), symbol: base_symbol }
}
return { credit, debit }
}
@ -274,7 +284,7 @@ const ActivityFeedTable = ({
!isOpenbook
? '+'
: ''}
{formatFixedDecimals(value, true)}
<FormatNumericValue value={value} isUsd />
</Td>
<Td>
{activity_type !== 'liquidate_token_with_token' ? (
@ -392,24 +402,24 @@ const MobileActivityFeedItem = ({
</p>
<p className="text-right font-mono text-sm text-th-fgd-1">
{isLiquidation ? (
formatFixedDecimals(value, true)
<FormatNumericValue value={value} isUsd />
) : isSwap ? (
<>
<span className="mr-1">
{activity.activity_details.swap_in_amount.toLocaleString(
undefined,
{ maximumFractionDigits: 6 }
)}
<FormatNumericValue
value={activity.activity_details.swap_in_amount}
decimals={6}
/>
</span>
<span className="font-body text-th-fgd-3">
{activity.activity_details.swap_in_symbol}
</span>
<span className="mx-1 font-body text-th-fgd-3">for</span>
<span className="mr-1">
{activity.activity_details.swap_out_amount.toLocaleString(
undefined,
{ maximumFractionDigits: 6 }
)}
<FormatNumericValue
value={activity.activity_details.swap_out_amount}
decimals={6}
/>
</span>
<span className="font-body text-th-fgd-3">
{activity.activity_details.swap_out_symbol}
@ -506,37 +516,51 @@ const MobileActivityFeedItem = ({
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:asset-liquidated')}</p>
<p className="font-mono text-sm text-th-fgd-1">
{formatDecimal(activity.activity_details.asset_amount)}{' '}
<FormatNumericValue
value={activity.activity_details.asset_amount}
/>{' '}
<span className="font-body">
{activity.activity_details.asset_symbol}
</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
{formatFixedDecimals(activity.activity_details.asset_price, true)}
<FormatNumericValue
value={activity.activity_details.asset_price}
isUsd
/>
</p>
<p className="font-mono text-xs text-th-fgd-3">
{formatFixedDecimals(
activity.activity_details.asset_price *
activity.activity_details.asset_amount,
true
)}
<FormatNumericValue
value={
activity.activity_details.asset_price *
activity.activity_details.asset_amount
}
isUsd
/>
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:asset-returned')}</p>
<p className="font-mono text-sm text-th-fgd-1">
{formatDecimal(activity.activity_details.liab_amount)}{' '}
<FormatNumericValue
value={activity.activity_details.liab_amount}
/>{' '}
<span className="font-body">
{activity.activity_details.liab_symbol}
</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
{formatFixedDecimals(activity.activity_details.liab_price, true)}
<FormatNumericValue
value={activity.activity_details.liab_price}
isUsd
/>
</p>
<p className="font-mono text-xs text-th-fgd-3">
{formatFixedDecimals(
activity.activity_details.liab_price *
activity.activity_details.liab_amount,
true
)}
<FormatNumericValue
value={
activity.activity_details.liab_price *
activity.activity_details.liab_amount
}
isUsd
/>
</p>
</div>
<div className="col-span-2 flex justify-center pt-3">

View File

@ -3,17 +3,30 @@ import {
HealthType,
toUiDecimalsForQuote,
} from '@blockworks-foundation/mango-v4'
import { formatFixedDecimals } from '../../utils/numbers'
import { useTranslation } from 'next-i18next'
import useMangoAccount from 'hooks/useMangoAccount'
import useMangoGroup from 'hooks/useMangoGroup'
import { useMemo } from 'react'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const SummaryItem = ({ label, value }: { label: string; value: string }) => {
const SummaryItem = ({
label,
value,
isUsd,
suffix,
}: {
label: string
value: number
isUsd?: boolean
suffix?: string
}) => {
return (
<div>
<p className="text-sm text-th-fgd-3">{label}</p>
<p className="font-mono text-sm text-th-fgd-1">{value}</p>
<p className="font-mono text-sm text-th-fgd-1">
<FormatNumericValue value={value} decimals={2} isUsd={isUsd} />
{suffix}
</p>
</div>
)
}
@ -56,20 +69,11 @@ const MangoAccountSummary = () => {
return (
<div className="space-y-2">
<SummaryItem
label={t('account-value')}
value={formatFixedDecimals(accountValue, true, true)}
/>
<SummaryItem label={t('health')} value={`${health}%`} />
<SummaryItem
label={t('free-collateral')}
value={formatFixedDecimals(freeCollateral, true, true)}
/>
<SummaryItem label={t('leverage')} value={`${leverage.toFixed(2)}x`} />
<SummaryItem
label={t('pnl')}
value={formatFixedDecimals(pnl, true, true)}
/>
<SummaryItem label={t('account-value')} value={accountValue} isUsd />
<SummaryItem label={t('health')} value={health} suffix="%" />
<SummaryItem label={t('free-collateral')} value={freeCollateral} isUsd />
<SummaryItem label={t('leverage')} value={leverage} suffix="x" />
<SummaryItem label={t('pnl')} value={pnl} isUsd />
</div>
)
}

View File

@ -14,7 +14,7 @@ import {
import { ExclamationCircleIcon, TrashIcon } from '@heroicons/react/20/solid'
import useUnsettledPerpPositions from 'hooks/useUnsettledPerpPositions'
import { getMultipleAccounts } from '@project-serum/anchor/dist/cjs/utils/rpc'
import { formatFixedDecimals } from 'utils/numbers'
import { formatCurrencyValue } from 'utils/numbers'
const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
const { t } = useTranslation(['close-account'])
@ -135,14 +135,10 @@ const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
{t('withdraw-assets-worth', {
value:
mangoAccount && group
? formatFixedDecimals(
? formatCurrencyValue(
toUiDecimalsForQuote(
mangoAccount!
.current!.getEquity(group)
.toNumber()
),
false,
true
mangoAccount!.current!.getEquity(group)
)
)
: 0,
})}

View File

@ -8,7 +8,6 @@ import dayjs from 'dayjs'
import Change from '@components/shared/Change'
import SheenLoader from '@components/shared/SheenLoader'
import { NoSymbolIcon } from '@heroicons/react/20/solid'
import { formatDecimal } from 'utils/numbers'
interface PnlChange {
time: string
@ -110,10 +109,7 @@ const PnlHistoryModal = ({
key={v.time + v.pnlChange}
>
<p>{dayjs(v.time).format('YYYY-MM-DD')}</p>
<Change
change={Number(formatDecimal(v.pnlChange, 2))}
prefix="$"
/>
<Change change={v.pnlChange} prefix="$" />
</div>
))}
</div>

View File

@ -25,11 +25,6 @@ import {
} from 'react'
import { ALPHA_DEPOSIT_LIMIT } from 'utils/constants'
import { notify } from 'utils/notifications'
import {
floorToDecimal,
formatDecimal,
formatFixedDecimals,
} from 'utils/numbers'
import ActionTokenList from '../account/ActionTokenList'
import ButtonGroup from '../forms/ButtonGroup'
import Input from '../forms/Input'
@ -48,6 +43,7 @@ import { useEnhancedWallet } from '../wallet/EnhancedWalletProvider'
import Modal from '../shared/Modal'
import NumberFormat, { NumberFormatValues } from 'react-number-format'
import { withValueLimit } from '@components/swap/SwapForm'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const UserSetupModal = ({
isOpen,
@ -169,10 +165,7 @@ const UserSetupModal = ({
key,
value,
tokenDecimals: walletBalance.maxDecimals,
walletBalance: floorToDecimal(
walletBalance.maxAmount,
walletBalance.maxDecimals
).toNumber(),
walletBalance: walletBalance.maxAmount,
walletBalanceValue: walletBalance.maxAmount * value?.[0].uiPrice,
}
})
@ -213,14 +206,22 @@ const UserSetupModal = ({
tokenMax.amount < Number(depositAmount) ||
(depositToken === 'SOL' && maxSolDeposit <= 0)
const setMax = useCallback(() => {
const max = new Decimal(tokenMax.amount).toDecimalPlaces(
tokenMax.decimals,
Decimal.ROUND_FLOOR
)
setDepositAmount(max.toString())
setSizePercentage('100')
}, [tokenMax])
const handleSizePercentage = useCallback(
(percentage: string) => {
setSizePercentage(percentage)
let amount = new Decimal(tokenMax.amount).mul(percentage).div(100)
if (percentage !== '100') {
amount = floorToDecimal(amount, tokenMax.decimals)
}
const amount = new Decimal(tokenMax.amount)
.mul(percentage)
.div(100)
.toDecimalPlaces(tokenMax.decimals, Decimal.ROUND_FLOOR)
setDepositAmount(amount.toString())
},
[tokenMax]
@ -422,20 +423,10 @@ const UserSetupModal = ({
<Label text={t('amount')} />
<MaxAmountButton
className="mb-2"
decimals={tokenMax.decimals}
label="Max"
onClick={() => {
setDepositAmount(
floorToDecimal(
tokenMax.amount,
tokenMax.decimals
).toFixed()
)
setSizePercentage('100')
}}
value={floorToDecimal(
tokenMax.amount,
tokenMax.decimals
).toFixed()}
onClick={setMax}
value={tokenMax.amount}
/>
</div>
<div className="mb-6 grid grid-cols-2">
@ -492,16 +483,19 @@ const UserSetupModal = ({
<p className="font-mono text-th-fgd-2">
{depositAmount ? (
<>
{formatDecimal(
Number(depositAmount),
depositBank.mintDecimals
)}{' '}
<FormatNumericValue
value={depositAmount}
decimals={depositBank.mintDecimals}
/>{' '}
<span className="text-xs text-th-fgd-3">
(
{formatFixedDecimals(
depositBank.uiPrice * Number(depositAmount),
true
)}
<FormatNumericValue
value={
depositBank.uiPrice * Number(depositAmount)
}
decimals={2}
isUsd
/>
)
</span>
</>

View File

@ -1,20 +1,23 @@
import Decimal from 'decimal.js'
import FormatNumericValue from './FormatNumericValue'
const AmountWithValue = ({
amount,
amountDecimals,
value,
stacked,
}: {
amount: Decimal | number | string
value: string
amountDecimals?: number
value: number | string
stacked?: boolean
}) => {
return (
<p className={`font-mono text-th-fgd-2 ${stacked ? 'text-right' : ''}`}>
<>
{amount}{' '}
<FormatNumericValue value={amount} decimals={amountDecimals} />{' '}
<span className={`text-th-fgd-4 ${stacked ? 'block' : ''}`}>
{value}
<FormatNumericValue value={value} decimals={2} isUsd={true} />
</span>
</>
</p>

View File

@ -10,10 +10,8 @@ import { useRouter } from 'next/router'
import { useCallback, useMemo } from 'react'
import {
floorToDecimal,
formatDecimal,
formatFixedDecimals,
formatNumericValue,
getDecimalCount,
trimDecimals,
} from 'utils/numbers'
import { breakpoints } from 'utils/theme'
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
@ -25,6 +23,7 @@ import AmountWithValue from './AmountWithValue'
import ConnectEmptyState from './ConnectEmptyState'
import { useWallet } from '@solana/wallet-adapter-react'
import Decimal from 'decimal.js'
import FormatNumericValue from './FormatNumericValue'
const BalancesTable = () => {
const { t } = useTranslation(['common', 'trade'])
@ -109,41 +108,29 @@ const BalancesTable = () => {
<Td className="text-right">
<Balance bank={bank} />
<p className="text-sm text-th-fgd-4">
{mangoAccount
? `${formatFixedDecimals(
mangoAccount.getTokenBalanceUi(bank) * bank.uiPrice,
true
)}`
: '$0.00'}
<FormatNumericValue
value={
mangoAccount
? mangoAccount.getTokenBalanceUi(bank) * bank.uiPrice
: 0
}
isUsd
/>
</p>
</Td>
<Td className="text-right">
<AmountWithValue
amount={
inOrders
? formatDecimal(Number(inOrders), bank.mintDecimals)
: '0'
}
value={formatFixedDecimals(
inOrders * bank.uiPrice,
true,
true
)}
amount={inOrders}
amountDecimals={bank.mintDecimals}
value={inOrders * bank.uiPrice}
stacked
/>
</Td>
<Td className="text-right">
<AmountWithValue
amount={
unsettled
? formatDecimal(Number(unsettled), bank.mintDecimals)
: '0'
}
value={formatFixedDecimals(
unsettled * bank.uiPrice,
true,
true
)}
amount={unsettled}
amountDecimals={bank.mintDecimals}
value={unsettled * bank.uiPrice}
stacked
/>
</Td>
@ -186,24 +173,33 @@ const BalancesTable = () => {
<div className="mb-0.5 flex justify-end space-x-1.5">
<Balance bank={bank} />
<span className="text-sm text-th-fgd-4">
{mangoAccount
? `${formatFixedDecimals(
mangoAccount.getTokenBalanceUi(bank) * bank.uiPrice,
false,
true
)}`
: '$0.00'}
<FormatNumericValue
value={
mangoAccount
? mangoAccount.getTokenBalanceUi(bank) * bank.uiPrice
: 0
}
isUsd
/>
</span>
</div>
<div className="flex space-x-2">
<p className="text-xs text-th-fgd-4">
{t('trade:in-orders')}:{' '}
<span className="font-mono text-th-fgd-3">{inOrders}</span>
<span className="font-mono text-th-fgd-3">
<FormatNumericValue
value={inOrders}
decimals={bank.mintDecimals}
/>
</span>
</p>
<p className="text-xs text-th-fgd-4">
{t('trade:unsettled')}:{' '}
<span className="font-mono text-th-fgd-3">
{unsettled ? unsettled.toFixed(bank.mintDecimals) : 0}
<FormatNumericValue
value={unsettled}
decimals={bank.mintDecimals}
/>
</span>
</p>
</div>
@ -253,29 +249,32 @@ const Balance = ({ bank }: { bank: Bank }) => {
}
let minOrderDecimals: number
let tickSize: number
let tickDecimals: number
if (selectedMarket instanceof Serum3Market) {
const market = group.getSerum3ExternalMarket(
selectedMarket.serumMarketExternal
)
minOrderDecimals = getDecimalCount(market.minOrderSize)
tickSize = getDecimalCount(market.tickSize)
tickDecimals = getDecimalCount(market.tickSize)
} else {
minOrderDecimals = getDecimalCount(selectedMarket.minOrderSize)
tickSize = getDecimalCount(selectedMarket.tickSize)
tickDecimals = getDecimalCount(selectedMarket.tickSize)
}
if (type === 'quote') {
const trimmedBalance = trimDecimals(balance, tickSize)
const baseSize = trimDecimals(trimmedBalance / price, minOrderDecimals)
const quoteSize = trimDecimals(baseSize * price, tickSize)
const floorBalance = floorToDecimal(balance, tickDecimals).toNumber()
const baseSize = floorToDecimal(
floorBalance / price,
minOrderDecimals
).toNumber()
const quoteSize = floorToDecimal(baseSize * price, tickDecimals)
set((s) => {
s.tradeForm.baseSize = baseSize.toString()
s.tradeForm.quoteSize = quoteSize.toString()
})
} else {
const baseSize = trimDecimals(balance, minOrderDecimals)
const quoteSize = trimDecimals(baseSize * price, tickSize)
const baseSize = floorToDecimal(balance, minOrderDecimals).toNumber()
const quoteSize = floorToDecimal(baseSize * price, tickDecimals)
set((s) => {
s.tradeForm.baseSize = baseSize.toString()
s.tradeForm.quoteSize = quoteSize.toString()
@ -333,21 +332,21 @@ const Balance = ({ bank }: { bank: Bank }) => {
handleTradeFormBalanceClick(Math.abs(balance), isBaseOrQuote)
}
>
{formatDecimal(balance, bank.mintDecimals)}
<FormatNumericValue value={balance} decimals={bank.mintDecimals} />
</LinkButton>
) : asPath.includes('/swap') ? (
<LinkButton
className="font-normal underline-offset-4"
onClick={() =>
handleSwapFormBalanceClick(
floorToDecimal(balance, bank.mintDecimals).toNumber()
Number(formatNumericValue(balance, bank.mintDecimals))
)
}
>
{formatDecimal(balance, bank.mintDecimals)}
<FormatNumericValue value={balance} decimals={bank.mintDecimals} />
</LinkButton>
) : (
formatDecimal(balance, bank.mintDecimals)
<FormatNumericValue value={balance} decimals={bank.mintDecimals} />
)}
</p>
)

View File

@ -1,6 +1,6 @@
import { MinusSmallIcon } from '@heroicons/react/20/solid'
import { formatDecimal } from 'utils/numbers'
import { DownTriangle, UpTriangle } from './DirectionTriangles'
import FormatNumericValue from './FormatNumericValue'
const Change = ({
change,
@ -42,7 +42,10 @@ const Change = ({
}`}
>
{prefix ? prefix : ''}
{isNaN(change) ? '0.00' : formatDecimal(Math.abs(change), 2)}
<FormatNumericValue
value={isNaN(change) ? '0.00' : Math.abs(change)}
decimals={2}
/>
{suffix ? suffix : ''}
</p>
</div>

View File

@ -1,5 +1,5 @@
import { useMemo } from 'react'
import { formatFixedDecimals } from 'utils/numbers'
import FormatNumericValue from './FormatNumericValue'
interface DailyRangeProps {
high: number
@ -16,7 +16,7 @@ const DailyRange = ({ high, low, price }: DailyRangeProps) => {
<div className="flex items-center justify-between md:block">
<div className="flex items-center">
<span className={`pr-2 font-mono text-th-fgd-2`}>
{formatFixedDecimals(low, true)}
<FormatNumericValue value={low} isUsd />
</span>
<div className="mt-[2px] flex h-2 w-32 rounded-sm bg-th-bkg-3">
<div
@ -27,7 +27,7 @@ const DailyRange = ({ high, low, price }: DailyRangeProps) => {
></div>
</div>
<span className={`pl-2 font-mono text-th-fgd-2`}>
{formatFixedDecimals(high, true)}
<FormatNumericValue value={high} isUsd />
</span>
</div>
</div>

View File

@ -21,10 +21,11 @@ import ChartRangeButtons from './ChartRangeButtons'
import Change from './Change'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
import { formatFixedDecimals } from 'utils/numbers'
import { formatNumericValue } from 'utils/numbers'
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
import { AxisDomain } from 'recharts/types/util/types'
import { useTranslation } from 'next-i18next'
import FormatNumericValue from './FormatNumericValue'
dayjs.extend(relativeTime)
@ -155,17 +156,18 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
play
numbers={`${
mouseData[yKey] < 0 ? '-' : ''
}${prefix}${formatFixedDecimals(
}${prefix}${formatNumericValue(
Math.abs(mouseData[yKey])
)}${suffix}`}
/>
) : (
<span>
{`${
mouseData[yKey] < 0 ? '-' : ''
}${prefix}${formatFixedDecimals(
Math.abs(mouseData[yKey])
)}${suffix}`}
{mouseData[yKey] < 0 ? '-' : ''}
{prefix}
<FormatNumericValue
value={Math.abs(mouseData[yKey])}
/>
{suffix}
</span>
)}
{!hideChange ? (
@ -202,17 +204,18 @@ const DetailedAreaChart: FunctionComponent<DetailedAreaChartProps> = ({
play
numbers={`${
data[data.length - 1][yKey] < 0 ? '-' : ''
}${prefix}${formatFixedDecimals(
}${prefix}${formatNumericValue(
Math.abs(data[data.length - 1][yKey])
)}${suffix}`}
/>
) : (
<span>
{`${
data[data.length - 1][yKey] < 0 ? '-' : ''
}${prefix}${formatFixedDecimals(
Math.abs(data[data.length - 1][yKey])
)}${suffix}`}
{data[data.length - 1][yKey] < 0 ? '-' : ''}
{prefix}
<FormatNumericValue
value={Math.abs(data[data.length - 1][yKey])}
/>
{suffix}
</span>
)}
{!hideChange ? (

View File

@ -0,0 +1,24 @@
import Decimal from 'decimal.js'
import { formatCurrencyValue, formatNumericValue } from 'utils/numbers'
const FormatNumericValue = ({
value,
decimals,
isUsd,
roundUp,
}: {
value: Decimal | number | string
decimals?: number
isUsd?: boolean
roundUp?: boolean
}) => {
return (
<span>
{isUsd
? formatCurrencyValue(value, decimals)
: formatNumericValue(value, decimals, roundUp)}
</span>
)
}
export default FormatNumericValue

View File

@ -1,17 +1,21 @@
import Decimal from 'decimal.js'
import { LinkButton } from './Button'
import FormatNumericValue from './FormatNumericValue'
const MaxAmountButton = ({
className,
decimals,
disabled,
label,
onClick,
value,
}: {
className?: string
decimals: number
disabled?: boolean
label: string
onClick: () => void
value: string
value: number | string | Decimal
}) => {
return (
<LinkButton
@ -20,7 +24,9 @@ const MaxAmountButton = ({
onClick={onClick}
>
<p className="mr-1 text-th-fgd-4">{label}:</p>
<span className="font-mono text-th-fgd-2 underline">{value}</span>
<span className="font-mono text-th-fgd-2 underline">
<FormatNumericValue value={value} decimals={decimals} />
</span>
</LinkButton>
)
}

View File

@ -6,7 +6,8 @@ import mangoStore from '@store/mangoStore'
import { useTranslation } from 'next-i18next'
import dynamic from 'next/dynamic'
import { useMemo } from 'react'
import { formatFixedDecimals } from 'utils/numbers'
import { formatYAxis } from 'utils/formatting'
import { formatNumericValue } from 'utils/numbers'
const DetailedAreaChart = dynamic(
() => import('@components/shared/DetailedAreaChart'),
{ ssr: false }
@ -72,7 +73,7 @@ const PerpMarketDetails = ({
daysToShow={'999'}
heightClass="h-64"
prefix="$"
tickFormat={(x) => formatFixedDecimals(x, true)}
tickFormat={(x) => formatYAxis(x)}
title={t('price')}
xKey="date_hour"
yKey={'price'}
@ -95,7 +96,7 @@ const PerpMarketDetails = ({
daysToShow={'999'}
heightClass="h-64"
suffix="%"
tickFormat={(x) => formatFixedDecimals(x)}
tickFormat={(x) => formatNumericValue(x)}
title={t('hourly-funding')}
xKey="date_hour"
yKey={'funding_rate_hourly'}
@ -107,7 +108,7 @@ const PerpMarketDetails = ({
daysToShow={'999'}
heightClass="h-64"
suffix="%"
tickFormat={(x) => formatFixedDecimals(x)}
tickFormat={(x) => formatNumericValue(x)}
title={t('instantaneous-funding')}
xKey="date_hour"
yKey={'instantaneous_funding_rate'}

View File

@ -4,7 +4,6 @@ import { useTheme } from 'next-themes'
import { useViewport } from '../../hooks/useViewport'
import mangoStore from '@store/mangoStore'
import { COLORS } from '../../styles/colors'
import { formatFixedDecimals } from '../../utils/numbers'
import { breakpoints } from '../../utils/theme'
import ContentBox from '../shared/ContentBox'
import Change from '../shared/Change'
@ -15,6 +14,7 @@ import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
import { usePerpFundingRate } from '@components/trade/PerpFundingRate'
import { IconButton } from '@components/shared/Button'
import { ChevronRightIcon } from '@heroicons/react/20/solid'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const SimpleAreaChart = dynamic(
() => import('@components/shared/SimpleAreaChart'),
{ ssr: false }
@ -86,7 +86,9 @@ const PerpMarketsTable = ({
</Td>
<Td>
<div className="flex flex-col text-right">
<p>{formatFixedDecimals(market.uiPrice, true)}</p>
<p>
<FormatNumericValue value={market.uiPrice} isUsd />
</p>
</div>
</Td>
<Td>
@ -126,10 +128,12 @@ const PerpMarketsTable = ({
</span>
</p>
<p className="text-xs text-th-fgd-4">
{formatFixedDecimals(
market.openInterest.toNumber() * market.uiPrice,
true
)}
<FormatNumericValue
value={
market.openInterest.toNumber() * market.uiPrice
}
isUsd
/>
</p>
</div>
</Td>
@ -213,7 +217,7 @@ const MobilePerpMarketItem = ({ market }: { market: PerpMarket }) => {
<p className="text-th-fgd-1">{market.name}</p>
<div className="flex items-center space-x-3">
<p className="font-mono">
{formatFixedDecimals(market.uiPrice, true)}
<FormatNumericValue value={market.uiPrice} isUsd />
</p>
<Change change={change} suffix="%" />
</div>

View File

@ -5,7 +5,6 @@ import { useMemo } from 'react'
import { useViewport } from '../../hooks/useViewport'
import mangoStore from '@store/mangoStore'
import { COLORS } from '../../styles/colors'
import { formatFixedDecimals } from '../../utils/numbers'
import { breakpoints } from '../../utils/theme'
import ContentBox from '../shared/ContentBox'
import Change from '../shared/Change'
@ -14,6 +13,7 @@ import dynamic from 'next/dynamic'
import { useCoingecko } from 'hooks/useCoingecko'
import useMangoGroup from 'hooks/useMangoGroup'
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const SimpleAreaChart = dynamic(
() => import('@components/shared/SimpleAreaChart'),
{ ssr: false }
@ -72,9 +72,11 @@ const SpotMarketsTable = () => {
<Td>
<div className="flex flex-col text-right">
<p>
{oraclePrice
? formatFixedDecimals(oraclePrice, true)
: ''}
{oraclePrice ? (
<FormatNumericValue value={oraclePrice} isUsd />
) : (
''
)}
</p>
</div>
</Td>
@ -174,7 +176,11 @@ const MobileSpotMarketItem = ({ market }: { market: Serum3Market }) => {
<p className="text-th-fgd-1">{market.name}</p>
<div className="flex items-center space-x-3">
<p className="font-mono">
{bank?.uiPrice ? formatFixedDecimals(bank.uiPrice, true) : '-'}
{bank?.uiPrice ? (
<FormatNumericValue value={bank.uiPrice} isUsd />
) : (
'-'
)}
</p>
<Change change={change} suffix="%" />
</div>

View File

@ -8,7 +8,6 @@ import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { Fragment, useEffect, useMemo, useState } from 'react'
import { useViewport } from '../../hooks/useViewport'
import { formatDecimal, formatFixedDecimals } from '../../utils/numbers'
import { breakpoints } from '../../utils/theme'
import { IconButton, LinkButton } from '../shared/Button'
import ContentBox from '../shared/ContentBox'
@ -20,6 +19,7 @@ import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
import useMangoGroup from 'hooks/useMangoGroup'
import mangoStore from '@store/mangoStore'
import AmountWithValue from '@components/shared/AmountWithValue'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const TokenStats = () => {
const { t } = useTranslation(['common', 'token'])
@ -96,11 +96,12 @@ const TokenStats = () => {
</Tooltip>
</div>
</Th>
<Th />
</TrHead>
</thead>
<tbody>
{banks.map(({ key, value }) => {
const bank = value[0]
const bank: Bank = value[0]
let logoURI
if (mangoTokens?.length) {
@ -130,45 +131,47 @@ const TokenStats = () => {
</Td>
<Td>
<div className="flex flex-col text-right">
<p>{formatFixedDecimals(deposits)}</p>
<p className="text-th-fgd-4">
{formatFixedDecimals(deposits * price, true, true)}
</p>
<AmountWithValue
amount={deposits.toFixed(4)}
value={(deposits * price).toFixed(2)}
stacked
/>
</div>
</Td>
<Td>
<div className="flex flex-col text-right">
<p>{formatFixedDecimals(borrows)}</p>
<p className="text-th-fgd-4">
{formatFixedDecimals(borrows * price, true, true)}
</p>
<AmountWithValue
amount={borrows.toFixed(4)}
value={(borrows * price).toFixed(2)}
stacked
/>
</div>
</Td>
<Td>
<div className="flex flex-col text-right">
<p>
{available > 0 ? formatFixedDecimals(available) : '0'}
</p>
<p className="text-th-fgd-4">
{available > 0
? formatFixedDecimals(available * price, false, true)
: '$0.00'}
</p>
<AmountWithValue
amount={available}
amountDecimals={bank.mintDecimals}
value={(available * price).toFixed(2)}
stacked
/>
</div>
</Td>
<Td>
<div className="flex justify-end space-x-1.5">
<p className="text-th-up">
{formatDecimal(bank.getDepositRateUi(), 2, {
fixed: true,
})}
<FormatNumericValue
value={bank.getDepositRateUi()}
decimals={2}
/>
%
</p>
<span className="text-th-fgd-4">|</span>
<p className="text-th-down">
{formatDecimal(bank.getBorrowRateUi(), 2, {
fixed: true,
})}
<FormatNumericValue
value={bank.getBorrowRateUi()}
decimals={2}
/>
%
</p>
</div>
@ -177,11 +180,10 @@ const TokenStats = () => {
<div className="flex flex-col text-right">
<p>
{bank.uiDeposits() > 0
? formatDecimal(
(bank.uiBorrows() / bank.uiDeposits()) * 100,
1,
{ fixed: true }
)
? (
(bank.uiBorrows() / bank.uiDeposits()) *
100
).toFixed(1)
: '0.0'}
%
</p>
@ -243,12 +245,8 @@ const TokenStats = () => {
{t('total-deposits')}
</p>
<AmountWithValue
amount={formatFixedDecimals(deposits)}
value={formatFixedDecimals(
deposits * price,
true,
true
)}
amount={deposits.toFixed(4)}
value={(deposits * price).toFixed(2)}
stacked
/>
</div>
@ -257,8 +255,8 @@ const TokenStats = () => {
{t('total-borrows')}
</p>
<AmountWithValue
amount={formatFixedDecimals(borrows)}
value={formatFixedDecimals(borrows * price, true, true)}
amount={borrows.toFixed(4)}
value={(borrows * price).toFixed(2)}
stacked
/>
</div>
@ -292,11 +290,19 @@ const TokenStats = () => {
<p className="text-xs text-th-fgd-3">{t('rates')}</p>
<p className="space-x-2">
<span className="font-mono text-th-up">
{formatDecimal(bank.getDepositRate().toNumber(), 2)}%
<FormatNumericValue
value={bank.getDepositRateUi()}
decimals={2}
/>
%
</span>
<span className="font-normal text-th-fgd-4">|</span>
<span className="font-mono text-th-down">
{formatDecimal(bank.getBorrowRate().toNumber(), 2)}%
<FormatNumericValue
value={bank.getBorrowRateUi()}
decimals={2}
/>
%
</span>
</p>
</div>
@ -306,11 +312,10 @@ const TokenStats = () => {
</p>
<p className="font-mono text-th-fgd-1">
{bank.uiDeposits() > 0
? formatDecimal(
(bank.uiBorrows() / bank.uiDeposits()) * 100,
1,
{ fixed: true }
)
? (
(bank.uiBorrows() / bank.uiDeposits()) *
100
).toFixed(1)
: '0.0'}
%
</p>
@ -322,17 +327,15 @@ const TokenStats = () => {
</p>
</Tooltip>
<p className="text-th-fgd-1">
{available > 0 ? formatFixedDecimals(available) : '0'}{' '}
<FormatNumericValue
value={available}
decimals={bank.mintDecimals}
/>{' '}
<span className="text-th-fgd-4">
(
{available > 0
? formatFixedDecimals(
available * price,
false,
true
)
: '$0.00'}
)
<FormatNumericValue
value={(available * price).toFixed(2)}
isUsd
/>
</span>
</p>
</div>

View File

@ -1,7 +1,8 @@
import MaxAmountButton from '@components/shared/MaxAmountButton'
import mangoStore from '@store/mangoStore'
import Decimal from 'decimal.js'
import { useTranslation } from 'next-i18next'
import { floorToDecimal } from 'utils/numbers'
import { formatNumericValue } from 'utils/numbers'
import { useTokenMax } from './useTokenMax'
const MaxSwapAmount = ({
@ -21,29 +22,26 @@ const MaxSwapAmount = ({
if (mangoAccountLoading) return null
const maxBalanceValue = floorToDecimal(
tokenMax.toNumber(),
decimals
).toFixed()
const maxBorrowValue = floorToDecimal(
amountWithBorrow.toNumber(),
decimals
).toFixed()
const setMax = (value: Decimal) => {
setAmountIn(formatNumericValue(value, decimals))
}
return (
<div className="flex flex-wrap justify-end pl-6 text-xs">
<MaxAmountButton
className="mb-0.5"
decimals={decimals}
label="Bal"
onClick={() => setAmountIn(maxBalanceValue)}
value={maxBalanceValue}
onClick={() => setMax(tokenMax)}
value={tokenMax}
/>
{useMargin ? (
<MaxAmountButton
className="mb-0.5 ml-2"
decimals={decimals}
label={t('max')}
onClick={() => setAmountIn(maxBorrowValue)}
value={maxBorrowValue}
onClick={() => setMax(amountWithBorrow)}
value={amountWithBorrow}
/>
) : null}
</div>

View File

@ -1,9 +1,9 @@
import { Dispatch, SetStateAction } from 'react'
import { RouteInfo, Token } from '../../types/jupiter'
import { formatDecimal } from '../../utils/numbers'
import Modal from '../shared/Modal'
import useJupiterMints from '../../hooks/useJupiterMints'
import FormatNumericValue from '@components/shared/FormatNumericValue'
type RoutesModalProps = {
onClose: () => void
@ -92,10 +92,12 @@ const RoutesModal = ({
</div>
</div>
<div className="text-lg">
{formatDecimal(
route.outAmount / 10 ** (outputTokenInfo?.decimals || 1),
6
)}
<FormatNumericValue
value={
route.outAmount / 10 ** (outputTokenInfo?.decimals || 1)
}
decimals={outputTokenInfo?.decimals || 6}
/>
</div>
</div>
</button>

View File

@ -10,7 +10,7 @@ import useMangoAccount from 'hooks/useMangoAccount'
import useJupiterMints from 'hooks/useJupiterMints'
import useMangoGroup from 'hooks/useMangoGroup'
import { PublicKey } from '@solana/web3.js'
import { formatDecimal } from 'utils/numbers'
import FormatNumericValue from '@components/shared/FormatNumericValue'
// const generateSearchTerm = (item: Token, searchValue: string) => {
// const normalizedSearchValue = searchValue.toLowerCase()
@ -75,9 +75,17 @@ const TokenItem = ({
token.amountWithBorrow &&
token.decimals ? (
<p className="font-mono text-sm text-th-fgd-2">
{useMargin
? formatDecimal(token.amountWithBorrow.toNumber(), token.decimals)
: formatDecimal(token.amount.toNumber(), token.decimals)}
{useMargin ? (
<FormatNumericValue
value={token.amountWithBorrow}
decimals={token.decimals}
/>
) : (
<FormatNumericValue
value={token.amount}
decimals={token.decimals}
/>
)}
</p>
) : null}
</button>

View File

@ -13,12 +13,7 @@ import { IconButton, LinkButton } from '../shared/Button'
import { Transition } from '@headlessui/react'
import SheenLoader from '../shared/SheenLoader'
import mangoStore from '@store/mangoStore'
import {
countLeadingZeros,
formatDecimal,
formatFixedDecimals,
trimDecimals,
} from '../../utils/numbers'
import { countLeadingZeros, floorToDecimal } from '../../utils/numbers'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { PAGINATION_PAGE_LENGTH, PREFERRED_EXPLORER_KEY } from 'utils/constants'
import Tooltip from '@components/shared/Tooltip'
@ -29,6 +24,7 @@ import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
import useMangoAccount from 'hooks/useMangoAccount'
import ConnectEmptyState from '@components/shared/ConnectEmptyState'
import { useWallet } from '@solana/wallet-adapter-react'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const SwapHistoryTable = () => {
const { t } = useTranslation(['common', 'settings', 'swap'])
@ -104,7 +100,7 @@ const SwapHistoryTable = () => {
} = h
const borrowAmount =
loan > 0
? `${trimDecimals(loan, countLeadingZeros(loan) + 2)}`
? `${floorToDecimal(loan, countLeadingZeros(loan) + 2)}`
: 0
const borrowFee =
swap_in_loan_origination_fee > 0
@ -152,8 +148,11 @@ const SwapHistoryTable = () => {
</div>
<div>
<p className="whitespace-nowrap">
{`${formatDecimal(swap_in_amount, inDecimals)}`}
<span className="ml-1 font-body text-th-fgd-3">
<FormatNumericValue
value={swap_in_amount}
decimals={inDecimals}
/>{' '}
<span className="font-body text-th-fgd-3">
{inSymbol}
</span>
</p>
@ -161,7 +160,7 @@ const SwapHistoryTable = () => {
<span className="font-body text-th-fgd-4">
{t('price')}:
</span>{' '}
{formatFixedDecimals(swap_in_price_usd, true)}
<FormatNumericValue value={swap_in_price_usd} isUsd />
</p>
</div>
</div>
@ -178,25 +177,31 @@ const SwapHistoryTable = () => {
</div>
<div>
<p className="whitespace-nowrap">
{`${formatDecimal(swap_out_amount, outDecimals)}`}
<span className="ml-1 font-body text-th-fgd-3">
<FormatNumericValue
value={swap_out_amount}
decimals={outDecimals}
/>{' '}
<span className="font-body text-th-fgd-3">
{outSymbol}
</span>
</p>
<p className="text-xs text-th-fgd-4">
<span className="font-body">{t('price')}:</span>{' '}
{formatFixedDecimals(swap_out_price_usd, true)}
<FormatNumericValue
value={swap_out_price_usd}
isUsd
/>
</p>
</div>
</div>
</Td>
<Td>
<p className="text-right">
{formatFixedDecimals(
swap_out_price_usd * swap_out_amount,
false,
true
)}
<FormatNumericValue
value={swap_out_price_usd * swap_out_amount}
decimals={2}
isUsd
/>
</p>
</Td>
<Td>
@ -268,7 +273,7 @@ const SwapHistoryTable = () => {
const borrowAmount =
loan > 0
? `${trimDecimals(loan, countLeadingZeros(loan) + 2)}`
? `${floorToDecimal(loan, countLeadingZeros(loan) + 2)}`
: 0
const borrowFee =
swap_in_loan_origination_fee > 0
@ -310,7 +315,10 @@ const SwapHistoryTable = () => {
</div>
<div>
<p className="whitespace-nowrap font-mono text-th-fgd-1">
{formatDecimal(swap_in_amount, inDecimals)}{' '}
<FormatNumericValue
value={swap_in_amount}
decimals={inDecimals}
/>{' '}
<span className="font-body text-th-fgd-3">
{inSymbol}
</span>
@ -319,7 +327,7 @@ const SwapHistoryTable = () => {
<span className="font-body text-th-fgd-4">
{t('price')}:
</span>{' '}
{formatFixedDecimals(swap_in_price_usd, true)}
<FormatNumericValue value={swap_in_price_usd} isUsd />
</p>
</div>
</div>
@ -335,7 +343,10 @@ const SwapHistoryTable = () => {
</div>
<div>
<p className="whitespace-nowrap font-mono text-th-fgd-1">
{formatDecimal(swap_out_amount, outDecimals)}{' '}
<FormatNumericValue
value={swap_out_amount}
decimals={outDecimals}
/>{' '}
<span className="font-body text-th-fgd-3">
{outSymbol}
</span>
@ -344,7 +355,10 @@ const SwapHistoryTable = () => {
<span className="font-body text-th-fgd-4">
{t('price')}:
</span>{' '}
{formatFixedDecimals(swap_out_price_usd, true)}
<FormatNumericValue
value={swap_out_price_usd}
isUsd
/>
</p>
</div>
</div>

View File

@ -28,7 +28,7 @@ import {
} from '@heroicons/react/20/solid'
import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { formatDecimal, formatFixedDecimals } from '../../utils/numbers'
import { formatNumericValue } from '../../utils/numbers'
import { notify } from '../../utils/notifications'
import useJupiterMints from '../../hooks/useJupiterMints'
import { RouteInfo } from 'types/jupiter'
@ -43,6 +43,7 @@ import { Disclosure } from '@headlessui/react'
import RoutesModal from './RoutesModal'
import useMangoAccount from 'hooks/useMangoAccount'
import { createAssociatedTokenAccountIdempotentInstruction } from '@blockworks-foundation/mango-v4'
import FormatNumericValue from '@components/shared/FormatNumericValue'
type JupiterRouteInfoProps = {
amountIn: Decimal
@ -372,14 +373,14 @@ const SwapReviewRouteInfo = ({
</div>
</div>
<p className="mb-0.5 flex items-center text-center text-lg">
<span className="mr-1 font-mono text-th-fgd-1">{`${formatFixedDecimals(
amountIn.toNumber()
)}`}</span>{' '}
<span className="mr-1 font-mono text-th-fgd-1">
<FormatNumericValue value={amountIn} />
</span>{' '}
{inputTokenInfo?.symbol}
<ArrowRightIcon className="mx-2 h-5 w-5 text-th-fgd-4" />
<span className="mr-1 font-mono text-th-fgd-1">{`${formatFixedDecimals(
amountOut.toNumber()
)}`}</span>{' '}
<span className="mr-1 font-mono text-th-fgd-1">
<FormatNumericValue value={amountOut} />
</span>{' '}
{`${outputTokenInfo?.symbol}`}
</p>
</div>
@ -396,7 +397,7 @@ const SwapReviewRouteInfo = ({
<span className="font-body text-th-fgd-3">
{inputTokenInfo?.symbol} {' '}
</span>
{formatFixedDecimals(amountOut.div(amountIn).toNumber())}{' '}
<FormatNumericValue value={amountOut.div(amountIn)} />{' '}
<span className="font-body text-th-fgd-3">
{outputTokenInfo?.symbol}
</span>
@ -407,7 +408,7 @@ const SwapReviewRouteInfo = ({
<span className="font-body text-th-fgd-3">
{outputTokenInfo?.symbol} {' '}
</span>
{formatFixedDecimals(amountIn.div(amountOut).toNumber())}{' '}
<FormatNumericValue value={amountIn.div(amountOut)} />{' '}
<span className="font-body text-th-fgd-3">
{inputTokenInfo?.symbol}
</span>
@ -446,17 +447,22 @@ const SwapReviewRouteInfo = ({
</p>
{outputTokenInfo?.decimals && selectedRoute ? (
<p className="text-right font-mono text-sm text-th-fgd-2">
{selectedRoute.swapMode === 'ExactIn'
? formatDecimal(
{selectedRoute.swapMode === 'ExactIn' ? (
<FormatNumericValue
value={
selectedRoute.otherAmountThreshold /
10 ** outputTokenInfo.decimals || 1,
outputTokenInfo.decimals
)
: formatDecimal(
selectedRoute.outAmount /
10 ** outputTokenInfo.decimals || 1,
outputTokenInfo.decimals
)}{' '}
10 ** outputTokenInfo.decimals
}
decimals={outputTokenInfo.decimals}
/>
) : (
<FormatNumericValue
value={
selectedRoute.outAmount / 10 ** outputTokenInfo.decimals
}
decimals={outputTokenInfo.decimals}
/>
)}{' '}
<span className="font-body text-th-fgd-3">
{outputTokenInfo?.symbol}
</span>
@ -468,11 +474,13 @@ const SwapReviewRouteInfo = ({
<p className="text-sm text-th-fgd-3">{t('swap:maximum-cost')}</p>
{inputTokenInfo?.decimals && selectedRoute ? (
<p className="text-right font-mono text-sm text-th-fgd-2">
{formatDecimal(
selectedRoute.otherAmountThreshold /
10 ** inputTokenInfo.decimals || 1,
inputTokenInfo.decimals
)}{' '}
<FormatNumericValue
value={
selectedRoute.otherAmountThreshold /
10 ** inputTokenInfo.decimals
}
decimals={inputTokenInfo.decimals}
/>{' '}
<span className="font-body text-th-fgd-3">
{inputTokenInfo?.symbol}
</span>
@ -494,19 +502,21 @@ const SwapReviewRouteInfo = ({
content={
balance
? t('swap:tooltip-borrow-balance', {
balance: formatFixedDecimals(balance),
borrowAmount: formatFixedDecimals(borrowAmount),
balance: formatNumericValue(balance),
borrowAmount: formatNumericValue(borrowAmount),
token: inputTokenInfo?.symbol,
rate: formatDecimal(inputBank!.getBorrowRateUi(), 2, {
fixed: true,
}),
rate: formatNumericValue(
inputBank!.getBorrowRateUi(),
2
),
})
: t('swap:tooltip-borrow-no-balance', {
borrowAmount: formatFixedDecimals(borrowAmount),
borrowAmount: formatNumericValue(borrowAmount),
token: inputTokenInfo?.symbol,
rate: formatDecimal(inputBank!.getBorrowRateUi(), 2, {
fixed: true,
}),
rate: formatNumericValue(
inputBank!.getBorrowRateUi(),
2
),
})
}
delay={250}
@ -516,7 +526,7 @@ const SwapReviewRouteInfo = ({
</p>
</Tooltip>
<p className="text-right font-mono text-sm text-th-fgd-2">
~{formatFixedDecimals(borrowAmount)}{' '}
~<FormatNumericValue value={borrowAmount} />{' '}
<span className="font-body">{inputTokenInfo?.symbol}</span>
</p>
</div>
@ -595,15 +605,17 @@ const SwapReviewRouteInfo = ({
</Tooltip>
<p className="text-right font-mono text-sm text-th-fgd-2">
~
{formatFixedDecimals(
amountIn
.mul(inputBank!.loanOriginationFeeRate.toFixed())
.toNumber()
)}{' '}
<FormatNumericValue
value={amountIn.mul(
inputBank!.loanOriginationFeeRate.toNumber()
)}
/>{' '}
<span className="font-body">{inputBank!.name}</span> (
{formatFixedDecimals(
inputBank!.loanOriginationFeeRate.toNumber() * 100
)}
<FormatNumericValue
value={
inputBank!.loanOriginationFeeRate.toNumber() * 100
}
/>
%)
</p>
</div>

View File

@ -12,7 +12,7 @@ import {
} from 'recharts'
import FlipNumbers from 'react-flip-numbers'
import ContentBox from '../shared/ContentBox'
import { formatFixedDecimals } from '../../utils/numbers'
import { formatNumericValue } from '../../utils/numbers'
import SheenLoader from '../shared/SheenLoader'
import { COLORS } from '../../styles/colors'
import { useTheme } from 'next-themes'
@ -29,6 +29,7 @@ import { ANIMATION_SETTINGS_KEY } from 'utils/constants'
import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings'
import { useTranslation } from 'next-i18next'
import { ArrowsRightLeftIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
import FormatNumericValue from '@components/shared/FormatNumericValue'
dayjs.extend(relativeTime)
@ -78,7 +79,7 @@ const CustomizedLabel = ({
textAnchor={x && y && x > width / 3 ? 'end' : 'start'}
className="font-mono"
>
{formatFixedDecimals(value)}
{formatNumericValue(value)}
</Text>
)
} else return <div />
@ -220,10 +221,10 @@ const SwapTokenChart = () => {
height={48}
width={35}
play
numbers={formatFixedDecimals(mouseData['price'])}
numbers={formatNumericValue(mouseData['price'])}
/>
) : (
<span>{formatFixedDecimals(mouseData['price'])}</span>
<FormatNumericValue value={mouseData['price']} />
)}
<span
className={`ml-0 mt-2 flex items-center text-sm md:ml-3 md:mt-0`}
@ -243,16 +244,14 @@ const SwapTokenChart = () => {
height={48}
width={35}
play
numbers={formatFixedDecimals(
numbers={formatNumericValue(
chartData[chartData.length - 1]['price']
)}
/>
) : (
<span>
{formatFixedDecimals(
chartData[chartData.length - 1]['price']
)}
</span>
<FormatNumericValue
value={chartData[chartData.length - 1]['price']}
/>
)}
<span
className={`ml-0 mt-2 flex items-center text-sm md:ml-3 md:mt-0`}

View File

@ -2,13 +2,13 @@ import { Bank } from '@blockworks-foundation/mango-v4'
import BorrowRepayModal from '@components/modals/BorrowRepayModal'
import DepositWithdrawModal from '@components/modals/DepositWithdrawModal'
import Button from '@components/shared/Button'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import useMangoGroup from 'hooks/useMangoGroup'
import { useTranslation } from 'next-i18next'
import { useRouter } from 'next/router'
import { useMemo, useState } from 'react'
import { formatDecimal } from 'utils/numbers'
const ActionPanel = ({ bank }: { bank: Bank }) => {
const { t } = useTranslation('common')
@ -46,12 +46,14 @@ const ActionPanel = ({ bank }: { bank: Bank }) => {
{bank.name} {t('balance')}:
</p>
<p className="font-mono text-th-fgd-2">
{mangoAccount
? formatDecimal(
mangoAccount.getTokenBalanceUi(bank),
bank.mintDecimals
)
: 0}
{mangoAccount ? (
<FormatNumericValue
value={mangoAccount.getTokenBalanceUi(bank)}
decimals={bank.mintDecimals}
/>
) : (
0
)}
</p>
</div>
<div className="flex space-x-2">

View File

@ -1,6 +1,7 @@
import { Bank } from '@blockworks-foundation/mango-v4'
import Change from '@components/shared/Change'
import ChartRangeButtons from '@components/shared/ChartRangeButtons'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import { ArrowSmallUpIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
@ -9,7 +10,6 @@ import parse from 'html-react-parser'
import { useTranslation } from 'next-i18next'
import dynamic from 'next/dynamic'
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
import { formatFixedDecimals } from 'utils/numbers'
const PriceChart = dynamic(() => import('@components/token/PriceChart'), {
ssr: false,
})
@ -174,7 +174,7 @@ const CoingeckoStats = ({
<p>{t('token:market-cap')}</p>
<p className="font-mono text-th-fgd-2">
{market_cap?.usd ? (
formatFixedDecimals(market_cap.usd, true)
<FormatNumericValue value={market_cap.usd} isUsd />
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
@ -191,7 +191,7 @@ const CoingeckoStats = ({
<p>{t('token:volume')}</p>
<p className="font-mono text-th-fgd-2">
{total_volume?.usd ? (
formatFixedDecimals(total_volume.usd, true)
<FormatNumericValue value={total_volume.usd} isUsd />
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
@ -205,7 +205,7 @@ const CoingeckoStats = ({
<div className="flex items-center font-mono text-th-fgd-2">
<span className="mr-2">
{ath?.usd ? (
formatFixedDecimals(ath.usd, true)
<FormatNumericValue value={ath.usd} isUsd />
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
@ -228,7 +228,7 @@ const CoingeckoStats = ({
<div className="flex items-center font-mono text-th-fgd-2">
<span className="mr-2">
{atl?.usd ? (
formatFixedDecimals(atl.usd, true)
<FormatNumericValue value={atl.usd} isUsd />
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
@ -250,7 +250,10 @@ const CoingeckoStats = ({
<p>{t('token:fdv')}</p>
<p className="font-mono text-th-fgd-2">
{fully_diluted_valuation?.usd ? (
formatFixedDecimals(fully_diluted_valuation.usd, true)
<FormatNumericValue
value={fully_diluted_valuation.usd}
isUsd
/>
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
@ -269,7 +272,7 @@ const CoingeckoStats = ({
<p>{t('token:circulating-supply')}</p>
<p className="font-mono text-th-fgd-2">
{circulating_supply ? (
formatFixedDecimals(circulating_supply)
<FormatNumericValue value={circulating_supply} />
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
@ -285,7 +288,7 @@ const CoingeckoStats = ({
<p>{t('token:total-supply')}</p>
<p className="font-mono text-th-fgd-2">
{total_supply ? (
formatFixedDecimals(total_supply)
<FormatNumericValue value={total_supply} />
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}
@ -298,7 +301,7 @@ const CoingeckoStats = ({
<p>{t('token:max-supply')}</p>
<p className="font-mono text-th-fgd-2">
{max_supply ? (
formatFixedDecimals(max_supply)
<FormatNumericValue value={max_supply} />
) : (
<span className="font-body text-th-fgd-4">
{t('unavailable')}

View File

@ -3,7 +3,7 @@ import { useTheme } from 'next-themes'
import { useMemo } from 'react'
import { Area, AreaChart, ResponsiveContainer, XAxis, YAxis } from 'recharts'
import { COLORS } from 'styles/colors'
import { formatFixedDecimals } from 'utils/numbers'
import { formatYAxis } from 'utils/formatting'
const PriceChart = ({
prices,
@ -71,7 +71,7 @@ const PriceChart = ({
fill: 'var(--fgd-4)',
fontSize: 10,
}}
tickFormatter={(x) => formatFixedDecimals(x, true)}
tickFormatter={(x) => formatYAxis(x)}
tickLine={false}
/>
</AreaChart>

View File

@ -5,7 +5,7 @@ import Image from 'next/legacy/image'
import { useRouter } from 'next/router'
import { useMemo, useState } from 'react'
import FlipNumbers from 'react-flip-numbers'
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
import { formatCurrencyValue } from 'utils/numbers'
import Link from 'next/link'
import SheenLoader from '@components/shared/SheenLoader'
import Tooltip from '@components/shared/Tooltip'
@ -18,6 +18,7 @@ import ActionPanel from './ActionPanel'
import ChartTabs from './ChartTabs'
import CoingeckoStats from './CoingeckoStats'
import { useQuery } from '@tanstack/react-query'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const DEFAULT_COINGECKO_VALUES = {
ath: 0,
@ -126,10 +127,10 @@ const TokenPage = () => {
play
delay={0.05}
duration={1}
numbers={formatFixedDecimals(bank.uiPrice, true)}
numbers={formatCurrencyValue(bank.uiPrice)}
/>
) : (
<span>{formatFixedDecimals(bank.uiPrice, true)}</span>
<FormatNumericValue value={bank.uiPrice} isUsd />
)}
</div>
{coingeckoTokenInfo.data ? (
@ -156,13 +157,14 @@ const TokenPage = () => {
<p className="tooltip-underline mr-1">{t('utilization')}:</p>
</Tooltip>
<span className="font-mono text-th-fgd-2 no-underline">
{bank.uiDeposits() > 0
? formatDecimal(
(bank.uiBorrows() / bank.uiDeposits()) * 100,
1,
{ fixed: true }
)
: '0.0'}
{bank.uiDeposits() > 0 ? (
<FormatNumericValue
value={(bank.uiBorrows() / bank.uiDeposits()) * 100}
decimals={1}
/>
) : (
'0.0'
)}
%
</span>
</div>

View File

@ -6,7 +6,7 @@ import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useTranslation } from 'next-i18next'
import { useCallback, useMemo } from 'react'
import { floorToDecimal } from 'utils/numbers'
import { formatNumericValue } from 'utils/numbers'
import { useSpotMarketMax } from './SpotSlider'
const MaxSizeButton = ({
@ -57,33 +57,30 @@ const MaxSizeButton = ({
const set = mangoStore.getState().set
set((state) => {
if (side === 'buy') {
state.tradeForm.quoteSize = floorToDecimal(max, tickDecimals).toFixed()
state.tradeForm.quoteSize = formatNumericValue(max, tickDecimals)
if (tradeType === 'Market' || !price) {
state.tradeForm.baseSize = floorToDecimal(
state.tradeForm.baseSize = formatNumericValue(
max / oraclePrice,
minOrderDecimals
).toFixed()
)
} else {
state.tradeForm.baseSize = floorToDecimal(
state.tradeForm.baseSize = formatNumericValue(
max / parseFloat(price),
minOrderDecimals
).toFixed()
)
}
} else {
state.tradeForm.baseSize = floorToDecimal(
max,
minOrderDecimals
).toFixed()
state.tradeForm.baseSize = formatNumericValue(max, minOrderDecimals)
if (tradeType === 'Market' || !price) {
state.tradeForm.quoteSize = floorToDecimal(
state.tradeForm.quoteSize = formatNumericValue(
max * oraclePrice,
minOrderDecimals
).toFixed()
)
} else {
state.tradeForm.quoteSize = floorToDecimal(
state.tradeForm.quoteSize = formatNumericValue(
max * parseFloat(price),
minOrderDecimals
).toFixed()
)
}
}
})
@ -102,20 +99,11 @@ const MaxSizeButton = ({
const max = selectedMarket instanceof Serum3Market ? spotMax : perpMax || 0
const tradePrice = tradeType === 'Market' ? oraclePrice : Number(price)
if (side === 'buy') {
return floorToDecimal(max / tradePrice, minOrderDecimals).toFixed()
return max / tradePrice
} else {
return floorToDecimal(max, minOrderDecimals).toFixed()
return max
}
}, [
perpMax,
spotMax,
selectedMarket,
minOrderDecimals,
tickDecimals,
price,
side,
tradeType,
])
}, [perpMax, spotMax, selectedMarket, price, side, tradeType])
return (
<div className="mb-2 mt-3 flex items-center justify-between">
@ -123,6 +111,7 @@ const MaxSizeButton = ({
<FadeInFadeOut show={!!price}>
<MaxAmountButton
className="text-xs"
decimals={minOrderDecimals}
label={t('max')}
onClick={handleMax}
value={maxAmount}

View File

@ -10,6 +10,7 @@ import {
import Input from '@components/forms/Input'
import { IconButton } from '@components/shared/Button'
import ConnectEmptyState from '@components/shared/ConnectEmptyState'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import Loading from '@components/shared/Loading'
import SideBadge from '@components/shared/SideBadge'
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
@ -30,7 +31,7 @@ import { useViewport } from 'hooks/useViewport'
import { useTranslation } from 'next-i18next'
import { ChangeEvent, useCallback, useState } from 'react'
import { notify } from 'utils/notifications'
import { formatFixedDecimals, getDecimalCount } from 'utils/numbers'
import { getDecimalCount } from 'utils/numbers'
import { breakpoints } from 'utils/theme'
import TableMarketName from './TableMarketName'
@ -325,7 +326,11 @@ const OpenOrders = () => {
</>
)}
<Td className="w-[16.67%] text-right">
{formatFixedDecimals(o.size * o.price, true, true)}
<FormatNumericValue
value={o.size * o.price}
decimals={2}
isUsd
/>
</Td>
<Td className="w-[16.67%]">
<div className="flex justify-end space-x-2">

View File

@ -4,7 +4,7 @@ import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useCallback, useMemo, useState } from 'react'
import { trimDecimals } from 'utils/numbers'
import { floorToDecimal } from 'utils/numbers'
const PerpButtonGroup = ({
minOrderDecimals,
@ -50,28 +50,27 @@ const PerpButtonGroup = ({
set((s) => {
if (s.tradeForm.side === 'buy') {
s.tradeForm.quoteSize = trimDecimals(size, tickDecimals).toFixed(
tickDecimals
)
s.tradeForm.quoteSize = floorToDecimal(size, tickDecimals).toString()
if (Number(s.tradeForm.price)) {
s.tradeForm.baseSize = trimDecimals(
s.tradeForm.baseSize = floorToDecimal(
size / Number(s.tradeForm.price),
minOrderDecimals
).toFixed(minOrderDecimals)
).toString()
} else {
s.tradeForm.baseSize = ''
}
} else if (s.tradeForm.side === 'sell') {
s.tradeForm.baseSize = trimDecimals(size, minOrderDecimals).toFixed(
s.tradeForm.baseSize = floorToDecimal(
size,
minOrderDecimals
)
).toString()
if (Number(s.tradeForm.price)) {
s.tradeForm.quoteSize = trimDecimals(
s.tradeForm.quoteSize = floorToDecimal(
size * Number(s.tradeForm.price),
tickDecimals
).toFixed(tickDecimals)
).toString()
}
}
})

View File

@ -1,6 +1,7 @@
import { PerpMarket, PerpPosition } from '@blockworks-foundation/mango-v4'
import Button, { LinkButton } from '@components/shared/Button'
import ConnectEmptyState from '@components/shared/ConnectEmptyState'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
import { NoSymbolIcon } from '@heroicons/react/20/solid'
import { useWallet } from '@solana/wallet-adapter-react'
@ -10,13 +11,7 @@ import useMangoGroup from 'hooks/useMangoGroup'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useTranslation } from 'next-i18next'
import { useCallback, useState } from 'react'
import {
formatDecimal,
formatFixedDecimals,
getDecimalCount,
numberFormat,
trimDecimals,
} from 'utils/numbers'
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
import { calculateLimitPriceForMarketOrder } from 'utils/tradeForm'
import MarketCloseModal from './MarketCloseModal'
import PerpSideBadge from './PerpSideBadge'
@ -96,10 +91,10 @@ const PerpPositions = () => {
position.marketIndex
)
const basePosition = position.getBasePositionUi(market)
const trimmedBasePosition = trimDecimals(
const floorBasePosition = floorToDecimal(
basePosition,
getDecimalCount(market.minOrderSize)
)
).toNumber()
const isSelectedMarket =
selectedMarket instanceof PerpMarket &&
selectedMarket.perpMarketIndex === position.marketIndex
@ -124,38 +119,46 @@ const PerpPositions = () => {
<p className="flex justify-end">
{isSelectedMarket ? (
<LinkButton
onClick={() => handlePositionClick(trimmedBasePosition)}
onClick={() => handlePositionClick(floorBasePosition)}
>
{Math.abs(trimmedBasePosition)}
<FormatNumericValue
value={Math.abs(basePosition)}
decimals={getDecimalCount(market.minOrderSize)}
/>
</LinkButton>
) : (
Math.abs(trimmedBasePosition)
<FormatNumericValue
value={Math.abs(basePosition)}
decimals={getDecimalCount(market.minOrderSize)}
/>
)}
</p>
</Td>
<Td className="text-right font-mono">
<div>
$
{Math.abs(trimmedBasePosition * market._uiPrice).toFixed(2)}
</div>
<FormatNumericValue
value={floorBasePosition * market._uiPrice}
decimals={2}
isUsd
/>
</Td>
<Td className="text-right font-mono">
<div>
$
{numberFormat.format(
position.getAverageEntryPriceUi(market)
)}
</div>
<FormatNumericValue
value={position.getAverageEntryPriceUi(market)}
isUsd
/>
</Td>
<Td className={`text-right font-mono`}>
<div>{formatDecimal(unsettledPnl, market.baseDecimals)}</div>
<FormatNumericValue
value={unsettledPnl}
decimals={market.baseDecimals}
/>
</Td>
<Td
className={`text-right font-mono ${
cummulativePnl > 0 ? 'text-th-up' : 'text-th-down'
}`}
>
<div>{formatFixedDecimals(cummulativePnl, true)}</div>
<FormatNumericValue value={cummulativePnl} isUsd />
</Td>
<Td className={`text-right`}>
<Button

View File

@ -4,7 +4,7 @@ import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useCallback, useMemo } from 'react'
import { trimDecimals } from 'utils/numbers'
import { floorToDecimal } from 'utils/numbers'
const PerpSlider = ({
minOrderDecimals,
@ -61,20 +61,20 @@ const PerpSlider = ({
if (s.tradeForm.side === 'buy') {
s.tradeForm.quoteSize = val
if (Number(price)) {
s.tradeForm.baseSize = trimDecimals(
s.tradeForm.baseSize = floorToDecimal(
parseFloat(val) / price,
minOrderDecimals
).toFixed(minOrderDecimals)
).toString()
} else {
s.tradeForm.baseSize = ''
}
} else if (s.tradeForm.side === 'sell') {
s.tradeForm.baseSize = val
if (Number(price)) {
s.tradeForm.quoteSize = trimDecimals(
s.tradeForm.quoteSize = floorToDecimal(
parseFloat(val) * price,
tickDecimals
).toFixed(tickDecimals)
).toString()
}
}
})

View File

@ -1,8 +1,7 @@
import useInterval from '@components/shared/useInterval'
import mangoStore from '@store/mangoStore'
import { useEffect, useMemo } from 'react'
import { floorToDecimal, getDecimalCount } from 'utils/numbers'
import Decimal from 'decimal.js'
import { formatNumericValue, getDecimalCount } from 'utils/numbers'
import { ChartTradeType } from 'types'
import { useTranslation } from 'next-i18next'
import useSelectedMarket from 'hooks/useSelectedMarket'
@ -162,25 +161,21 @@ const RecentTrades = () => {
const side =
trade.side || (trade.takerSide === 0 ? 'bid' : 'ask')
// const price =
// typeof trade.price === 'number'
// ? trade.price
// : trade.price.toNumber()
const formattedPrice = market?.tickSize
? floorToDecimal(
trade.price,
getDecimalCount(market.tickSize)
)
: new Decimal(trade?.price || 0)
const formattedPrice =
market?.tickSize && trade.price
? formatNumericValue(
trade.price,
getDecimalCount(market.tickSize)
)
: trade?.price || 0
// const size = trade?.quantity?.toNumber() || trade?.size
const formattedSize =
market?.minOrderSize && trade.size
? floorToDecimal(
? formatNumericValue(
trade.size,
getDecimalCount(market.minOrderSize)
)
: new Decimal(trade.size || 0)
: trade?.size || 0
return (
<tr className="font-mono text-xs" key={i}>
@ -191,10 +186,10 @@ const RecentTrades = () => {
: 'text-th-down'
}`}
>
{formattedPrice.toFixed()}
{formattedPrice}
</td>
<td className="pb-1.5 text-right text-th-fgd-3">
{formattedSize.toFixed()}
{formattedSize}
</td>
<td className="pb-1.5 text-right text-th-fgd-4">
{trade.time

View File

@ -3,7 +3,7 @@ import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useCallback, useState } from 'react'
import { trimDecimals } from 'utils/numbers'
import { floorToDecimal } from 'utils/numbers'
import { useSpotMarketMax } from './SpotSlider'
const SpotButtonGroup = ({
@ -29,28 +29,24 @@ const SpotButtonGroup = ({
set((s) => {
if (s.tradeForm.side === 'buy') {
s.tradeForm.quoteSize = trimDecimals(size, tickDecimals).toFixed(
tickDecimals
)
s.tradeForm.quoteSize = floorToDecimal(size, tickDecimals).toString()
if (Number(s.tradeForm.price)) {
s.tradeForm.baseSize = trimDecimals(
s.tradeForm.baseSize = floorToDecimal(
size / Number(s.tradeForm.price),
minOrderDecimals
).toFixed(minOrderDecimals)
).toString()
} else {
s.tradeForm.baseSize = ''
}
} else if (s.tradeForm.side === 'sell') {
s.tradeForm.baseSize = trimDecimals(size, tickDecimals).toFixed(
minOrderDecimals
)
s.tradeForm.baseSize = floorToDecimal(size, tickDecimals).toString()
if (Number(s.tradeForm.price)) {
s.tradeForm.quoteSize = trimDecimals(
s.tradeForm.quoteSize = floorToDecimal(
size * Number(s.tradeForm.price),
tickDecimals
).toFixed(tickDecimals)
).toString()
}
}
})

View File

@ -5,7 +5,7 @@ import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useCallback, useMemo } from 'react'
import { GenericMarket } from 'types'
import { trimDecimals } from 'utils/numbers'
import { floorToDecimal } from 'utils/numbers'
export const useSpotMarketMax = (
mangoAccount: MangoAccount | undefined,
@ -78,20 +78,20 @@ const SpotSlider = ({
if (s.tradeForm.side === 'buy') {
s.tradeForm.quoteSize = val
if (Number(price)) {
s.tradeForm.baseSize = trimDecimals(
s.tradeForm.baseSize = floorToDecimal(
parseFloat(val) / price,
minOrderDecimals
).toFixed(minOrderDecimals)
).toString()
} else {
s.tradeForm.baseSize = ''
}
} else if (s.tradeForm.side === 'sell') {
s.tradeForm.baseSize = val
if (Number(price)) {
s.tradeForm.quoteSize = trimDecimals(
s.tradeForm.quoteSize = floorToDecimal(
parseFloat(val) * price,
tickDecimals
).toFixed(tickDecimals)
).toString()
}
}
})

View File

@ -6,6 +6,7 @@ import {
} from '@blockworks-foundation/mango-v4'
import { IconButton, LinkButton } from '@components/shared/Button'
import ConnectEmptyState from '@components/shared/ConnectEmptyState'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import SheenLoader from '@components/shared/SheenLoader'
import SideBadge from '@components/shared/SideBadge'
import {
@ -27,7 +28,6 @@ import { useViewport } from 'hooks/useViewport'
import { useTranslation } from 'next-i18next'
import { useCallback, useMemo, useState } from 'react'
import { PAGINATION_PAGE_LENGTH } from 'utils/constants'
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
import { breakpoints } from 'utils/theme'
import TableMarketName from './TableMarketName'
@ -268,14 +268,18 @@ const TradeHistory = () => {
</Td>
<Td className="text-right font-mono">{trade.size}</Td>
<Td className="text-right font-mono">
{formatDecimal(trade.price)}
<FormatNumericValue value={trade.price} />
</Td>
<Td className="text-right font-mono">
{formatFixedDecimals(trade.price * trade.size, true)}
<FormatNumericValue
value={trade.price * trade.size}
decimals={2}
isUsd
/>
</Td>
<Td className="text-right">
<span className="font-mono">
{formatDecimal(trade.feeCost)}
<FormatNumericValue value={trade.feeCost} />
</span>
<p className="font-body text-xs text-th-fgd-4">
{trade.liquidity}
@ -292,7 +296,7 @@ const TradeHistory = () => {
)}
</Td>
<Td className="xl:!pl-0">
{trade.market.name.includes('PERP') ? (
{trade.market.name.includes('PERP') ? (
<div className="flex justify-end">
<Tooltip content="View Counterparty" delay={250}>
<a
@ -336,7 +340,7 @@ const TradeHistory = () => {
</span>
{' for '}
<span className="font-mono text-th-fgd-2">
{formatDecimal(trade.price)}
<FormatNumericValue value={trade.price} />
</span>
</p>
</div>
@ -354,11 +358,11 @@ const TradeHistory = () => {
)}
</span>
<p className="font-mono text-th-fgd-2">
{formatFixedDecimals(
trade.price * trade.size,
true,
true
)}
<FormatNumericValue
value={trade.price * trade.size}
decimals={2}
isUsd
/>
</p>
</div>
{trade.market.name.includes('PERP') ? (

View File

@ -5,6 +5,7 @@ import {
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'
@ -12,7 +13,6 @@ import useMangoGroup from 'hooks/useMangoGroup'
import useSelectedMarket from 'hooks/useSelectedMarket'
import { useTranslation } from 'next-i18next'
import { useMemo } from 'react'
import { formatFixedDecimals } from 'utils/numbers'
import Slippage from './Slippage'
const TradeSummary = ({
@ -76,13 +76,17 @@ const TradeSummary = ({
<div className="flex justify-between text-xs">
<p>{t('trade:order-value')}</p>
<p className="text-th-fgd-2">
{tradeForm.price && tradeForm.baseSize
? formatFixedDecimals(
parseFloat(tradeForm.price) * parseFloat(tradeForm.baseSize),
false,
true
)
: '0.00'}
{tradeForm.price && tradeForm.baseSize ? (
<FormatNumericValue
value={
parseFloat(tradeForm.price) * parseFloat(tradeForm.baseSize)
}
decimals={2}
isUsd
/>
) : (
'0.00'
)}
</p>
</div>
<HealthImpact maintProjectedHealth={maintProjectedHealth} small />
@ -91,15 +95,17 @@ const TradeSummary = ({
<p className="tooltip-underline">{t('free-collateral')}</p>
</Tooltip>
<p className="text-th-fgd-2">
{group && mangoAccount
? formatFixedDecimals(
toUiDecimalsForQuote(
mangoAccount.getCollateralValue(group).toNumber()
),
false,
true
)
: ''}
{group && mangoAccount ? (
<FormatNumericValue
value={toUiDecimalsForQuote(
mangoAccount.getCollateralValue(group)
)}
decimals={2}
isUsd
/>
) : (
''
)}
</p>
</div>
<Slippage />

View File

@ -15,8 +15,8 @@ import { PerpMarket, PerpPosition } from '@blockworks-foundation/mango-v4'
import TableMarketName from './TableMarketName'
import useMangoAccount from 'hooks/useMangoAccount'
import { useWallet } from '@solana/wallet-adapter-react'
import { formatDecimal } from 'utils/numbers'
import ConnectEmptyState from '@components/shared/ConnectEmptyState'
import FormatNumericValue from '@components/shared/FormatNumericValue'
const UnsettledTrades = ({
unsettledSpotBalances,
@ -198,10 +198,10 @@ const UnsettledTrades = ({
<TableMarketName market={market} />
</Td>
<Td className="text-right font-mono">
{formatDecimal(
position.getUnsettledPnlUi(group, market),
market.baseDecimals
)}{' '}
<FormatNumericValue
value={position.getUnsettledPnlUi(group, market)}
decimals={market.baseDecimals}
/>{' '}
<span className="font-body text-th-fgd-4">USDC</span>
</Td>
<Td>

View File

@ -1,5 +1,5 @@
import { PublicKey } from '@solana/web3.js'
import { formatDecimal, numberCompacter } from './numbers'
import { formatNumericValue, numberCompacter } from './numbers'
export function abbreviateAddress(address: PublicKey, size = 5) {
const base58 = address.toBase58()
@ -11,5 +11,5 @@ export const formatYAxis = (value: number) => {
? '0'
: Math.abs(value) > 1
? numberCompacter.format(value)
: formatDecimal(value, 2)
: formatNumericValue(value, 2)
}

View File

@ -1,28 +1,93 @@
import Decimal from 'decimal.js'
export const formatNumericValue = (
value: number | string | Decimal,
decimals?: number,
roundUp?: boolean
): string => {
const numberValue = Number(value)
let formattedValue
if (numberValue > -0.0000000001 && numberValue < 0.000000001) {
formattedValue = '0'
} else if (decimals) {
formattedValue = roundUp
? roundValue(numberValue, decimals, true)
: roundValue(numberValue, decimals)
} else if (Math.abs(numberValue) >= 1000) {
formattedValue = roundUp
? roundValue(numberValue, 0, true)
: roundValue(numberValue, 0)
} else if (Math.abs(numberValue) >= 0.1) {
formattedValue = roundUp
? roundValue(numberValue, 3, true)
: roundValue(numberValue, 3)
} else {
formattedValue = roundUp
? roundValue(numberValue, 9, true)
: roundValue(numberValue, 9)
}
return formattedValue
}
export const formatCurrencyValue = (
value: number | string | Decimal,
decimals?: number
): string => {
const numberValue = Number(value)
let formattedValue
if (numberValue > -0.0000000001 && numberValue < 0.000000001) {
formattedValue = '$0.00'
} else if (decimals) {
formattedValue = Intl.NumberFormat('en', {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
style: 'currency',
currency: 'USD',
}).format(numberValue)
} else if (Math.abs(numberValue) >= 1000) {
formattedValue = usdFormatter0.format(numberValue)
} else if (Math.abs(numberValue) >= 0.1) {
formattedValue = usdFormatter2.format(numberValue)
} else {
formattedValue = usdFormatter3Sig.format(numberValue)
}
if (formattedValue === '-$0.00') return '$0.00'
return formattedValue
}
const roundValue = (
value: number | string | Decimal,
decimals: number,
roundUp?: boolean
): string => {
const decimalValue = value instanceof Decimal ? value : new Decimal(value)
const roundMode = roundUp ? Decimal.ROUND_UP : Decimal.ROUND_FLOOR
const roundedValue = decimalValue
.toDecimalPlaces(decimals, roundMode)
.toNumber()
if (decimals === 2) return digits2.format(roundedValue)
if (decimals === 3) return digits3.format(roundedValue)
if (decimals === 4) return digits4.format(roundedValue)
if (decimals === 5) return digits5.format(roundedValue)
if (decimals === 6) return digits6.format(roundedValue)
if (decimals === 7) return digits7.format(roundedValue)
if (decimals === 8) return digits8.format(roundedValue)
if (decimals === 9) return digits9.format(roundedValue)
return roundedValue.toLocaleString(undefined, {
maximumFractionDigits: decimals,
})
}
const digits2 = new Intl.NumberFormat('en', { maximumFractionDigits: 2 })
const digits3 = new Intl.NumberFormat('en', { maximumFractionDigits: 3 })
const digits4 = new Intl.NumberFormat('en', { maximumFractionDigits: 4 })
const digits5 = new Intl.NumberFormat('en', { maximumFractionDigits: 5 })
const digits6 = new Intl.NumberFormat('en', { maximumFractionDigits: 6 })
const digits7 = new Intl.NumberFormat('en', { maximumFractionDigits: 7 })
const digits8 = new Intl.NumberFormat('en', { maximumFractionDigits: 8 })
const digits9 = new Intl.NumberFormat('en', { maximumFractionDigits: 9 })
export const formatDecimal = (
value: number,
decimals = 6,
opts = { fixed: false }
): string => {
if (opts?.fixed) return value.toFixed(decimals)
if (value > -0.0000000001 && value < 0.000000001) return '0.00'
if (decimals === 2) return digits2.format(value)
if (decimals === 5) return digits5.format(value)
if (decimals === 6) return digits6.format(value)
if (decimals === 8) return digits8.format(value)
if (decimals === 9) return digits9.format(value)
return value.toLocaleString(undefined, { maximumFractionDigits: decimals })
}
export const numberFormat = new Intl.NumberFormat('en', {
maximumSignificantDigits: 7,
})
@ -33,7 +98,7 @@ export const floorToDecimal = (
): Decimal => {
const decimal = value instanceof Decimal ? value : new Decimal(value)
return decimal.toDecimalPlaces(decimals, Decimal.ROUND_DOWN)
return decimal.toDecimalPlaces(decimals, Decimal.ROUND_FLOOR)
}
const usdFormatter0 = Intl.NumberFormat('en', {
@ -50,43 +115,13 @@ const usdFormatter2 = Intl.NumberFormat('en', {
currency: 'USD',
})
const usdFormatter3 = Intl.NumberFormat('en', {
const usdFormatter3Sig = Intl.NumberFormat('en', {
minimumSignificantDigits: 3,
maximumSignificantDigits: 3,
style: 'currency',
currency: 'USD',
})
export const formatFixedDecimals = (
value: number,
isUSD?: boolean,
is2DP?: boolean
): string => {
let formattedValue
if (value === 0) {
formattedValue = isUSD ? '$0.00' : '0'
} else if (is2DP) {
formattedValue = usdFormatter2.format(value)
} else if (Math.abs(value) >= 1000) {
formattedValue = isUSD
? usdFormatter0.format(value)
: Number(floorToDecimal(value, 0)).toLocaleString(undefined, {
maximumFractionDigits: 0,
})
} else if (Math.abs(value) >= 0.1) {
formattedValue = isUSD
? usdFormatter2.format(value)
: Number(floorToDecimal(value, 3)).toLocaleString(undefined, {
maximumFractionDigits: 3,
})
} else {
formattedValue = isUSD ? usdFormatter3.format(value) : digits9.format(value)
}
if (formattedValue === '-$0.00') return '$0.00'
return formattedValue
}
export const countLeadingZeros = (x: number) => {
if (x % 1 == 0) {
return 0
@ -95,13 +130,6 @@ export const countLeadingZeros = (x: number) => {
}
}
export const trimDecimals = (n: number, digits: number) => {
const step = Math.pow(10, digits || 0)
const temp = Math.trunc(step * n)
return temp / step
}
export const getDecimalCount = (value: number): number => {
if (
!isNaN(value) &&