Merge branch 'tif' into main
This commit is contained in:
commit
6ff5517045
|
@ -59,6 +59,7 @@ export interface Balances extends BalancesBase {
|
|||
value?: I80F48 | null | undefined
|
||||
depositRate?: I80F48 | null | undefined
|
||||
borrowRate?: I80F48 | null | undefined
|
||||
decimals?: number | null | undefined
|
||||
}
|
||||
|
||||
export interface OpenOrdersBalances extends BalancesBase {
|
||||
|
|
|
@ -1,31 +1,29 @@
|
|||
import { useCallback } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useBalances } from '../hooks/useBalances'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import Button, { LinkButton } from '../components/Button'
|
||||
import { notify } from '../utils/notifications'
|
||||
import { ArrowSmDownIcon, ExclamationIcon } from '@heroicons/react/outline'
|
||||
import { Market } from '@project-serum/serum'
|
||||
import {
|
||||
getMarketIndexBySymbol,
|
||||
getTokenBySymbol,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
import { useState } from 'react'
|
||||
import { getTokenBySymbol } from '@blockworks-foundation/mango-client'
|
||||
import Loading from './Loading'
|
||||
import { useViewport } from '../hooks/useViewport'
|
||||
import { breakpoints } from './TradePageGrid'
|
||||
import { floorToDecimal, formatUsdValue } from '../utils'
|
||||
import { Table, Td, Th, TrBody, TrHead } from './TableElements'
|
||||
import { floorToDecimal, formatUsdValue, getPrecisionDigits } from '../utils'
|
||||
import { ExpandableRow, Table, Td, Th, TrBody, TrHead } from './TableElements'
|
||||
import { useSortableData } from '../hooks/useSortableData'
|
||||
import DepositModal from './DepositModal'
|
||||
import WithdrawModal from './WithdrawModal'
|
||||
import { ExpandableRow } from './TableElements'
|
||||
import MobileTableHeader from './mobile/MobileTableHeader'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { TransactionSignature } from '@solana/web3.js'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const BalancesTable = ({
|
||||
showZeroBalances = false,
|
||||
showDepositWithdraw = false,
|
||||
clickToPopulateTradeForm = false,
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const [showDepositModal, setShowDepositModal] = useState(false)
|
||||
|
@ -59,34 +57,42 @@ const BalancesTable = ({
|
|||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const wallet = useMangoStore((s) => s.wallet.current)
|
||||
const canWithdraw = mangoAccount?.owner.equals(wallet.publicKey)
|
||||
const { asPath } = useRouter()
|
||||
|
||||
const handleSizeClick = (size, symbol) => {
|
||||
const step = selectedMarket.minOrderSize
|
||||
const marketIndex = getMarketIndexBySymbol(
|
||||
mangoGroupConfig,
|
||||
marketConfig.baseSymbol
|
||||
)
|
||||
const minOrderSize = selectedMarket.minOrderSize
|
||||
const sizePrecisionDigits = getPrecisionDigits(minOrderSize)
|
||||
const marketIndex = marketConfig.marketIndex
|
||||
|
||||
const priceOrDefault = price
|
||||
? price
|
||||
: mangoGroup.getPrice(marketIndex, mangoGroupCache).toNumber()
|
||||
if (symbol === 'USDC') {
|
||||
const baseSize = Math.floor(size / priceOrDefault / step) * step
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = baseSize
|
||||
state.tradeForm.quoteSize = baseSize * priceOrDefault
|
||||
state.tradeForm.side = 'buy'
|
||||
})
|
||||
} else {
|
||||
const roundedSize = Math.round(size / step) * step
|
||||
const quoteSize = roundedSize * priceOrDefault
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = 'sell'
|
||||
})
|
||||
}
|
||||
}
|
||||
: mangoGroup.getPriceUi(marketIndex, mangoGroupCache)
|
||||
|
||||
let roundedSize, side
|
||||
if (symbol === 'USDC') {
|
||||
roundedSize = parseFloat(
|
||||
(
|
||||
Math.abs(size) / priceOrDefault +
|
||||
(size < 0 ? minOrderSize / 2 : -minOrderSize / 2)
|
||||
) // round up so neg USDC gets cleared
|
||||
.toFixed(sizePrecisionDigits)
|
||||
)
|
||||
side = size > 0 ? 'buy' : 'sell'
|
||||
} else {
|
||||
roundedSize = parseFloat(
|
||||
(
|
||||
Math.abs(size) + (size < 0 ? minOrderSize / 2 : -minOrderSize / 2)
|
||||
).toFixed(sizePrecisionDigits)
|
||||
)
|
||||
side = size > 0 ? 'sell' : 'buy'
|
||||
}
|
||||
const quoteSize = parseFloat((roundedSize * priceOrDefault).toFixed(2))
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = side
|
||||
})
|
||||
}
|
||||
const handleOpenDepositModal = useCallback((symbol) => {
|
||||
setActionSymbol(symbol)
|
||||
setShowDepositModal(true)
|
||||
|
@ -158,6 +164,7 @@ const BalancesTable = ({
|
|||
</div>
|
||||
{unsettledBalances.map((bal) => {
|
||||
const tokenConfig = getTokenBySymbol(mangoGroupConfig, bal.symbol)
|
||||
|
||||
return (
|
||||
<div
|
||||
className="border-b border-th-bkg-4 flex items-center justify-between py-4 last:border-b-0 last:pb-0"
|
||||
|
@ -356,20 +363,46 @@ const BalancesTable = ({
|
|||
className={`mr-2.5`}
|
||||
/>
|
||||
|
||||
{balance.symbol}
|
||||
{balance.symbol === 'USDC' ||
|
||||
decodeURIComponent(asPath).includes(
|
||||
`${balance.symbol}/USDC`
|
||||
) ? (
|
||||
<span>{balance.symbol}</span>
|
||||
) : (
|
||||
<Link
|
||||
href={{
|
||||
pathname: '/',
|
||||
query: { name: `${balance.symbol}/USDC` },
|
||||
}}
|
||||
shallow={true}
|
||||
>
|
||||
<a className="text-th-fgd-1 underline hover:no-underline hover:text-th-fgd-1">
|
||||
{balance.symbol}
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>{balance.deposits.toFixed()}</Td>
|
||||
<Td>{balance.borrows.toFixed()}</Td>
|
||||
<Td>
|
||||
{balance.deposits.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})}
|
||||
</Td>
|
||||
<Td>
|
||||
{balance.borrows.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})}
|
||||
</Td>
|
||||
<Td>{balance.orders}</Td>
|
||||
<Td>{balance.unsettled}</Td>
|
||||
<Td>
|
||||
{marketConfig.kind === 'spot' &&
|
||||
marketConfig.name.includes(balance.symbol) &&
|
||||
selectedMarket ? (
|
||||
selectedMarket &&
|
||||
clickToPopulateTradeForm ? (
|
||||
<span
|
||||
className={
|
||||
balance.net.toNumber() > 0
|
||||
balance.net.toNumber() != 0
|
||||
? 'cursor-pointer underline hover:no-underline'
|
||||
: ''
|
||||
}
|
||||
|
@ -377,10 +410,14 @@ const BalancesTable = ({
|
|||
handleSizeClick(balance.net, balance.symbol)
|
||||
}
|
||||
>
|
||||
{balance.net.toFixed()}
|
||||
{balance.net.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})}
|
||||
</span>
|
||||
) : (
|
||||
balance.net.toFixed()
|
||||
balance.net.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})
|
||||
)}
|
||||
</Td>
|
||||
<Td>{formatUsdValue(balance.value.toNumber())}</Td>
|
||||
|
@ -464,7 +501,9 @@ const BalancesTable = ({
|
|||
{balance.symbol}
|
||||
</div>
|
||||
<div className="text-th-fgd-1 text-right">
|
||||
{balance.net.toFixed()}
|
||||
{balance.net.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -477,25 +516,33 @@ const BalancesTable = ({
|
|||
<div className="pb-0.5 text-th-fgd-3 text-xs">
|
||||
{t('deposits')}
|
||||
</div>
|
||||
{balance.deposits.toFixed()}
|
||||
{balance.deposits.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})}
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<div className="pb-0.5 text-th-fgd-3 text-xs">
|
||||
{t('borrows')}
|
||||
</div>
|
||||
{balance.borrows.toFixed()}
|
||||
{balance.borrows.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})}
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<div className="pb-0.5 text-th-fgd-3 text-xs">
|
||||
{t('in-orders')}
|
||||
</div>
|
||||
{balance.orders.toFixed()}
|
||||
{balance.orders.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})}
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<div className="pb-0.5 text-th-fgd-3 text-xs">
|
||||
{t('unsettled')}
|
||||
</div>
|
||||
{balance.unsettled.toFixed()}
|
||||
{balance.unsettled.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})}
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<div className="pb-0.5 text-th-fgd-3 text-xs">
|
||||
|
@ -544,7 +591,9 @@ const BalancesTable = ({
|
|||
tokenSymbol={actionSymbol}
|
||||
repayAmount={
|
||||
balance.borrows.toNumber() > 0
|
||||
? balance.borrows.toFixed()
|
||||
? balance.borrows.toLocaleString(undefined, {
|
||||
maximumFractionDigits: balance.decimals,
|
||||
})
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import { ElementTitle } from './styles'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { i80f48ToPercent, floorToDecimal } from '../utils/index'
|
||||
import { getPrecisionDigits, i80f48ToPercent } from '../utils'
|
||||
import Tooltip from './Tooltip'
|
||||
import {
|
||||
getMarketIndexBySymbol,
|
||||
nativeI80F48ToUi,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
import { nativeI80F48ToUi } from '@blockworks-foundation/mango-client'
|
||||
import { useViewport } from '../hooks/useViewport'
|
||||
import { breakpoints } from './TradePageGrid'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -27,30 +24,38 @@ export default function MarketBalances() {
|
|||
const isMobile = width ? width < breakpoints.sm : false
|
||||
|
||||
const handleSizeClick = (size, symbol) => {
|
||||
const step = selectedMarket.minOrderSize
|
||||
const marketIndex = getMarketIndexBySymbol(
|
||||
mangoGroupConfig,
|
||||
marketConfig.baseSymbol
|
||||
)
|
||||
const minOrderSize = selectedMarket.minOrderSize
|
||||
const sizePrecisionDigits = getPrecisionDigits(minOrderSize)
|
||||
const marketIndex = marketConfig.marketIndex
|
||||
|
||||
const priceOrDefault = price
|
||||
? price
|
||||
: mangoGroup.getPrice(marketIndex, mangoGroupCache).toNumber()
|
||||
: mangoGroup.getPriceUi(marketIndex, mangoGroupCache)
|
||||
|
||||
let roundedSize, side
|
||||
if (symbol === 'USDC') {
|
||||
const baseSize = Math.floor(size / priceOrDefault / step) * step
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = baseSize
|
||||
state.tradeForm.quoteSize = baseSize * priceOrDefault
|
||||
state.tradeForm.side = 'buy'
|
||||
})
|
||||
roundedSize = parseFloat(
|
||||
(
|
||||
Math.abs(size) / priceOrDefault +
|
||||
(size < 0 ? minOrderSize / 2 : -minOrderSize / 2)
|
||||
) // round up so neg USDC gets cleared
|
||||
.toFixed(sizePrecisionDigits)
|
||||
)
|
||||
side = size > 0 ? 'buy' : 'sell'
|
||||
} else {
|
||||
const roundedSize = Math.round(size / step) * step
|
||||
const quoteSize = roundedSize * priceOrDefault
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = 'sell'
|
||||
})
|
||||
roundedSize = parseFloat(
|
||||
(
|
||||
Math.abs(size) + (size < 0 ? minOrderSize / 2 : -minOrderSize / 2)
|
||||
).toFixed(sizePrecisionDigits)
|
||||
)
|
||||
side = size > 0 ? 'sell' : 'buy'
|
||||
}
|
||||
const quoteSize = parseFloat((roundedSize * priceOrDefault).toFixed(2))
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = side
|
||||
})
|
||||
}
|
||||
|
||||
if (!mangoGroup || !selectedMarket) return null
|
||||
|
@ -67,31 +72,22 @@ export default function MarketBalances() {
|
|||
.reverse()
|
||||
.map(({ decimals, symbol, mintKey }) => {
|
||||
const tokenIndex = mangoGroup.getTokenIndex(mintKey)
|
||||
const deposit = mangoAccount
|
||||
? mangoAccount.getUiDeposit(
|
||||
mangoGroupCache.rootBankCache[tokenIndex],
|
||||
mangoGroup,
|
||||
tokenIndex
|
||||
const balance = mangoAccount
|
||||
? nativeI80F48ToUi(
|
||||
mangoAccount.getNet(
|
||||
mangoGroupCache.rootBankCache[tokenIndex],
|
||||
tokenIndex
|
||||
),
|
||||
decimals
|
||||
)
|
||||
: null
|
||||
const borrow = mangoAccount
|
||||
? mangoAccount.getUiBorrow(
|
||||
mangoGroupCache.rootBankCache[tokenIndex],
|
||||
mangoGroup,
|
||||
tokenIndex
|
||||
)
|
||||
: null
|
||||
|
||||
: 0
|
||||
const availableBalance = mangoAccount
|
||||
? floorToDecimal(
|
||||
nativeI80F48ToUi(
|
||||
mangoAccount.getAvailableBalance(
|
||||
mangoGroup,
|
||||
mangoGroupCache,
|
||||
tokenIndex
|
||||
),
|
||||
decimals
|
||||
).toNumber(),
|
||||
? nativeI80F48ToUi(
|
||||
mangoAccount.getAvailableBalance(
|
||||
mangoGroup,
|
||||
mangoGroupCache,
|
||||
tokenIndex
|
||||
),
|
||||
decimals
|
||||
)
|
||||
: 0
|
||||
|
@ -115,19 +111,20 @@ export default function MarketBalances() {
|
|||
<div className="pb-0.5 text-th-fgd-3 text-xs">
|
||||
{t('balance')}
|
||||
</div>
|
||||
<div className={`text-th-fgd-1`}>
|
||||
<div
|
||||
className={`text-th-fgd-1 ${
|
||||
balance != 0
|
||||
? 'cursor-pointer underline hover:no-underline'
|
||||
: ''
|
||||
}`}
|
||||
onClick={() => handleSizeClick(balance, symbol)}
|
||||
>
|
||||
{isLoading ? (
|
||||
<DataLoader />
|
||||
) : mangoAccount ? (
|
||||
deposit.gt(borrow) ? (
|
||||
deposit.toFixed()
|
||||
) : borrow.toNumber() > 0 ? (
|
||||
`-${borrow.toFixed()}`
|
||||
) : (
|
||||
0
|
||||
)
|
||||
) : (
|
||||
0
|
||||
balance.toLocaleString(undefined, {
|
||||
maximumFractionDigits: decimals,
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -147,10 +144,10 @@ export default function MarketBalances() {
|
|||
>
|
||||
{isLoading ? (
|
||||
<DataLoader />
|
||||
) : mangoAccount ? (
|
||||
availableBalance
|
||||
) : (
|
||||
0
|
||||
availableBalance.toLocaleString(undefined, {
|
||||
maximumFractionDigits: decimals,
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -59,20 +59,20 @@ const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
|||
// hard coded for now; market orders are very dangerous and fault prone
|
||||
const maxSlippage: number | undefined = 0.025
|
||||
|
||||
const txid = await mangoClient.placePerpOrder(
|
||||
const txid = await mangoClient.placePerpOrder2(
|
||||
mangoGroup,
|
||||
mangoAccount,
|
||||
mangoGroup.mangoCache,
|
||||
market,
|
||||
wallet,
|
||||
side,
|
||||
referencePrice * (1 + (side === 'buy' ? 1 : -1) * maxSlippage),
|
||||
size,
|
||||
'ioc',
|
||||
0, // client order id
|
||||
side === 'buy' ? askInfo : bidInfo,
|
||||
true, // reduce only
|
||||
referrerPk ? referrerPk : undefined
|
||||
{
|
||||
orderType: 'ioc',
|
||||
bookSideInfo: side === 'buy' ? askInfo : bidInfo,
|
||||
reduceOnly: true,
|
||||
referrerMangoAccountPk: referrerPk ? referrerPk : undefined,
|
||||
}
|
||||
)
|
||||
await sleep(500)
|
||||
actions.reloadMangoAccount()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import useMangoStore, { SECONDS } from '../stores/useMangoStore'
|
||||
import usePrevious from '../hooks/usePrevious'
|
||||
import useInterval from '../hooks/useInterval'
|
||||
import ChartApi from '../utils/chartDataConnector'
|
||||
|
@ -7,10 +7,10 @@ import UiLock from './UiLock'
|
|||
import ManualRefresh from './ManualRefresh'
|
||||
import useOraclePrice from '../hooks/useOraclePrice'
|
||||
import DayHighLow from './DayHighLow'
|
||||
import { useEffect } from 'react'
|
||||
import {
|
||||
getDecimalCount,
|
||||
getPrecisionDigits,
|
||||
patchInternalMarketName,
|
||||
perpContractPrecision,
|
||||
usdFormatter,
|
||||
} from '../utils'
|
||||
import { PerpMarket } from '@blockworks-foundation/mango-client'
|
||||
|
@ -20,7 +20,6 @@ import { breakpoints } from './TradePageGrid'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import SwitchMarketDropdown from './SwitchMarketDropdown'
|
||||
import Tooltip from './Tooltip'
|
||||
import { SECONDS } from '../stores/useMangoStore'
|
||||
|
||||
export function calculateFundingRate(perpStats, perpMarket) {
|
||||
const oldestStat = perpStats[perpStats.length - 1]
|
||||
|
@ -190,7 +189,11 @@ const MarketDetails = () => {
|
|||
</div>
|
||||
<div className="text-th-fgd-1 md:text-xs">
|
||||
{oraclePrice && selectedMarket
|
||||
? oraclePrice.toFixed(getDecimalCount(selectedMarket.tickSize))
|
||||
? oraclePrice.toNumber().toLocaleString(undefined, {
|
||||
maximumFractionDigits: getPrecisionDigits(
|
||||
selectedMarket.tickSize
|
||||
),
|
||||
})
|
||||
: '--'}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -255,7 +258,9 @@ const MarketDetails = () => {
|
|||
{selectedMarket ? (
|
||||
`${parseOpenInterest(
|
||||
selectedMarket as PerpMarket
|
||||
)} ${baseSymbol}`
|
||||
).toLocaleString(undefined, {
|
||||
maximumFractionDigits: perpContractPrecision[baseSymbol],
|
||||
})} ${baseSymbol}`
|
||||
) : (
|
||||
<MarketDataLoader />
|
||||
)}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { ElementTitle } from './styles'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { formatUsdValue } from '../utils/index'
|
||||
import {
|
||||
formatUsdValue,
|
||||
getPrecisionDigits,
|
||||
perpContractPrecision,
|
||||
} from '../utils'
|
||||
import Button, { LinkButton } from './Button'
|
||||
import Tooltip from './Tooltip'
|
||||
import PerpSideBadge from './PerpSideBadge'
|
||||
|
@ -92,18 +96,17 @@ export default function MarketPosition() {
|
|||
perpAccount = mangoAccount.perpAccounts[marketIndex]
|
||||
}
|
||||
|
||||
const handleSizeClick = (size, side) => {
|
||||
const step = selectedMarket.minOrderSize
|
||||
|
||||
const handleSizeClick = (size) => {
|
||||
const sizePrecisionDigits = getPrecisionDigits(selectedMarket.minOrderSize)
|
||||
const priceOrDefault = price
|
||||
? price
|
||||
: mangoGroup.getPrice(marketIndex, mangoCache).toNumber()
|
||||
const roundedSize = Math.round(size / step) * step
|
||||
const quoteSize = roundedSize * priceOrDefault
|
||||
: mangoGroup.getPriceUi(marketIndex, mangoCache)
|
||||
const roundedSize = parseFloat(Math.abs(size).toFixed(sizePrecisionDigits))
|
||||
const quoteSize = parseFloat((roundedSize * priceOrDefault).toFixed(2))
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = side === 'buy' ? 'sell' : 'buy'
|
||||
state.tradeForm.side = size > 0 ? 'sell' : 'buy'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -177,14 +180,11 @@ export default function MarketPosition() {
|
|||
) : basePosition ? (
|
||||
<span
|
||||
className="cursor-pointer underline hover:no-underline"
|
||||
onClick={() =>
|
||||
handleSizeClick(
|
||||
Math.abs(basePosition),
|
||||
basePosition > 0 ? 'buy' : 'sell'
|
||||
)
|
||||
}
|
||||
onClick={() => handleSizeClick(basePosition)}
|
||||
>
|
||||
{`${Math.abs(basePosition)} ${baseSymbol}`}
|
||||
{`${Math.abs(basePosition).toLocaleString(undefined, {
|
||||
maximumFractionDigits: perpContractPrecision[baseSymbol],
|
||||
})} ${baseSymbol}`}
|
||||
</span>
|
||||
) : (
|
||||
`0 ${baseSymbol}`
|
||||
|
|
|
@ -99,7 +99,11 @@ const DesktopTable = ({
|
|||
</Td>
|
||||
{editOrderIndex !== index ? (
|
||||
<>
|
||||
<Td className="w-[14.286%]">{order.size}</Td>
|
||||
<Td className="w-[14.286%]">
|
||||
{order.size.toLocaleString(undefined, {
|
||||
maximumFractionDigits: 4,
|
||||
})}
|
||||
</Td>
|
||||
<Td className="w-[14.286%]">
|
||||
{usdFormatter(order.price, decimals)}
|
||||
</Td>
|
||||
|
|
|
@ -9,7 +9,11 @@ import Button from '../components/Button'
|
|||
import { useViewport } from '../hooks/useViewport'
|
||||
import { breakpoints } from './TradePageGrid'
|
||||
import { ExpandableRow, Table, Td, Th, TrBody, TrHead } from './TableElements'
|
||||
import { formatUsdValue } from '../utils'
|
||||
import {
|
||||
formatUsdValue,
|
||||
getPrecisionDigits,
|
||||
perpContractPrecision,
|
||||
} from '../utils'
|
||||
import Loading from './Loading'
|
||||
import MarketCloseModal from './MarketCloseModal'
|
||||
import PerpSideBadge from './PerpSideBadge'
|
||||
|
@ -40,15 +44,15 @@ const PositionsTable = () => {
|
|||
setShowMarketCloseModal(false)
|
||||
}, [])
|
||||
|
||||
const handleSizeClick = (size, side, indexPrice) => {
|
||||
const step = selectedMarket.minOrderSize
|
||||
const handleSizeClick = (size, indexPrice) => {
|
||||
const sizePrecisionDigits = getPrecisionDigits(selectedMarket.minOrderSize)
|
||||
const priceOrDefault = price ? price : indexPrice
|
||||
const roundedSize = Math.round(size / step) * step
|
||||
const quoteSize = roundedSize * priceOrDefault
|
||||
const roundedSize = parseFloat(Math.abs(size).toFixed(sizePrecisionDigits))
|
||||
const quoteSize = parseFloat((roundedSize * priceOrDefault).toFixed(2))
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = side === 'buy' ? 'sell' : 'buy'
|
||||
state.tradeForm.side = size > 0 ? 'sell' : 'buy'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -129,6 +133,12 @@ const PositionsTable = () => {
|
|||
breakEvenPrice,
|
||||
unrealizedPnl,
|
||||
}) => {
|
||||
const basePositionUi = Math.abs(
|
||||
basePosition
|
||||
).toLocaleString(undefined, {
|
||||
maximumFractionDigits:
|
||||
perpContractPrecision[marketConfig.baseSymbol],
|
||||
})
|
||||
return (
|
||||
<TrBody key={`${marketConfig.marketIndex}`}>
|
||||
<Td>
|
||||
|
@ -169,22 +179,14 @@ const PositionsTable = () => {
|
|||
<span
|
||||
className="cursor-pointer underline hover:no-underline"
|
||||
onClick={() =>
|
||||
handleSizeClick(
|
||||
Math.abs(basePosition),
|
||||
basePosition > 0 ? 'buy' : 'sell',
|
||||
indexPrice
|
||||
)
|
||||
handleSizeClick(basePosition, indexPrice)
|
||||
}
|
||||
>
|
||||
{`${Math.abs(basePosition)} ${
|
||||
marketConfig.baseSymbol
|
||||
}`}
|
||||
{`${basePositionUi} ${marketConfig.baseSymbol}`}
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
{`${Math.abs(basePosition)} ${
|
||||
marketConfig.baseSymbol
|
||||
}`}
|
||||
{`${basePositionUi} ${marketConfig.baseSymbol}`}
|
||||
</span>
|
||||
)}
|
||||
</Td>
|
||||
|
|
|
@ -55,7 +55,7 @@ const TabContent = ({ activeTab }) => {
|
|||
case 'Orders':
|
||||
return <OpenOrdersTable />
|
||||
case 'Balances':
|
||||
return <BalancesTable />
|
||||
return <BalancesTable clickToPopulateTradeForm />
|
||||
case 'Trade History':
|
||||
return <TradeHistoryTable numTrades={100} />
|
||||
case 'Positions':
|
||||
|
@ -63,7 +63,7 @@ const TabContent = ({ activeTab }) => {
|
|||
case 'Fee Discount':
|
||||
return <FeeDiscountsTable />
|
||||
default:
|
||||
return <BalancesTable />
|
||||
return <BalancesTable clickToPopulateTradeForm />
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,11 @@ import {
|
|||
InformationCircleIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import { notify } from '../../utils/notifications'
|
||||
import { calculateTradePrice, getDecimalCount } from '../../utils'
|
||||
import {
|
||||
calculateTradePrice,
|
||||
getDecimalCount,
|
||||
percentFormat,
|
||||
} from '../../utils'
|
||||
import { floorToDecimal } from '../../utils/index'
|
||||
import useMangoStore, { Orderbook } from '../../stores/useMangoStore'
|
||||
import Button, { LinkButton } from '../Button'
|
||||
|
@ -658,20 +662,21 @@ export default function AdvancedTradeForm({
|
|||
)
|
||||
actions.reloadOrders()
|
||||
} else {
|
||||
txid = await mangoClient.placePerpOrder(
|
||||
txid = await mangoClient.placePerpOrder2(
|
||||
mangoGroup,
|
||||
mangoAccount,
|
||||
mangoGroup.mangoCache,
|
||||
market,
|
||||
wallet,
|
||||
side,
|
||||
perpOrderPrice,
|
||||
baseSize,
|
||||
perpOrderType,
|
||||
Date.now(),
|
||||
side === 'buy' ? askInfo : bidInfo, // book side used for ConsumeEvents
|
||||
reduceOnly,
|
||||
referrerPk ? referrerPk : undefined
|
||||
{
|
||||
orderType: perpOrderType,
|
||||
clientOrderId: Date.now(),
|
||||
bookSideInfo: side === 'buy' ? askInfo : bidInfo, // book side used for ConsumeEvents
|
||||
reduceOnly,
|
||||
referrerMangoAccountPk: referrerPk ? referrerPk : undefined,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1108,12 +1113,12 @@ export default function AdvancedTradeForm({
|
|||
) : (
|
||||
<div className="flex flex-col md:flex-row text-xs text-th-fgd-4 px-6 mt-2.5 items-center justify-center">
|
||||
<div>
|
||||
{t('maker-fee')}: {(makerFee * 100).toFixed(2)}%{' '}
|
||||
{t('maker-fee')}: {percentFormat.format(makerFee)}{' '}
|
||||
</div>
|
||||
<span className="hidden md:block md:px-1">|</span>
|
||||
<div>
|
||||
{' '}
|
||||
{t('taker-fee')}: {(takerFee * 100).toFixed(3)}%
|
||||
{t('taker-fee')}: {percentFormat.format(takerFee)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -316,18 +316,18 @@ export default function SimpleTradeForm({ initLeverage }) {
|
|||
orderType
|
||||
)
|
||||
} else {
|
||||
txid = await mangoClient.placePerpOrder(
|
||||
txid = await mangoClient.placePerpOrder2(
|
||||
mangoGroup,
|
||||
mangoAccount,
|
||||
mangoGroup.mangoCache,
|
||||
market,
|
||||
wallet,
|
||||
side,
|
||||
orderPrice,
|
||||
baseSize,
|
||||
orderType,
|
||||
0,
|
||||
side === 'buy' ? askInfo : bidInfo
|
||||
{
|
||||
orderType,
|
||||
bookSideInfo: side === 'buy' ? askInfo : bidInfo,
|
||||
}
|
||||
)
|
||||
}
|
||||
notify({ title: t('successfully-placed'), txid })
|
||||
|
|
|
@ -107,6 +107,7 @@ export function useBalances(): Balances[] {
|
|||
value: value(nativeBaseLocked, tokenIndex),
|
||||
depositRate: i80f48ToPercent(mangoGroup.getDepositRate(tokenIndex)),
|
||||
borrowRate: i80f48ToPercent(mangoGroup.getBorrowRate(tokenIndex)),
|
||||
decimals: mangoGroup.tokens[tokenIndex].decimals,
|
||||
},
|
||||
{
|
||||
market: null,
|
||||
|
@ -134,6 +135,7 @@ export function useBalances(): Balances[] {
|
|||
value: value(nativeQuoteLocked, quoteCurrencyIndex),
|
||||
depositRate: i80f48ToPercent(mangoGroup.getDepositRate(tokenIndex)),
|
||||
borrowRate: i80f48ToPercent(mangoGroup.getBorrowRate(tokenIndex)),
|
||||
decimals: mangoGroup.tokens[quoteCurrencyIndex].decimals,
|
||||
},
|
||||
]
|
||||
balances.push(marketPair)
|
||||
|
@ -172,6 +174,7 @@ export function useBalances(): Balances[] {
|
|||
value,
|
||||
depositRate,
|
||||
borrowRate,
|
||||
decimals: mangoGroup.tokens[QUOTE_INDEX].decimals,
|
||||
},
|
||||
].concat(baseBalances)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect } from 'react'
|
||||
import { AccountInfo, PublicKey } from '@solana/web3.js'
|
||||
import useMangoStore, { programId } from '../stores/useMangoStore'
|
||||
import useMangoStore, { programId, SECONDS } from '../stores/useMangoStore'
|
||||
import useInterval from './useInterval'
|
||||
import { Orderbook as SpotOrderBook, Market } from '@project-serum/serum'
|
||||
import { Market, Orderbook as SpotOrderBook } from '@project-serum/serum'
|
||||
import {
|
||||
BookSide,
|
||||
BookSideLayout,
|
||||
|
@ -19,7 +19,6 @@ import {
|
|||
marketSelector,
|
||||
marketsSelector,
|
||||
} from '../stores/selectors'
|
||||
import { SECONDS } from '../stores/useMangoStore'
|
||||
|
||||
function decodeBook(market, accInfo: AccountInfo<Buffer>): number[][] {
|
||||
if (market && accInfo?.data) {
|
||||
|
@ -33,7 +32,7 @@ function decodeBook(market, accInfo: AccountInfo<Buffer>): number[][] {
|
|||
market,
|
||||
BookSideLayout.decode(accInfo.data)
|
||||
)
|
||||
return book.getL2(depth).map(([price, size]) => [price, size])
|
||||
return book.getL2Ui(depth)
|
||||
}
|
||||
} else {
|
||||
return []
|
||||
|
|
|
@ -250,7 +250,7 @@
|
|||
"open-interest": "Open Interest",
|
||||
"open-orders": "Open Orders",
|
||||
"optional": "(Optional)",
|
||||
"oracle-price": "Oracle price",
|
||||
"oracle-price": "Oracle Price",
|
||||
"order-error": "Error placing order",
|
||||
"orderbook": "Orderbook",
|
||||
"orderbook-animation": "Orderbook Animation",
|
||||
|
|
|
@ -56,7 +56,7 @@ module.exports = {
|
|||
'fgd-2': '#C8C8C8',
|
||||
'fgd-3': '#B3B3B3',
|
||||
'fgd-4': '#878787',
|
||||
'bkg-button': '#52514E',
|
||||
'bkg-button': '#4E5152',
|
||||
},
|
||||
'mango-theme': {
|
||||
yellow: {
|
||||
|
|
|
@ -12,8 +12,8 @@ export async function sleep(ms) {
|
|||
|
||||
export const percentFormat = new Intl.NumberFormat(undefined, {
|
||||
style: 'percent',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 1,
|
||||
maximumFractionDigits: 3,
|
||||
})
|
||||
|
||||
export function floorToDecimal(
|
||||
|
@ -41,6 +41,9 @@ export function roundToDecimal(
|
|||
return decimals ? Math.round(value * 10 ** decimals) / 10 ** decimals : value
|
||||
}
|
||||
|
||||
export function getPrecisionDigits(x: number): number {
|
||||
return -Math.round(Math.log10(x))
|
||||
}
|
||||
export function getDecimalCount(value): number {
|
||||
if (
|
||||
!isNaN(value) &&
|
||||
|
|
37
yarn.lock
37
yarn.lock
|
@ -1659,13 +1659,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.1.tgz#0937807e807e8332aa708cfef4bcb6cbb88b4129"
|
||||
integrity sha512-2TuBmGUn9qeYz6sJINJlElrBuPsaUAtYyUsJ3XplEBf1pczrANAgs5ceJUFzdiqGEWLn+84ObSdBeChT/AXYFA==
|
||||
dependencies:
|
||||
"@project-serum/borsh" "^0.2.2"
|
||||
"@project-serum/borsh" "^0.2.4"
|
||||
"@solana/web3.js" "^1.17.0"
|
||||
base64-js "^1.5.1"
|
||||
bn.js "^5.1.2"
|
||||
bs58 "^4.0.1"
|
||||
buffer-layout "^1.2.2"
|
||||
camelcase "^5.3.1"
|
||||
cross-fetch "^3.1.5"
|
||||
crypto-hash "^1.3.0"
|
||||
eventemitter3 "^4.0.7"
|
||||
find "^0.3.0"
|
||||
|
@ -1825,7 +1826,27 @@
|
|||
buffer-layout "^1.2.0"
|
||||
dotenv "10.0.0"
|
||||
|
||||
"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.31.0":
|
||||
"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0":
|
||||
version "1.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.35.0.tgz#a2d09add241f48a370470a5c4db8596cb13f0dc6"
|
||||
integrity sha512-eKf2rPoWEyVq7QsgAQKNqxODvPsb0vqSwwg2xRY1e49Fn5Qh29m2FiLcYHRS/xhPu/7b/5gsD+RzO3BWozOeZQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@ethersproject/sha2" "^5.5.0"
|
||||
"@solana/buffer-layout" "^3.0.0"
|
||||
bn.js "^5.0.0"
|
||||
borsh "^0.4.0"
|
||||
bs58 "^4.0.1"
|
||||
buffer "6.0.1"
|
||||
cross-fetch "^3.1.4"
|
||||
jayson "^3.4.4"
|
||||
js-sha3 "^0.8.0"
|
||||
rpc-websockets "^7.4.2"
|
||||
secp256k1 "^4.0.2"
|
||||
superstruct "^0.14.2"
|
||||
tweetnacl "^1.0.0"
|
||||
|
||||
"@solana/web3.js@^1.31.0":
|
||||
version "1.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.34.0.tgz#33becf2c7e87497d73406374185e54e0b7bc235d"
|
||||
integrity sha512-6QvqN2DqEELvuV+5yUQM8P9fRiSG+6SzQ58HjumJqODu14r7eu5HXVWEymvKAvMLGME+0TmAdJHjw9xD5NgUWA==
|
||||
|
@ -2206,14 +2227,14 @@
|
|||
integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==
|
||||
|
||||
"@types/node@*":
|
||||
version "17.0.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.17.tgz#a8ddf6e0c2341718d74ee3dc413a13a042c45a0c"
|
||||
integrity sha512-e8PUNQy1HgJGV3iU/Bp2+D/DXh3PYeyli8LgIwsQcs1Ar1LoaWHSIT6Rw+H2rNJmiq6SNWiDytfx8+gYj7wDHw==
|
||||
version "17.0.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074"
|
||||
integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==
|
||||
|
||||
"@types/node@^12.12.54":
|
||||
version "12.20.45"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.45.tgz#f4980d177999299d99cd4b290f7f39366509a44f"
|
||||
integrity sha512-1Jg2Qv5tuxBqgQV04+wO5u+wmSHbHgpORCJdeCLM+E+YdPElpdHhgywU+M1V1InL8rfOtpqtOjswk+uXTKwx7w==
|
||||
version "12.20.46"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.46.tgz#7e49dee4c54fd19584e6a9e0da5f3dc2e9136bc7"
|
||||
integrity sha512-cPjLXj8d6anFPzFvOPxS3fvly3Shm5nTfl6g8X5smexixbuGUf7hfr21J5tX9JW+UPStp/5P5R8qrKL5IyVJ+A==
|
||||
|
||||
"@types/node@^14.14.25":
|
||||
version "14.18.11"
|
||||
|
|
Loading…
Reference in New Issue