Merge branch 'main' into perp-market-details-modal
This commit is contained in:
commit
5c03151a2e
|
@ -1,3 +1,5 @@
|
|||
import Decimal from 'decimal.js'
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export const NEXT_PUBLIC_BIRDEYE_API_KEY =
|
||||
process.env.NEXT_PUBLIC_BIRDEYE_API_KEY ||
|
||||
|
@ -59,3 +61,80 @@ export function getNextBarTime(lastBar: any, resolution = '1D') {
|
|||
|
||||
return nextBarTime
|
||||
}
|
||||
|
||||
export const SUBSCRIPT_NUMBER_MAP: Record<number, string> = {
|
||||
4: '₄',
|
||||
5: '₅',
|
||||
6: '₆',
|
||||
7: '₇',
|
||||
8: '₈',
|
||||
9: '₉',
|
||||
10: '₁₀',
|
||||
11: '₁₁',
|
||||
12: '₁₂',
|
||||
13: '₁₃',
|
||||
14: '₁₄',
|
||||
15: '₁₅',
|
||||
}
|
||||
|
||||
export const calcPricePrecision = (num: number | string) => {
|
||||
if (!num) return 8
|
||||
|
||||
switch (true) {
|
||||
case Math.abs(+num) < 0.00000000001:
|
||||
return 16
|
||||
|
||||
case Math.abs(+num) < 0.000000001:
|
||||
return 14
|
||||
|
||||
case Math.abs(+num) < 0.0000001:
|
||||
return 12
|
||||
|
||||
case Math.abs(+num) < 0.00001:
|
||||
return 10
|
||||
|
||||
case Math.abs(+num) < 0.05:
|
||||
return 6
|
||||
|
||||
case Math.abs(+num) < 1:
|
||||
return 4
|
||||
|
||||
case Math.abs(+num) < 20:
|
||||
return 3
|
||||
|
||||
default:
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
export const formatPrice = (
|
||||
num: number,
|
||||
precision?: number,
|
||||
gr0 = true
|
||||
): string => {
|
||||
if (!num) {
|
||||
return num.toString()
|
||||
}
|
||||
|
||||
if (!precision) {
|
||||
precision = calcPricePrecision(+num)
|
||||
}
|
||||
|
||||
let formated: string = new Decimal(num).toFixed(precision)
|
||||
|
||||
if (formated.match(/^0\.[0]+$/g)) {
|
||||
formated = formated.replace(/\.[0]+$/g, '')
|
||||
}
|
||||
|
||||
if (gr0 && formated.match(/\.0{4,15}[1-9]+/g)) {
|
||||
const match = formated.match(/\.0{4,15}/g)
|
||||
if (!match) return ''
|
||||
const matchString: string = match[0].slice(1)
|
||||
formated = formated.replace(
|
||||
/\.0{4,15}/g,
|
||||
`.0${SUBSCRIPT_NUMBER_MAP[matchString.length]}`
|
||||
)
|
||||
}
|
||||
|
||||
return formated
|
||||
}
|
||||
|
|
|
@ -69,6 +69,9 @@ export function subscribeOnStream(
|
|||
}
|
||||
if (!isOpen(socket)) {
|
||||
console.warn('Socket Closed')
|
||||
socket.addEventListener('open', (_event) => {
|
||||
socket.send(JSON.stringify(msg))
|
||||
})
|
||||
return
|
||||
}
|
||||
console.warn('[subscribeBars birdeye]')
|
||||
|
|
|
@ -158,7 +158,7 @@ export const queryBirdeyeBars = async (
|
|||
for (const bar of data.data.items) {
|
||||
if (bar.unixTime >= from && bar.unixTime < to) {
|
||||
const timestamp = bar.unixTime * 1000
|
||||
if (bar.h > 22311) continue
|
||||
if (bar.h >= 223111) continue
|
||||
bars = [
|
||||
...bars,
|
||||
{
|
||||
|
@ -223,7 +223,7 @@ export default {
|
|||
session: '24x7',
|
||||
timezone: 'Etc/UTC',
|
||||
minmov: 1,
|
||||
pricescale: 100,
|
||||
pricescale: 10 ** 16,
|
||||
has_intraday: true,
|
||||
has_weekly_and_monthly: false,
|
||||
has_empty_bars: true,
|
||||
|
|
|
@ -636,7 +636,7 @@ const AccountPage = () => {
|
|||
{t('account:total-funding-earned')}
|
||||
</p>
|
||||
</Tooltip>
|
||||
{Math.abs(fundingTotalValue) > 1 && mangoAccountAddress ? (
|
||||
{mangoAccountAddress ? (
|
||||
<Tooltip
|
||||
className="hidden md:block"
|
||||
content="Funding Chart"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
ArrowUpLeftIcon,
|
||||
ChevronDownIcon,
|
||||
QuestionMarkCircleIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -8,7 +9,7 @@ import { useCallback, useEffect, useState } from 'react'
|
|||
import { useViewport } from '../../hooks/useViewport'
|
||||
import { formatNumericValue } from '../../utils/numbers'
|
||||
import { breakpoints } from '../../utils/theme'
|
||||
import { IconButton } from '../shared/Button'
|
||||
import Button, { IconButton } from '../shared/Button'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import useJupiterMints from 'hooks/useJupiterMints'
|
||||
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
|
||||
|
@ -18,6 +19,7 @@ import BorrowRepayModal from '@components/modals/BorrowRepayModal'
|
|||
import BankAmountWithValue from '@components/shared/BankAmountWithValue'
|
||||
import useBanksWithBalances from 'hooks/useBanksWithBalances'
|
||||
import { getAvailableToBorrow } from './YourBorrowsTable'
|
||||
import { Disclosure, Transition } from '@headlessui/react'
|
||||
|
||||
const AssetsBorrowsTable = () => {
|
||||
const { t } = useTranslation(['common', 'token'])
|
||||
|
@ -133,10 +135,10 @@ const AssetsBorrowsTable = () => {
|
|||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<div>
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{banks.map((b) => {
|
||||
const bank = b.bank
|
||||
let logoURI
|
||||
let logoURI: string | undefined
|
||||
if (mangoTokens?.length) {
|
||||
logoURI = mangoTokens.find(
|
||||
(t) => t.address === bank.mint.toString()
|
||||
|
@ -146,47 +148,75 @@ const AssetsBorrowsTable = () => {
|
|||
const available = group ? getAvailableToBorrow(b, group) : 0
|
||||
|
||||
return (
|
||||
<div
|
||||
key={bank.name}
|
||||
className="border-b border-th-bkg-3 px-6 py-4"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
{logoURI ? (
|
||||
<Image alt="" width="24" height="24" src={logoURI} />
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-th-fgd-1">{bank.name}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div>
|
||||
<p className="mb-0.5 text-right text-xs">
|
||||
{t('available')}
|
||||
</p>
|
||||
<BankAmountWithValue
|
||||
amount={available > 0 ? available : 0}
|
||||
bank={bank}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="mb-0.5 text-right text-xs">{t('rate')}</p>
|
||||
<p className="text-right text-th-down">
|
||||
{formatNumericValue(bank.getBorrowRateUi(), 2)}%
|
||||
</p>
|
||||
</div>
|
||||
<IconButton
|
||||
disabled={b.maxBorrow === 0}
|
||||
onClick={() => handleShowBorrowModal(bank.name)}
|
||||
size="medium"
|
||||
<Disclosure key={bank.name}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={`w-full border-t border-th-bkg-3 p-4 text-left first:border-t-0 focus:outline-none`}
|
||||
>
|
||||
<ArrowUpLeftIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
{logoURI ? (
|
||||
<Image
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
src={logoURI}
|
||||
/>
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-th-fgd-1">{bank.name}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div>
|
||||
<p className="mb-0.5 text-right text-xs">
|
||||
{t('rate')}
|
||||
</p>
|
||||
<p className="text-right text-th-down">
|
||||
{formatNumericValue(bank.getBorrowRateUi(), 2)}%
|
||||
</p>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
className={`${
|
||||
open ? 'rotate-180' : 'rotate-360'
|
||||
} h-6 w-6 flex-shrink-0 text-th-fgd-3`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Disclosure.Button>
|
||||
<Transition
|
||||
enter="transition ease-in duration-200"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
>
|
||||
<Disclosure.Panel>
|
||||
<div className="mx-4 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4 pb-4">
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('available')}
|
||||
</p>
|
||||
<BankAmountWithValue
|
||||
amount={available > 0 ? available : 0}
|
||||
bank={bank}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
className="col-span-2 flex items-center justify-center"
|
||||
onClick={() => handleShowBorrowModal(bank.name)}
|
||||
secondary
|
||||
>
|
||||
<ArrowUpLeftIcon className="mr-2 h-5 w-5" />
|
||||
{`${t('borrow')} ${bank.name.split(' ')[0]}`}
|
||||
</Button>
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
|
|
@ -91,7 +91,7 @@ const BorrowPage = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col border-b border-th-bkg-3 px-6 py-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div className="flex flex-col border-b border-th-bkg-3 px-4 py-4 md:px-6 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div className="flex flex-col md:flex-row">
|
||||
<div className="pb-4 md:pr-6 md:pb-0">
|
||||
<Tooltip
|
||||
|
|
|
@ -3,6 +3,7 @@ import useJupiterMints from 'hooks/useJupiterMints'
|
|||
import {
|
||||
ArrowDownRightIcon,
|
||||
ArrowUpLeftIcon,
|
||||
ChevronDownIcon,
|
||||
NoSymbolIcon,
|
||||
QuestionMarkCircleIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
|
@ -17,12 +18,13 @@ import useMangoGroup from 'hooks/useMangoGroup'
|
|||
import ConnectEmptyState from '../shared/ConnectEmptyState'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import Decimal from 'decimal.js'
|
||||
import { IconButton } from '@components/shared/Button'
|
||||
import Button, { IconButton } from '@components/shared/Button'
|
||||
import { useCallback, useState } from 'react'
|
||||
import BorrowRepayModal from '@components/modals/BorrowRepayModal'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import BankAmountWithValue from '@components/shared/BankAmountWithValue'
|
||||
import { BankWithBalance } from 'hooks/useBanksWithBalances'
|
||||
import { Disclosure, Transition } from '@headlessui/react'
|
||||
|
||||
export const getAvailableToBorrow = (
|
||||
bankWithBal: BankWithBalance,
|
||||
|
@ -171,80 +173,120 @@ const YourBorrowsTable = ({ banks }: { banks: BankWithBalance[] }) => {
|
|||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
banks.map((b) => {
|
||||
const bank: Bank = b.bank
|
||||
<div className="border-b border-th-bkg-3">
|
||||
{banks.map((b) => {
|
||||
const bank: Bank = b.bank
|
||||
|
||||
let logoURI
|
||||
if (mangoTokens.length) {
|
||||
logoURI = mangoTokens.find(
|
||||
(t) => t.address === bank.mint.toString()
|
||||
)?.logoURI
|
||||
}
|
||||
let logoURI: string | undefined
|
||||
if (mangoTokens.length) {
|
||||
logoURI = mangoTokens.find(
|
||||
(t) => t.address === bank.mint.toString()
|
||||
)?.logoURI
|
||||
}
|
||||
|
||||
const available = group
|
||||
? getAvailableToBorrow(b, group)
|
||||
: new Decimal(0)
|
||||
const available = group
|
||||
? getAvailableToBorrow(b, group)
|
||||
: new Decimal(0)
|
||||
|
||||
const borrowedAmount = mangoAccount
|
||||
? Math.abs(mangoAccount.getTokenBalanceUi(bank))
|
||||
: 0
|
||||
const borrowedAmount = mangoAccount
|
||||
? Math.abs(mangoAccount.getTokenBalanceUi(bank))
|
||||
: 0
|
||||
|
||||
return (
|
||||
<div
|
||||
key={bank.name}
|
||||
className="border-b border-th-bkg-3 px-6 py-4"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
{logoURI ? (
|
||||
<Image alt="" width="24" height="24" src={logoURI} />
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-th-fgd-1">{bank.name}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div>
|
||||
<p className="mb-0.5 text-right text-xs">
|
||||
{t('borrow:borrowed-amount')}
|
||||
</p>
|
||||
<BankAmountWithValue
|
||||
amount={borrowedAmount}
|
||||
bank={bank}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="mb-0.5 text-right text-xs">{t('rate')}</p>
|
||||
<p className="text-right text-th-down">
|
||||
{formatNumericValue(bank.getBorrowRateUi(), 2)}%
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
handleShowActionModals(bank.name, 'repay')
|
||||
}
|
||||
size="medium"
|
||||
return (
|
||||
<Disclosure key={bank.name}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button
|
||||
className={`w-full border-t border-th-bkg-3 p-4 text-left first:border-t-0 focus:outline-none`}
|
||||
>
|
||||
<ArrowDownRightIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
disabled={available.eq(0)}
|
||||
onClick={() =>
|
||||
handleShowActionModals(bank.name, 'borrow')
|
||||
}
|
||||
size="medium"
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-2.5 flex flex-shrink-0 items-center">
|
||||
{logoURI ? (
|
||||
<Image
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
src={logoURI}
|
||||
/>
|
||||
) : (
|
||||
<QuestionMarkCircleIcon className="h-7 w-7 text-th-fgd-3" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-th-fgd-1">{bank.name}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div>
|
||||
<p className="mb-0.5 text-right text-xs">
|
||||
{t('borrow:borrowed-amount')}
|
||||
</p>
|
||||
<BankAmountWithValue
|
||||
amount={borrowedAmount}
|
||||
bank={bank}
|
||||
/>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
className={`${
|
||||
open ? 'rotate-180' : 'rotate-360'
|
||||
} h-6 w-6 flex-shrink-0 text-th-fgd-3`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Disclosure.Button>
|
||||
<Transition
|
||||
enter="transition ease-in duration-200"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
>
|
||||
<ArrowUpLeftIcon className="h-5 w-5" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
<Disclosure.Panel>
|
||||
<div className="mx-4 grid grid-cols-2 gap-4 border-t border-th-bkg-3 pt-4 pb-4">
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('rate')}
|
||||
</p>
|
||||
<p className="font-mono text-th-down">
|
||||
{formatNumericValue(bank.getBorrowRateUi(), 2)}%
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{t('available')}
|
||||
</p>
|
||||
<BankAmountWithValue
|
||||
amount={available}
|
||||
bank={bank}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center"
|
||||
disabled={!mangoAccount}
|
||||
onClick={() =>
|
||||
handleShowActionModals(bank.name, 'repay')
|
||||
}
|
||||
secondary
|
||||
>
|
||||
<ArrowDownRightIcon className="mr-2 h-5 w-5" />
|
||||
{`${t('repay')} ${bank.name.split(' ')[0]}`}
|
||||
</Button>
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center"
|
||||
onClick={() =>
|
||||
handleShowActionModals(bank.name, 'borrow')
|
||||
}
|
||||
secondary
|
||||
>
|
||||
<ArrowUpLeftIcon className="mr-2 h-5 w-5" />
|
||||
{`${t('borrow')} ${bank.name.split(' ')[0]}`}
|
||||
</Button>
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
) : mangoAccountAddress || connected ? (
|
||||
<div className="border-b border-th-bkg-3">
|
||||
|
|
|
@ -28,6 +28,7 @@ const PerpMarketDetails = ({
|
|||
const [priceDaysToShow, setPriceDaysToShow] = useState('30')
|
||||
const [oiDaysToShow, setOiDaysToShow] = useState('30')
|
||||
const [hourlyFundingeDaysToShow, setHourlyFundingDaysToShow] = useState('30')
|
||||
const [instantFundingDaysToShow, setInstantFundingDaysToShow] = useState('30')
|
||||
const rate = usePerpFundingRate()
|
||||
|
||||
const [marketStats, lastStat] = useMemo(() => {
|
||||
|
@ -62,6 +63,16 @@ const PerpMarketDetails = ({
|
|||
}
|
||||
}, [marketStats, fundingRate])
|
||||
|
||||
const instantFundingRateStats = useMemo(() => {
|
||||
if (marketStats) {
|
||||
return marketStats.map((stat) => ({
|
||||
...stat,
|
||||
instantaneous_funding_rate: stat.instantaneous_funding_rate * 100,
|
||||
}))
|
||||
}
|
||||
return []
|
||||
}, [marketStats])
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="col-span-2 flex items-center border-b border-th-bkg-3 px-6 py-3">
|
||||
|
@ -119,7 +130,7 @@ const PerpMarketDetails = ({
|
|||
yKey={'open_interest'}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 border-b border-r border-th-bkg-3 py-4 px-6 md:col-span-1">
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1">
|
||||
<DetailedAreaChart
|
||||
data={perpHourlyStats ? perpHourlyStats : []}
|
||||
daysToShow={hourlyFundingeDaysToShow}
|
||||
|
@ -135,6 +146,22 @@ const PerpMarketDetails = ({
|
|||
yDecimals={5}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 border-b border-th-bkg-3 py-4 px-6 md:col-span-1 md:border-l md:pl-6">
|
||||
<DetailedAreaChart
|
||||
data={instantFundingRateStats}
|
||||
daysToShow={instantFundingDaysToShow}
|
||||
setDaysToShow={setInstantFundingDaysToShow}
|
||||
heightClass="h-64"
|
||||
loading={loadingPerpStats}
|
||||
loaderHeightClass="h-[350px]"
|
||||
suffix="%"
|
||||
tickFormat={(x) => formatNumericValue(x, 4)}
|
||||
title={t('trade:instantaneous-funding')}
|
||||
xKey="date_hour"
|
||||
yKey={'instantaneous_funding_rate'}
|
||||
yDecimals={5}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
@ -81,7 +81,6 @@ const SwapHistoryTable = () => {
|
|||
signature,
|
||||
swap_in_amount,
|
||||
swap_in_loan_origination_fee,
|
||||
swap_in_price_usd,
|
||||
swap_in_symbol,
|
||||
swap_out_amount,
|
||||
loan,
|
||||
|
@ -137,23 +136,15 @@ const SwapHistoryTable = () => {
|
|||
src={baseLogoURI || ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="whitespace-nowrap">
|
||||
<FormatNumericValue
|
||||
value={swap_in_amount}
|
||||
decimals={inDecimals}
|
||||
/>{' '}
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{inSymbol}
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
<span className="font-body text-th-fgd-4">
|
||||
{t('price')}:
|
||||
</span>{' '}
|
||||
<FormatNumericValue value={swap_in_price_usd} isUsd />
|
||||
</p>
|
||||
</div>
|
||||
<p className="whitespace-nowrap">
|
||||
<FormatNumericValue
|
||||
value={swap_in_amount}
|
||||
decimals={inDecimals}
|
||||
/>{' '}
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{inSymbol}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
|
@ -166,24 +157,15 @@ const SwapHistoryTable = () => {
|
|||
src={quoteLogoURI || ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="whitespace-nowrap">
|
||||
<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>{' '}
|
||||
<FormatNumericValue
|
||||
value={swap_out_price_usd}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<p className="whitespace-nowrap">
|
||||
<FormatNumericValue
|
||||
value={swap_out_amount}
|
||||
decimals={outDecimals}
|
||||
/>{' '}
|
||||
<span className="font-body text-th-fgd-3">
|
||||
{outSymbol}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
|
@ -253,7 +235,6 @@ const SwapHistoryTable = () => {
|
|||
signature,
|
||||
swap_in_amount,
|
||||
swap_in_loan_origination_fee,
|
||||
swap_in_price_usd,
|
||||
swap_in_symbol,
|
||||
swap_out_amount,
|
||||
loan,
|
||||
|
@ -345,28 +326,6 @@ const SwapHistoryTable = () => {
|
|||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{`${swap_in_symbol} ${t('price')}`}
|
||||
</p>
|
||||
<p className="font-mono text-th-fgd-1">
|
||||
<FormatNumericValue
|
||||
value={swap_in_price_usd}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<p className="text-xs text-th-fgd-3">
|
||||
{`${swap_out_symbol} ${t('price')}`}
|
||||
</p>
|
||||
<p className="font-mono text-th-fgd-1">
|
||||
<FormatNumericValue
|
||||
value={swap_out_price_usd}
|
||||
isUsd
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
{borrowAmount ? (
|
||||
<>
|
||||
<div className="col-span-1">
|
||||
|
|
|
@ -11,7 +11,11 @@ import useSelectedMarket from 'hooks/useSelectedMarket'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Token } from 'types/jupiter'
|
||||
import { getDecimalCount, numberCompacter } from 'utils/numbers'
|
||||
import {
|
||||
formatCurrencyValue,
|
||||
getDecimalCount,
|
||||
numberCompacter,
|
||||
} from 'utils/numbers'
|
||||
import MarketSelectDropdown from './MarketSelectDropdown'
|
||||
import PerpFundingRate from './PerpFundingRate'
|
||||
import { BorshAccountsCoder } from '@coral-xyz/anchor'
|
||||
|
@ -179,8 +183,9 @@ const AdvancedMarketHeader = ({
|
|||
</div>
|
||||
<div className="font-mono text-xs text-th-fgd-2">
|
||||
{price ? (
|
||||
`$${price.toFixed(
|
||||
getDecimalCount(serumOrPerpMarket?.tickSize || 0.01) + 1
|
||||
`${formatCurrencyValue(
|
||||
price,
|
||||
getDecimalCount(serumOrPerpMarket?.tickSize || 0.01)
|
||||
)}`
|
||||
) : (
|
||||
<span className="text-th-fgd-4">–</span>
|
||||
|
|
|
@ -115,7 +115,7 @@ const AdvancedTradeForm = () => {
|
|||
|
||||
s.tradeForm.baseSize = e.value
|
||||
if (price && e.value !== '' && !Number.isNaN(Number(e.value))) {
|
||||
s.tradeForm.quoteSize = (price * parseFloat(e.value)).toString()
|
||||
s.tradeForm.quoteSize = new Decimal(price).mul(e.value).toFixed()
|
||||
} else {
|
||||
s.tradeForm.quoteSize = ''
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ const AdvancedTradeForm = () => {
|
|||
|
||||
s.tradeForm.quoteSize = e.value
|
||||
if (price && e.value !== '' && !Number.isNaN(Number(e.value))) {
|
||||
s.tradeForm.baseSize = (parseFloat(e.value) / price).toString()
|
||||
s.tradeForm.baseSize = new Decimal(e.value).div(price).toFixed()
|
||||
} else {
|
||||
s.tradeForm.baseSize = ''
|
||||
}
|
||||
|
@ -415,7 +415,7 @@ const AdvancedTradeForm = () => {
|
|||
thousandSeparator=","
|
||||
allowNegative={false}
|
||||
isNumericString={true}
|
||||
decimalScale={6}
|
||||
decimalScale={tickDecimals}
|
||||
name="price"
|
||||
id="price"
|
||||
className="ml-2 w-full bg-transparent font-mono focus:outline-none"
|
||||
|
|
|
@ -7,7 +7,7 @@ import useSelectedMarket from 'hooks/useSelectedMarket'
|
|||
import useUnownedAccount from 'hooks/useUnownedAccount'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { formatNumericValue } from 'utils/numbers'
|
||||
import { floorToDecimal } from 'utils/numbers'
|
||||
import { useSpotMarketMax } from './SpotSlider'
|
||||
|
||||
const MaxSizeButton = ({
|
||||
|
@ -59,30 +59,33 @@ const MaxSizeButton = ({
|
|||
const set = mangoStore.getState().set
|
||||
set((state) => {
|
||||
if (side === 'buy') {
|
||||
state.tradeForm.quoteSize = formatNumericValue(max, tickDecimals)
|
||||
state.tradeForm.quoteSize = floorToDecimal(max, tickDecimals).toFixed()
|
||||
if (tradeType === 'Market' || !price) {
|
||||
state.tradeForm.baseSize = formatNumericValue(
|
||||
state.tradeForm.baseSize = floorToDecimal(
|
||||
max / oraclePrice,
|
||||
minOrderDecimals
|
||||
)
|
||||
).toFixed()
|
||||
} else {
|
||||
state.tradeForm.baseSize = formatNumericValue(
|
||||
state.tradeForm.baseSize = floorToDecimal(
|
||||
max / parseFloat(price),
|
||||
minOrderDecimals
|
||||
)
|
||||
).toFixed()
|
||||
}
|
||||
} else {
|
||||
state.tradeForm.baseSize = formatNumericValue(max, minOrderDecimals)
|
||||
state.tradeForm.baseSize = floorToDecimal(
|
||||
max,
|
||||
minOrderDecimals
|
||||
).toFixed()
|
||||
if (tradeType === 'Market' || !price) {
|
||||
state.tradeForm.quoteSize = formatNumericValue(
|
||||
state.tradeForm.quoteSize = floorToDecimal(
|
||||
max * oraclePrice,
|
||||
minOrderDecimals
|
||||
)
|
||||
).toFixed()
|
||||
} else {
|
||||
state.tradeForm.quoteSize = formatNumericValue(
|
||||
state.tradeForm.quoteSize = floorToDecimal(
|
||||
max * parseFloat(price),
|
||||
minOrderDecimals
|
||||
)
|
||||
).toFixed()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -87,7 +87,7 @@ const RecentTrades = () => {
|
|||
{
|
||||
cacheTime: 1000 * 60 * 15,
|
||||
staleTime: 0,
|
||||
enabled: !!selectedMarketAddress,
|
||||
enabled: !!selectedMarketAddress && market instanceof PerpMarket,
|
||||
refetchOnWindowFocus: true,
|
||||
refetchInterval: 1000 * 10,
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { PerpMarket, Serum3Market } from '@blockworks-foundation/mango-v4'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import MarketLogos from './MarketLogos'
|
||||
|
||||
const TableMarketName = ({ market }: { market: PerpMarket | Serum3Market }) => {
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const { asPath } = useRouter()
|
||||
|
||||
return selectedMarket?.name === market.name ? (
|
||||
return selectedMarket?.name === market.name && asPath.includes('/trade') ? (
|
||||
<div className="flex items-center">
|
||||
<MarketLogos market={market} size="large" />
|
||||
<span className="whitespace-nowrap">{market.name}</span>
|
||||
|
|
|
@ -37,6 +37,7 @@ import Datafeed from 'apis/datafeed'
|
|||
// import PerpDatafeed from 'apis/mngo/datafeed'
|
||||
import useStablePrice from 'hooks/useStablePrice'
|
||||
import { isMangoError } from 'types'
|
||||
import { formatPrice } from 'apis/birdeye/helpers'
|
||||
|
||||
export interface ChartContainerProps {
|
||||
container: ChartingLibraryWidgetOptions['container']
|
||||
|
@ -724,6 +725,18 @@ const TradingViewChart = () => {
|
|||
'header_symbol_search',
|
||||
'popup_hints',
|
||||
],
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
custom_formatters: {
|
||||
priceFormatterFactory: () => {
|
||||
return {
|
||||
format: (price) => {
|
||||
// return the appropriate format
|
||||
return formatPrice(price)
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
fullscreen: defaultProps.fullscreen,
|
||||
autosize: defaultProps.autosize,
|
||||
studies_overrides: defaultProps.studiesOverrides,
|
||||
|
|
|
@ -83,27 +83,26 @@ const UnsettledTrades = ({
|
|||
setSettleMktAddress(market.publicKey.toString())
|
||||
|
||||
try {
|
||||
const mangoAccounts = await client.getAllMangoAccounts(group)
|
||||
const perpPosition = mangoAccount.getPerpPosition(market.perpMarketIndex)
|
||||
const mangoAccountPnl = perpPosition?.getEquityUi(market)
|
||||
|
||||
if (mangoAccountPnl === undefined)
|
||||
throw new Error('Unable to get account P&L')
|
||||
|
||||
const sign = Math.sign(mangoAccountPnl)
|
||||
const filteredAccounts = mangoAccounts
|
||||
.map((m) => ({
|
||||
mangoAccount: m,
|
||||
pnl:
|
||||
m?.getPerpPosition(market.perpMarketIndex)?.getEquityUi(market) ||
|
||||
0,
|
||||
}))
|
||||
.sort((a, b) => sign * (a.pnl - b.pnl))
|
||||
console.log('mangoAccountPnl', mangoAccountPnl)
|
||||
|
||||
const settleCandidates = await market.getSettlePnlCandidates(
|
||||
client,
|
||||
group,
|
||||
mangoAccountPnl < 0 ? 'positive' : 'negative',
|
||||
2
|
||||
)
|
||||
console.log('settleCandidates', settleCandidates)
|
||||
|
||||
const profitableAccount =
|
||||
mangoAccountPnl >= 0 ? mangoAccount : filteredAccounts[0].mangoAccount
|
||||
mangoAccountPnl < 0 ? settleCandidates[0].account : mangoAccount
|
||||
const unprofitableAccount =
|
||||
mangoAccountPnl < 0 ? mangoAccount : filteredAccounts[0].mangoAccount
|
||||
mangoAccountPnl > 0 ? settleCandidates[0].account : mangoAccount
|
||||
|
||||
const txid = await client.perpSettlePnl(
|
||||
group,
|
||||
|
|
|
@ -34,7 +34,7 @@ const ConnectedMenu = () => {
|
|||
const onConnectFetchAccountData = async (wallet: Wallet) => {
|
||||
if (!wallet.adapter.publicKey) return
|
||||
await actions.fetchMangoAccounts(wallet.adapter.publicKey)
|
||||
actions.fetchTourSettings(wallet.adapter.publicKey?.toString() as string)
|
||||
// actions.fetchTourSettings(wallet.adapter.publicKey?.toString() as string)
|
||||
actions.fetchWalletTokens(wallet.adapter.publicKey)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"postinstall": "tar -xzC public -f vendor/charting_library.tgz;tar -xzC public -f vendor/datafeeds.tgz"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-v4": "^0.9.9",
|
||||
"@blockworks-foundation/mango-v4": "^0.9.13",
|
||||
"@headlessui/react": "1.6.6",
|
||||
"@heroicons/react": "2.0.10",
|
||||
"@project-serum/anchor": "0.25.0",
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"grouping": "Grouping",
|
||||
"hide-asks": "Hide Asks",
|
||||
"hide-bids": "Hide Bids",
|
||||
"hourly-funding": "Hourly Funding",
|
||||
"hourly-funding": "Average Hourly Funding",
|
||||
"in-orders": "In Orders",
|
||||
"init-leverage": "Init Leverage",
|
||||
"instantaneous-funding": "Instantaneous Funding",
|
||||
|
|
|
@ -712,10 +712,7 @@ const mangoStore = create<MangoStore>()(
|
|||
)
|
||||
}
|
||||
|
||||
if (
|
||||
mangoAccount.serum3Active().length &&
|
||||
Object.keys(openOrders).length
|
||||
) {
|
||||
if (mangoAccount.serum3Active().length) {
|
||||
serumOpenOrderAccounts = Array.from(
|
||||
mangoAccount.serum3OosMapByMarketIndex.values()
|
||||
)
|
||||
|
@ -914,7 +911,6 @@ const mangoStore = create<MangoStore>()(
|
|||
state.profile.loadDetails = false
|
||||
})
|
||||
} catch (e) {
|
||||
notify({ type: 'error', title: 'Failed to load profile details' })
|
||||
console.error(e)
|
||||
set((state) => {
|
||||
state.profile.loadDetails = false
|
||||
|
@ -936,7 +932,6 @@ const mangoStore = create<MangoStore>()(
|
|||
state.settings.loading = false
|
||||
})
|
||||
} catch (e) {
|
||||
notify({ type: 'error', title: 'Failed to load profile details' })
|
||||
console.error(e)
|
||||
set((state) => {
|
||||
state.settings.loading = false
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@blockworks-foundation/mango-v4@^0.9.9":
|
||||
version "0.9.10"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.9.10.tgz#af6de3f552904e8f028912c908c7d145e01331c9"
|
||||
integrity sha512-4eea9qpKN2Y7v8/HMabnTfueVkQtvnVxoi/mAGAumvnQJZ3KK74133KhTwdhWXgJUJfD72lLKhjj1uLKzzwg/g==
|
||||
"@blockworks-foundation/mango-v4@^0.9.13":
|
||||
version "0.9.13"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.9.13.tgz#4869bf461a31d7a32da676f8c41217a07ba77fe8"
|
||||
integrity sha512-2fkwB2Ogpox6LtFTxAG/J6PqE//LYbYYt2M1zNEZSiqXoNjEOg6qeA6xDo6CWK1HTE66su7PqDmluje7yEl4dA==
|
||||
dependencies:
|
||||
"@coral-xyz/anchor" "^0.26.0"
|
||||
"@project-serum/serum" "0.13.65"
|
||||
|
|
Loading…
Reference in New Issue