improve typing to fix errors with recent trades and history
This commit is contained in:
parent
f80144640f
commit
09852aa007
|
@ -12,6 +12,7 @@
|
|||
"@next/next/no-img-element": 0,
|
||||
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
|
||||
"react-hooks/exhaustive-deps": "warn", // Checks effect dependencies
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
2,
|
||||
{
|
||||
|
|
|
@ -8,16 +8,12 @@ export const socketUrl = `wss://public-api.birdeye.so/socket?x-api-key=${NEXT_PU
|
|||
|
||||
// Make requests to CryptoCompare API
|
||||
export async function makeApiRequest(path: string) {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}${path}`, {
|
||||
headers: {
|
||||
'X-API-KEY': NEXT_PUBLIC_BIRDEYE_API_KEY,
|
||||
},
|
||||
})
|
||||
return response.json()
|
||||
} catch (error: any) {
|
||||
throw new Error(`CryptoCompare request error: ${error.status}`)
|
||||
}
|
||||
const response = await fetch(`${API_URL}${path}`, {
|
||||
headers: {
|
||||
'X-API-KEY': NEXT_PUBLIC_BIRDEYE_API_KEY,
|
||||
},
|
||||
})
|
||||
return response.json()
|
||||
}
|
||||
|
||||
const RESOLUTION_MAPPING: Record<string, string> = {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import React, { Component, ErrorInfo, ReactNode } from 'react'
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
interface State {
|
||||
error: any
|
||||
hasError: boolean
|
||||
}
|
||||
|
||||
class ErrorBoundary extends Component<Props, State> {
|
||||
public state: State = {
|
||||
error: null,
|
||||
hasError: false,
|
||||
}
|
||||
|
||||
public static getDerivedStateFromError(e: Error): State {
|
||||
// Update state so the next render will show the fallback UI.
|
||||
return { hasError: true, error: e }
|
||||
}
|
||||
|
||||
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
console.error('Uncaught error:', error, errorInfo)
|
||||
}
|
||||
|
||||
public render() {
|
||||
const error = this.state.error
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div>
|
||||
<p>Error. Please refresh</p>
|
||||
<div>{`${error}`}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary
|
|
@ -1,16 +1,15 @@
|
|||
import React, { FunctionComponent } from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { PerpOrderSide } from '@blockworks-foundation/mango-v4'
|
||||
|
||||
type SideBadgeProps = {
|
||||
side: string | PerpOrderSide
|
||||
side: string
|
||||
}
|
||||
|
||||
const SideBadge: FunctionComponent<SideBadgeProps> = ({ side }) => {
|
||||
const { t } = useTranslation('common')
|
||||
if (side !== 'buy' && side !== 'sell') {
|
||||
return <div>Unknown</div>
|
||||
}
|
||||
|
||||
const isBid =
|
||||
typeof side === 'string' ? ['buy', 'long'].includes(side) : 'bid' in side
|
||||
const isBid = side === 'buy'
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -21,7 +20,7 @@ const SideBadge: FunctionComponent<SideBadgeProps> = ({ side }) => {
|
|||
}
|
||||
uppercase md:-my-0.5 md:px-1.5 md:py-0.5 md:text-xs`}
|
||||
>
|
||||
{typeof side === 'string' ? t(side) : 'bid' in side ? 'Long' : 'Short'}
|
||||
{isBid ? 'Buy' : 'Sell'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -254,6 +254,7 @@ const OpenOrders = () => {
|
|||
let tickSize: number
|
||||
let minOrderSize: number
|
||||
let expiryTimestamp: number | undefined
|
||||
let side: string
|
||||
if (o instanceof PerpOrder) {
|
||||
market = group.getPerpMarketByMarketIndex(o.perpMarketIndex)
|
||||
tickSize = market.tickSize
|
||||
|
@ -262,6 +263,7 @@ const OpenOrders = () => {
|
|||
o.expiryTimestamp === U64_MAX_BN
|
||||
? 0
|
||||
: o.expiryTimestamp.toNumber()
|
||||
side = 'bid' in o.side ? 'buy' : 'sell'
|
||||
} else {
|
||||
market = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(marketPk)
|
||||
|
@ -271,6 +273,7 @@ const OpenOrders = () => {
|
|||
)
|
||||
tickSize = serumMarket.tickSize
|
||||
minOrderSize = serumMarket.minOrderSize
|
||||
side = o.side
|
||||
}
|
||||
return (
|
||||
<TrBody
|
||||
|
@ -281,7 +284,7 @@ const OpenOrders = () => {
|
|||
<TableMarketName market={market} />
|
||||
</Td>
|
||||
<Td className="w-[16.67%] text-right">
|
||||
<SideBadge side={o.side} />
|
||||
<SideBadge side={side} />
|
||||
</Td>
|
||||
{modifyOrderId !== o.orderId.toString() ? (
|
||||
<>
|
||||
|
@ -398,10 +401,12 @@ const OpenOrders = () => {
|
|||
let market: PerpMarket | Serum3Market
|
||||
let tickSize: number
|
||||
let minOrderSize: number
|
||||
let side: string
|
||||
if (o instanceof PerpOrder) {
|
||||
market = group.getPerpMarketByMarketIndex(o.perpMarketIndex)
|
||||
tickSize = market.tickSize
|
||||
minOrderSize = market.minOrderSize
|
||||
side = 'bid' in o.side ? 'buy' : 'sell'
|
||||
} else {
|
||||
market = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(marketPk)
|
||||
|
@ -411,6 +416,7 @@ const OpenOrders = () => {
|
|||
)
|
||||
tickSize = serumMarket.tickSize
|
||||
minOrderSize = serumMarket.minOrderSize
|
||||
side = o.side
|
||||
}
|
||||
return (
|
||||
<div
|
||||
|
@ -421,7 +427,7 @@ const OpenOrders = () => {
|
|||
<TableMarketName market={market} />
|
||||
{modifyOrderId !== o.orderId.toString() ? (
|
||||
<div className="mt-1 flex items-center space-x-1">
|
||||
<SideBadge side={o.side} />
|
||||
<SideBadge side={side} />
|
||||
<p className="text-th-fgd-4">
|
||||
<span className="font-mono text-th-fgd-3">
|
||||
<FormatNumericValue
|
||||
|
|
|
@ -2,7 +2,6 @@ import useInterval from '@components/shared/useInterval'
|
|||
import mangoStore from '@store/mangoStore'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { formatNumericValue, getDecimalCount } from 'utils/numbers'
|
||||
import { ChartTradeType } from 'types'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import useSelectedMarket from 'hooks/useSelectedMarket'
|
||||
import { Howl } from 'howler'
|
||||
|
@ -16,13 +15,16 @@ import TradeVolumeAlertModal, {
|
|||
DEFAULT_VOLUME_ALERT_SETTINGS,
|
||||
} from '@components/modals/TradeVolumeAlertModal'
|
||||
import dayjs from 'dayjs'
|
||||
import { PerpMarket } from '@blockworks-foundation/mango-v4'
|
||||
import { isPerpFillEvent } from './TradeHistory'
|
||||
import ErrorBoundary from '@components/ErrorBoundary'
|
||||
|
||||
const volumeAlertSound = new Howl({
|
||||
src: ['/sounds/trade-buy.mp3'],
|
||||
volume: 0.8,
|
||||
})
|
||||
|
||||
type Test = { buys: number; sells: number }
|
||||
|
||||
const RecentTrades = () => {
|
||||
const { t } = useTranslation(['common', 'trade'])
|
||||
const fills = mangoStore((s) => s.selectedMarket.fills)
|
||||
|
@ -49,56 +51,35 @@ const RecentTrades = () => {
|
|||
if (!fills.length) return
|
||||
const latesetFill = fills[0]
|
||||
if (!latestFillId) {
|
||||
const fillId =
|
||||
selectedMarket instanceof PerpMarket
|
||||
? latesetFill.takerClientOrderId
|
||||
: latesetFill.orderId
|
||||
const fillId = isPerpFillEvent(latesetFill)
|
||||
? latesetFill.takerClientOrderId
|
||||
: latesetFill.orderId
|
||||
setLatestFillId(fillId.toString())
|
||||
}
|
||||
}, [fills])
|
||||
|
||||
useInterval(() => {
|
||||
if (!soundSettings['recent-trades'] || !quoteBank || !fills.length) return
|
||||
const latesetFill = fills[0]
|
||||
const fillId =
|
||||
selectedMarket instanceof PerpMarket
|
||||
? latesetFill.takerClientOrderId
|
||||
: latesetFill.orderId
|
||||
if (!soundSettings['recent-trades'] || !quoteBank || !latesetFill) return
|
||||
const fillId = isPerpFillEvent(latesetFill)
|
||||
? latesetFill.takerClientOrderId
|
||||
: latesetFill.orderId
|
||||
setLatestFillId(fillId.toString())
|
||||
const fillsLimitIndex = fills.findIndex((f) => {
|
||||
const id =
|
||||
selectedMarket instanceof PerpMarket ? f.takerClientOrderId : f.orderId
|
||||
const id = isPerpFillEvent(f) ? f.takerClientOrderId : f.orderId
|
||||
return id.toString() === fillId.toString()
|
||||
})
|
||||
const newFillsVolumeValue = fills
|
||||
.slice(0, fillsLimitIndex)
|
||||
.reduce((a, c) => a + c.size * c.price, 0)
|
||||
.reduce((a, c) => {
|
||||
const size = isPerpFillEvent(c) ? c.quantity : c.size
|
||||
return a + size * c.price
|
||||
}, 0)
|
||||
if (newFillsVolumeValue * quoteBank.uiPrice > Number(alertSettings.value)) {
|
||||
volumeAlertSound.play()
|
||||
}
|
||||
}, alertSettings.seconds * 1000)
|
||||
|
||||
// const fetchRecentTrades = useCallback(async () => {
|
||||
// if (!market) return
|
||||
|
||||
// try {
|
||||
// const response = await fetch(
|
||||
// `https://event-history-api-candles.herokuapp.com/trades/address/${market.publicKey}`
|
||||
// )
|
||||
// const parsedResp = await response.json()
|
||||
// const newTrades = parsedResp.data
|
||||
// if (!newTrades) return null
|
||||
|
||||
// if (newTrades.length && trades.length === 0) {
|
||||
// setTrades(newTrades)
|
||||
// } else if (newTrades?.length && !isEqual(newTrades[0], trades[0])) {
|
||||
// setTrades(newTrades)
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.error('Unable to fetch recent trades', e)
|
||||
// }
|
||||
// }, [market, trades])
|
||||
|
||||
useEffect(() => {
|
||||
// if (CLUSTER === 'mainnet-beta') {
|
||||
// fetchRecentTrades()
|
||||
|
@ -113,19 +94,28 @@ const RecentTrades = () => {
|
|||
// }
|
||||
const actions = mangoStore.getState().actions
|
||||
actions.loadMarketFills()
|
||||
}, 5000)
|
||||
}, 6000)
|
||||
|
||||
const [buyRatio, sellRatio] = useMemo(() => {
|
||||
if (!fills.length) return [0, 0]
|
||||
|
||||
const vol = fills.reduce(
|
||||
(a: { buys: number; sells: number }, c: any) => {
|
||||
if (c.side === 'buy' || c.takerSide === 0) {
|
||||
a.buys = a.buys + c.size
|
||||
(acc: Test, fill) => {
|
||||
let side
|
||||
let size
|
||||
if (isPerpFillEvent(fill)) {
|
||||
side = fill.takerSide === 0 ? 'buy' : 'sell'
|
||||
size = fill.quantity
|
||||
} else {
|
||||
a.sells = a.sells + c.size
|
||||
side = fill.side
|
||||
size = fill.size
|
||||
}
|
||||
return a
|
||||
if (side === 'buy') {
|
||||
acc.buys = acc.buys + size
|
||||
} else {
|
||||
acc.sells = acc.sells + size
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ buys: 0, sells: 0 }
|
||||
)
|
||||
|
@ -134,7 +124,7 @@ const RecentTrades = () => {
|
|||
}, [fills])
|
||||
|
||||
return (
|
||||
<>
|
||||
<ErrorBoundary>
|
||||
<div className="thin-scroll h-full overflow-y-scroll">
|
||||
<div className="flex items-center justify-between border-b border-th-bkg-3 py-1 px-2">
|
||||
<Tooltip
|
||||
|
@ -179,9 +169,19 @@ const RecentTrades = () => {
|
|||
</thead>
|
||||
<tbody>
|
||||
{!!fills.length &&
|
||||
fills.map((trade: ChartTradeType, i: number) => {
|
||||
const side =
|
||||
trade.side || (trade.takerSide === 0 ? 'bid' : 'ask')
|
||||
fills.map((trade, i: number) => {
|
||||
let side
|
||||
let size
|
||||
let time
|
||||
if (isPerpFillEvent(trade)) {
|
||||
side = trade.takerSide === 0 ? 'bid' : 'ask'
|
||||
size = trade.quantity
|
||||
time = trade.timestamp.toString()
|
||||
} else {
|
||||
side = trade.side
|
||||
size = trade.size
|
||||
time = ''
|
||||
}
|
||||
|
||||
const formattedPrice =
|
||||
market?.tickSize && trade.price
|
||||
|
@ -192,12 +192,12 @@ const RecentTrades = () => {
|
|||
: trade?.price || 0
|
||||
|
||||
const formattedSize =
|
||||
market?.minOrderSize && trade.size
|
||||
market?.minOrderSize && size
|
||||
? formatNumericValue(
|
||||
trade.size,
|
||||
size,
|
||||
getDecimalCount(market.minOrderSize)
|
||||
)
|
||||
: trade?.size || 0
|
||||
: size || 0
|
||||
|
||||
return (
|
||||
<tr className="font-mono text-xs" key={i}>
|
||||
|
@ -212,12 +212,8 @@ const RecentTrades = () => {
|
|||
</td>
|
||||
<td className="pb-1.5 text-right">{formattedSize}</td>
|
||||
<td className="pb-1.5 text-right text-th-fgd-4">
|
||||
{trade.time
|
||||
? new Date(trade.time).toLocaleTimeString()
|
||||
: trade.timestamp
|
||||
? dayjs(trade.timestamp.toNumber() * 1000).format(
|
||||
'hh:mma'
|
||||
)
|
||||
{time
|
||||
? dayjs(Number(time) * 1000).format('hh:mma')
|
||||
: '-'}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -233,7 +229,7 @@ const RecentTrades = () => {
|
|||
onClose={() => setShowVolumeAlertModal(false)}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -33,15 +33,6 @@ import { abbreviateAddress } from 'utils/formatting'
|
|||
import { breakpoints } from 'utils/theme'
|
||||
import TableMarketName from './TableMarketName'
|
||||
|
||||
const byTimestamp = (a: any, b: any) => {
|
||||
return (
|
||||
new Date(b.loadTimestamp || b.timestamp * 1000).getTime() -
|
||||
new Date(a.loadTimestamp || a.timestamp * 1000).getTime()
|
||||
)
|
||||
}
|
||||
|
||||
const reverseSide = (side: string) => (side === 'long' ? 'short' : 'long')
|
||||
|
||||
type PerpFillEvent = ParsedFillEvent
|
||||
|
||||
const parsePerpEvent = (mangoAccountAddress: string, event: PerpFillEvent) => {
|
||||
|
@ -49,8 +40,8 @@ const parsePerpEvent = (mangoAccountAddress: string, event: PerpFillEvent) => {
|
|||
const orderId = maker ? event.makerOrderId : event.takerOrderId
|
||||
const value = event.quantity * event.price
|
||||
const feeRate = maker ? event.makerFee : event.takerFee
|
||||
const takerSide = event.takerSide === 0 ? 'long' : 'short'
|
||||
const side = maker ? reverseSide(takerSide) : takerSide
|
||||
const takerSide = event.takerSide === 0 ? 'buy' : 'sell'
|
||||
const side = maker ? (takerSide === 'buy' ? 'sell' : 'buy') : takerSide
|
||||
|
||||
return {
|
||||
...event,
|
||||
|
@ -86,15 +77,21 @@ const isApiSpotTradeHistory = (
|
|||
else return false
|
||||
}
|
||||
|
||||
const isSerumFillEvent = (
|
||||
t: PerpFillEvent | SerumEvent | SpotTradeHistory | PerpTradeHistory
|
||||
type CombinedTradeHistoryTypes =
|
||||
| SpotTradeHistory
|
||||
| PerpTradeHistory
|
||||
| PerpFillEvent
|
||||
| SerumEvent
|
||||
|
||||
export const isSerumFillEvent = (
|
||||
t: CombinedTradeHistoryTypes
|
||||
): t is SerumEvent => {
|
||||
if ('eventFlags' in t) return true
|
||||
else return false
|
||||
}
|
||||
|
||||
const isPerpFillEvent = (
|
||||
t: PerpFillEvent | SerumEvent | SpotTradeHistory | PerpTradeHistory
|
||||
export const isPerpFillEvent = (
|
||||
t: CombinedTradeHistoryTypes
|
||||
): t is PerpFillEvent => {
|
||||
if ('takerSide' in t) return true
|
||||
else return false
|
||||
|
@ -104,7 +101,7 @@ const parseApiTradeHistory = (
|
|||
mangoAccountAddress: string,
|
||||
trade: SpotTradeHistory | PerpTradeHistory
|
||||
) => {
|
||||
let side
|
||||
let side: 'buy' | 'sell'
|
||||
let size
|
||||
let feeCost
|
||||
let liquidity
|
||||
|
@ -115,16 +112,12 @@ const parseApiTradeHistory = (
|
|||
liquidity = trade.maker ? 'Maker' : 'Taker'
|
||||
} else {
|
||||
liquidity =
|
||||
trade.taker && trade.taker.toString() === mangoAccountAddress
|
||||
? 'Taker'
|
||||
: 'Maker'
|
||||
const sideObj: any = {}
|
||||
trade.taker && trade.taker === mangoAccountAddress ? 'Taker' : 'Maker'
|
||||
if (liquidity == 'Taker') {
|
||||
sideObj[trade.taker_side] = 1
|
||||
side = trade.taker_side == 'bid' ? 'buy' : 'sell'
|
||||
} else {
|
||||
sideObj[trade.taker_side == 'bid' ? 'ask' : 'bid'] = 1
|
||||
side = trade.taker_side == 'bid' ? 'sell' : 'buy'
|
||||
}
|
||||
side = sideObj
|
||||
size = trade.quantity
|
||||
const feeRate =
|
||||
trade.maker === mangoAccountAddress ? trade.maker_fee : trade.taker_fee
|
||||
|
@ -140,44 +133,40 @@ const parseApiTradeHistory = (
|
|||
}
|
||||
}
|
||||
|
||||
type CombinedTradeHistoryTypes =
|
||||
| PerpFillEvent
|
||||
| SerumEvent
|
||||
| SpotTradeHistory
|
||||
| PerpTradeHistory
|
||||
|
||||
const formatTradeHistory = (
|
||||
group: Group,
|
||||
selectedMarket: Serum3Market | PerpMarket | undefined,
|
||||
selectedMarket: Serum3Market | PerpMarket,
|
||||
mangoAccountAddress: string,
|
||||
tradeHistory: Array<CombinedTradeHistoryTypes>
|
||||
) => {
|
||||
return tradeHistory
|
||||
.flat()
|
||||
.map((event) => {
|
||||
let trade
|
||||
if (isSerumFillEvent(event)) {
|
||||
trade = parseSerumEvent(event)
|
||||
} else if (isPerpFillEvent(event)) {
|
||||
trade = parsePerpEvent(mangoAccountAddress, event)
|
||||
} else {
|
||||
trade = parseApiTradeHistory(mangoAccountAddress, event)
|
||||
}
|
||||
|
||||
let market
|
||||
return tradeHistory.flat().map((event) => {
|
||||
let trade
|
||||
let market = selectedMarket
|
||||
let time = 'Recent'
|
||||
if (isSerumFillEvent(event)) {
|
||||
trade = parseSerumEvent(event)
|
||||
} else if (isPerpFillEvent(event)) {
|
||||
trade = parsePerpEvent(mangoAccountAddress, event)
|
||||
market = selectedMarket
|
||||
time = trade.timestamp.toString()
|
||||
} else {
|
||||
trade = parseApiTradeHistory(mangoAccountAddress, event)
|
||||
time = trade.block_datetime
|
||||
if ('market' in trade) {
|
||||
market = group.getSerum3MarketByExternalMarket(
|
||||
new PublicKey(trade.market)
|
||||
)
|
||||
} else if ('perp_market' in trade) {
|
||||
market = group.getPerpMarketByMarketIndex(trade.market_index)
|
||||
} else {
|
||||
market = selectedMarket
|
||||
}
|
||||
}
|
||||
|
||||
return { ...trade, market }
|
||||
})
|
||||
.sort(byTimestamp)
|
||||
return {
|
||||
...trade,
|
||||
market,
|
||||
time,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const TradeHistory = () => {
|
||||
|
@ -187,7 +176,9 @@ const TradeHistory = () => {
|
|||
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
|
||||
const actions = mangoStore((s) => s.actions)
|
||||
const fills = mangoStore((s) => s.selectedMarket.fills)
|
||||
const tradeHistory = mangoStore((s) => s.mangoAccount.tradeHistory.data)
|
||||
const tradeHistoryFromApi = mangoStore(
|
||||
(s) => s.mangoAccount.tradeHistory.data
|
||||
)
|
||||
const loadingTradeHistory = mangoStore(
|
||||
(s) => s.mangoAccount.tradeHistory.loading
|
||||
)
|
||||
|
@ -213,14 +204,14 @@ const TradeHistory = () => {
|
|||
}
|
||||
}, [mangoAccount, selectedMarket])
|
||||
|
||||
const eventQueueFillsForAccount = useMemo(() => {
|
||||
const eventQueueFillsForOwner = useMemo(() => {
|
||||
if (!selectedMarket || !openOrderOwner) return []
|
||||
|
||||
return fills.filter((fill: any) => {
|
||||
if (fill.openOrders) {
|
||||
return fills.filter((fill) => {
|
||||
if (isSerumFillEvent(fill)) {
|
||||
// handles serum event queue for spot trades
|
||||
return openOrderOwner ? fill.openOrders.equals(openOrderOwner) : false
|
||||
} else if (fill.taker) {
|
||||
} else if (isPerpFillEvent(fill)) {
|
||||
// handles mango event queue for perp trades
|
||||
return (
|
||||
fill.taker.equals(openOrderOwner) || fill.maker.equals(openOrderOwner)
|
||||
|
@ -231,27 +222,27 @@ const TradeHistory = () => {
|
|||
|
||||
const combinedTradeHistory = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
if (!group) return []
|
||||
let newFills = []
|
||||
if (eventQueueFillsForAccount?.length) {
|
||||
newFills = eventQueueFillsForAccount.filter((fill) => {
|
||||
return !tradeHistory.find((t) => {
|
||||
if ('order_id' in t) {
|
||||
return t.order_id === fill.orderId?.toString()
|
||||
} else {
|
||||
return t.seq_num === fill.seqNum?.toNumber()
|
||||
if (!group || !selectedMarket) return []
|
||||
let newFills: (SerumEvent | PerpFillEvent)[] = []
|
||||
if (eventQueueFillsForOwner?.length) {
|
||||
newFills = eventQueueFillsForOwner.filter((fill) => {
|
||||
return !tradeHistoryFromApi.find((t) => {
|
||||
if ('order_id' in t && isSerumFillEvent(fill)) {
|
||||
return t.order_id === fill.orderId.toString()
|
||||
} else if ('seq_num' in t && isPerpFillEvent(fill)) {
|
||||
return t.seq_num === fill.seqNum.toNumber()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
return formatTradeHistory(group, selectedMarket, mangoAccountAddress, [
|
||||
...newFills,
|
||||
...tradeHistory,
|
||||
...tradeHistoryFromApi,
|
||||
])
|
||||
}, [
|
||||
eventQueueFillsForAccount,
|
||||
eventQueueFillsForOwner,
|
||||
mangoAccountAddress,
|
||||
tradeHistory,
|
||||
tradeHistoryFromApi,
|
||||
selectedMarket,
|
||||
])
|
||||
|
||||
|
@ -285,10 +276,10 @@ const TradeHistory = () => {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{combinedTradeHistory.map((trade: any, index: number) => {
|
||||
{combinedTradeHistory.map((trade, index: number) => {
|
||||
return (
|
||||
<TrBody
|
||||
key={`${trade.signature || trade.marketIndex}${index}`}
|
||||
key={`${trade.side}${trade.size}${trade.price}${trade.time}${index}`}
|
||||
className="my-1 p-2"
|
||||
>
|
||||
<Td className="">
|
||||
|
@ -317,17 +308,14 @@ const TradeHistory = () => {
|
|||
</p>
|
||||
</Td>
|
||||
<Td className="whitespace-nowrap text-right">
|
||||
{trade.block_datetime ? (
|
||||
<TableDateDisplay
|
||||
date={trade.block_datetime}
|
||||
showSeconds
|
||||
/>
|
||||
{trade.time ? (
|
||||
<TableDateDisplay date={trade.time} showSeconds />
|
||||
) : (
|
||||
'Recent'
|
||||
)}
|
||||
</Td>
|
||||
<Td className="xl:!pl-0">
|
||||
{trade.market.name.includes('PERP') ? (
|
||||
{'taker' in trade ? (
|
||||
<div className="flex justify-end">
|
||||
<Tooltip
|
||||
content={`View Counterparty ${abbreviateAddress(
|
||||
|
@ -363,11 +351,11 @@ const TradeHistory = () => {
|
|||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{combinedTradeHistory.map((trade: any, index: number) => {
|
||||
{combinedTradeHistory.map((trade, index: number) => {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
|
||||
key={`${trade.marketIndex}${index}`}
|
||||
key={`${trade.price}${trade.size}${trade.side}${trade.time}${index}`}
|
||||
>
|
||||
<div>
|
||||
<TableMarketName market={selectedMarket} />
|
||||
|
@ -387,11 +375,8 @@ const TradeHistory = () => {
|
|||
<div className="flex items-center space-x-2.5">
|
||||
<div className="flex flex-col items-end">
|
||||
<span className="mb-0.5 flex items-center space-x-1.5">
|
||||
{trade.block_datetime ? (
|
||||
<TableDateDisplay
|
||||
date={trade.block_datetime}
|
||||
showSeconds
|
||||
/>
|
||||
{trade.time ? (
|
||||
<TableDateDisplay date={trade.time} showSeconds />
|
||||
) : (
|
||||
'Recent'
|
||||
)}
|
||||
|
@ -404,7 +389,7 @@ const TradeHistory = () => {
|
|||
/>
|
||||
</p>
|
||||
</div>
|
||||
{trade.market.name.includes('PERP') ? (
|
||||
{'taker' in trade ? (
|
||||
<a
|
||||
className=""
|
||||
target="_blank"
|
||||
|
|
|
@ -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.5.22",
|
||||
"@blockworks-foundation/mango-v4": "^0.5.23",
|
||||
"@headlessui/react": "1.6.6",
|
||||
"@heroicons/react": "2.0.10",
|
||||
"@project-serum/anchor": "0.25.0",
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
PerpOrder,
|
||||
PerpPosition,
|
||||
BookSide,
|
||||
FillEvent,
|
||||
ParsedFillEvent,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
|
||||
import EmptyWallet from '../utils/wallet'
|
||||
|
@ -42,6 +42,7 @@ import {
|
|||
import {
|
||||
OrderbookL2,
|
||||
PerpTradeHistory,
|
||||
SerumEvent,
|
||||
SpotBalances,
|
||||
SpotTradeHistory,
|
||||
} from 'types'
|
||||
|
@ -300,7 +301,7 @@ export type MangoStore = {
|
|||
selectedMarket: {
|
||||
name: string
|
||||
current: Serum3Market | PerpMarket | undefined
|
||||
fills: (FillEvent | any)[]
|
||||
fills: (ParsedFillEvent | SerumEvent)[]
|
||||
bidsAccount: BookSide | Orderbook | undefined
|
||||
asksAccount: BookSide | Orderbook | undefined
|
||||
orderbook: OrderbookL2
|
||||
|
@ -1087,13 +1088,18 @@ const mangoStore = create<MangoStore>()(
|
|||
perpMarket = selectedMarket
|
||||
}
|
||||
|
||||
let loadedFills: any[] = []
|
||||
let loadedFills: (ParsedFillEvent | SerumEvent)[] = []
|
||||
if (serumMarket) {
|
||||
loadedFills = await serumMarket.loadFills(connection, 10000)
|
||||
loadedFills = loadedFills.filter((f) => !f?.eventFlags?.maker)
|
||||
const serumFills = (await serumMarket.loadFills(
|
||||
connection,
|
||||
10000
|
||||
)) as SerumEvent[]
|
||||
loadedFills = serumFills.filter((f) => !f?.eventFlags?.maker)
|
||||
} else if (perpMarket) {
|
||||
loadedFills = await perpMarket.loadFills(client)
|
||||
loadedFills = loadedFills.reverse()
|
||||
const perpFills = (await perpMarket.loadFills(
|
||||
client
|
||||
)) as unknown as ParsedFillEvent[]
|
||||
loadedFills = perpFills.reverse()
|
||||
}
|
||||
set((state) => {
|
||||
state.selectedMarket.fills = loadedFills
|
||||
|
|
|
@ -42,7 +42,7 @@ export interface SpotTradeHistory {
|
|||
instruction_num: number
|
||||
size: number
|
||||
price: number
|
||||
side: string
|
||||
side: 'buy' | 'sell'
|
||||
fee_cost: number
|
||||
open_orders_owner: string
|
||||
base_symbol: string
|
||||
|
@ -60,7 +60,7 @@ export interface PerpTradeHistory {
|
|||
taker_order_id: string
|
||||
taker_client_order_id: string
|
||||
taker_fee: number
|
||||
taker_side: string
|
||||
taker_side: 'bid' | 'ask'
|
||||
perp_market: string
|
||||
market_index: number
|
||||
price: number
|
||||
|
|
|
@ -29,10 +29,10 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@blockworks-foundation/mango-v4@^0.5.22":
|
||||
version "0.5.22"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.5.22.tgz#fb7f39ae1c57d117583e080a150695fb3fcdae8c"
|
||||
integrity sha512-r043k5NHKZ0uAlNqcMny5ZSjBmBhnrBf2lRqBuruwFZ0nJ0WXLXO9EZB5L3Ymb2fY81ldUcxBSznU5QljQqEPQ==
|
||||
"@blockworks-foundation/mango-v4@^0.5.23":
|
||||
version "0.5.23"
|
||||
resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-v4/-/mango-v4-0.5.23.tgz#f0216c75ce1e10d0187a73265134f42fb94f8c9a"
|
||||
integrity sha512-HUqYzNMoCd6M3Esm9dB2p4MYbFWgy3KSvnFrRFi0xFOQ4ncc4UA5sLxp1ah5I3lvNVgs8mEmnNh8JQ/cPtrZRg==
|
||||
dependencies:
|
||||
"@coral-xyz/anchor" "^0.26.0"
|
||||
"@project-serum/serum" "0.13.65"
|
||||
|
|
Loading…
Reference in New Issue