add openbook history

This commit is contained in:
tjs 2023-01-06 19:26:06 -05:00
parent 50fc7593ec
commit 59b724e431
8 changed files with 127 additions and 45 deletions

View File

@ -126,6 +126,7 @@ const ReadOnlyMangoAccount = () => {
state.mangoAccount.current = readOnlyMangoAccount
state.mangoAccount.initialLoad = false
})
actions.fetchTradeHistory()
} catch (error) {
console.error('error', error)
}

View File

@ -60,6 +60,7 @@ const MangoAccountsListModal = ({
try {
const reloadedMangoAccount = await retryFn(() => acc.reload(client))
actions.fetchOpenOrders(reloadedMangoAccount)
actions.fetchTradeHistory()
set((s) => {
s.mangoAccount.current = reloadedMangoAccount

View File

@ -65,7 +65,9 @@ export const TableDateDisplay = ({
showSeconds?: boolean
}) => (
<>
<p className="text-th-fgd-2">{dayjs(date).format('DD MMM YYYY')}</p>
<p className="tracking-normal text-th-fgd-2">
{dayjs(date).format('DD MMM YYYY')}
</p>
<p className="text-xs text-th-fgd-4">
{dayjs(date).format(showSeconds ? 'h:mm:ssa' : 'h:mma')}
</p>

View File

@ -1,5 +1,4 @@
import { I80F48, PerpMarket } from '@blockworks-foundation/mango-v4'
import InlineNotification from '@components/shared/InlineNotification'
import SideBadge from '@components/shared/SideBadge'
import {
Table,
@ -10,6 +9,7 @@ import {
TrHead,
} from '@components/shared/TableElements'
import { NoSymbolIcon } from '@heroicons/react/20/solid'
import { PublicKey } from '@solana/web3.js'
import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import useSelectedMarket from 'hooks/useSelectedMarket'
@ -86,27 +86,32 @@ const formatTradeHistory = (
}
const TradeHistory = () => {
const group = mangoStore.getState().group
const { selectedMarket } = useSelectedMarket()
const { mangoAccount, mangoAccountAddress } = useMangoAccount()
const fills = mangoStore((s) => s.selectedMarket.fills)
const tradeHistory = mangoStore((s) => s.mangoAccount.tradeHistory)
const { width } = useViewport()
const showTableView = width ? width > breakpoints.md : false
const openOrderOwner = useMemo(() => {
if (!mangoAccount || !selectedMarket) return
try {
if (selectedMarket instanceof PerpMarket) {
return mangoAccount.publicKey
} else {
if (selectedMarket instanceof PerpMarket) {
return mangoAccount.publicKey
} else {
try {
return mangoAccount.getSerum3OoAccount(selectedMarket.marketIndex)
.address
} catch {
console.warn(
'Unable to find OO account for mkt index',
selectedMarket.marketIndex
)
}
} catch (e) {
console.error('Error loading open order account for trade history: ', e)
}
}, [mangoAccount, selectedMarket])
const tradeHistoryFromEventQueue = useMemo(() => {
const eventQueueFillsForAccount = useMemo(() => {
if (!mangoAccountAddress || !selectedMarket) return []
const mangoAccountFills = fills
@ -127,9 +132,30 @@ const TradeHistory = () => {
return formatTradeHistory(mangoAccountAddress, mangoAccountFills)
}, [selectedMarket, mangoAccountAddress, openOrderOwner, fills])
if (!selectedMarket) return null
const combinedTradeHistory = useMemo(() => {
let newFills = []
if (eventQueueFillsForAccount?.length) {
console.log('eventQueueFillsForAccount', eventQueueFillsForAccount)
return mangoAccount && tradeHistoryFromEventQueue.length ? (
newFills = eventQueueFillsForAccount.filter((fill) => {
return !tradeHistory.find((t) => {
if (t.order_id) {
return t.order_id === fill.orderId?.toString()
}
// else {
// return t.seq_num === fill.seqNum?.toString()
// }
})
})
}
return [...newFills, ...tradeHistory]
}, [eventQueueFillsForAccount, tradeHistory])
console.log('trade history', tradeHistory)
if (!selectedMarket || !group) return null
return mangoAccount && combinedTradeHistory.length ? (
showTableView ? (
<div>
<Table>
@ -141,17 +167,31 @@ const TradeHistory = () => {
<Th className="text-right">Price</Th>
<Th className="text-right">Value</Th>
<Th className="text-right">Fee</Th>
{selectedMarket instanceof PerpMarket ? (
<Th className="text-right">Time</Th>
) : null}
<Th className="text-right">Time</Th>
</TrHead>
</thead>
<tbody>
{tradeHistoryFromEventQueue.map((trade: any) => {
{combinedTradeHistory.map((trade: any) => {
let market
if ('market' in trade) {
market = group.getSerum3MarketByExternalMarket(
new PublicKey(trade.market)
)
} else {
market = selectedMarket
}
let makerTaker = trade.liquidity
if ('maker' in trade) {
makerTaker = trade.maker ? 'Maker' : 'Taker'
}
return (
<TrBody key={`${trade.marketIndex}`} className="my-1 p-2">
<TrBody
key={`${trade.signature || trade.marketIndex}${trade.size}`}
className="my-1 p-2"
>
<Td className="">
<TableMarketName market={selectedMarket} />
<TableMarketName market={market} />
</Td>
<Td className="text-right">
<SideBadge side={trade.side} />
@ -161,37 +201,36 @@ const TradeHistory = () => {
{formatDecimal(trade.price)}
</Td>
<Td className="text-right font-mono">
${trade.value.toFixed(2)}
{trade.price * trade.size}
</Td>
<Td className="text-right">
<span className="font-mono">{trade.feeCost}</span>
<p className="font-body text-xs text-th-fgd-4">{`${
trade.liquidity ? trade.liquidity : ''
}`}</p>
<span className="font-mono">
{trade.fee_cost || trade.feeCost}
</span>
<p className="font-body text-xs text-th-fgd-4">
{makerTaker}
</p>
</Td>
{selectedMarket instanceof PerpMarket ? (
<Td className="whitespace-nowrap text-right font-mono">
<Td className="whitespace-nowrap text-right">
{trade.block_datetime ? (
<TableDateDisplay
date={trade.timestamp.toNumber() * 1000}
date={trade.block_datetime}
showSeconds
/>
</Td>
) : null}
) : (
'Recent'
)}
</Td>
</TrBody>
)
})}
</tbody>
</Table>
<div className="px-6 py-4">
<InlineNotification
type="info"
desc="During the Mango V4 alpha, only your recent Openbook trades will be displayed here. Full trade history will be available shortly."
/>
</div>
</div>
) : (
<div>
{tradeHistoryFromEventQueue.map((trade: any) => {
{eventQueueFillsForAccount.map((trade: any) => {
return (
<div
className="flex items-center justify-between border-b border-th-bkg-3 p-4"
@ -220,14 +259,8 @@ const TradeHistory = () => {
)
) : (
<div className="flex flex-col items-center justify-center px-6 pb-8 pt-4">
<div className="mb-8 w-full">
<InlineNotification
type="info"
desc="During the Mango V4 alpha, only your recent Openbook trades will be displayed here. Full trade history will be available shortly."
/>
</div>
<NoSymbolIcon className="mb-2 h-6 w-6 text-th-fgd-4" />
<p>No trade history for {selectedMarket?.name}</p>
<p>No trade history</p>
</div>
)
}

View File

@ -38,7 +38,7 @@ const TradeInfoTabs = () => {
return (
<div className="hide-scroll h-full overflow-y-scroll pb-5">
<div className="hide-scroll sticky top-0 z-10 overflow-x-auto border-b border-th-bkg-3">
<div className="hide-scroll sticky top-0 z-20 overflow-x-auto border-b border-th-bkg-3">
<TabButtons
activeValue={selectedTab}
onChange={(tab: string) => setSelectedTab(tab)}

View File

@ -38,6 +38,7 @@ const ConnectedMenu = () => {
await actions.fetchMangoAccounts(wallet.adapter as unknown as AnchorWallet)
actions.fetchTourSettings(wallet.adapter.publicKey?.toString() as string)
actions.fetchWalletTokens(wallet.adapter as unknown as AnchorWallet)
actions.fetchTradeHistory()
}
const handleDisconnect = useCallback(() => {

View File

@ -17,6 +17,7 @@ import {
PerpOrder,
PerpPosition,
BookSide,
FillEvent,
} from '@blockworks-foundation/mango-v4'
import EmptyWallet from '../utils/wallet'
@ -35,7 +36,7 @@ import {
OUTPUT_TOKEN_DEFAULT,
RPC_PROVIDER_KEY,
} from '../utils/constants'
import { OrderbookL2, SpotBalances } from 'types'
import { OrderbookL2, SpotBalances, SpotTradeHistory } from 'types'
import spotBalancesUpdater from './spotBalancesUpdater'
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
import perpPositionsUpdater from './perpPositionsUpdater'
@ -69,7 +70,7 @@ const emptyWallet = new EmptyWallet(Keypair.generate())
const initMangoClient = (provider: AnchorProvider): MangoClient => {
return MangoClient.connect(provider, CLUSTER, MANGO_V4_ID[CLUSTER], {
// blockhashCommitment: 'confirmed',
prioritizationFee: 2000,
prioritizationFee: 10000,
idsSource: 'get-program-accounts',
postSendTxCallback: ({ txid }: { txid: string }) => {
notify({
@ -245,6 +246,7 @@ export type MangoStore = {
initialLoad: boolean
}
}
tradeHistory: SpotTradeHistory[]
}
mangoAccounts: MangoAccount[]
markets: Serum3Market[] | undefined
@ -262,7 +264,7 @@ export type MangoStore = {
selectedMarket: {
name: string
current: Serum3Market | PerpMarket | undefined
fills: any
fills: (FillEvent | any)[]
bidsAccount: BookSide | Orderbook | undefined
asksAccount: BookSide | Orderbook | undefined
orderbook: OrderbookL2
@ -325,6 +327,7 @@ export type MangoStore = {
) => Promise<void>
fetchTokenStats: () => void
fetchTourSettings: (walletPk: string) => void
fetchTradeHistory: () => Promise<void>
fetchWalletTokens: (wallet: Wallet) => Promise<void>
connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise<void>
loadMarketFills: () => Promise<void>
@ -377,6 +380,7 @@ const mangoStore = create<MangoStore>()(
performance: { data: [], loading: false },
swapHistory: { data: [], initialLoad: false },
},
tradeHistory: [],
},
mangoAccounts: [],
markets: undefined,
@ -997,6 +1001,24 @@ const mangoStore = create<MangoStore>()(
console.log('Error fetching fills:', err)
}
},
async fetchTradeHistory() {
const set = get().set
// const selectedMarket = get().selectedMarket.current
// const group = get().group
const mangoAccount = get().mangoAccount.current
try {
const res = await fetch(
`https://mango-transaction-log.herokuapp.com/v4/stats/openbook-trades?address=${mangoAccount?.publicKey.toString()}&address-type=mango-account`
)
const parsedRes = await res.json()
set((s) => {
s.mangoAccount.tradeHistory = parsedRes.reverse()
})
} catch (e) {
console.error('Unable to fetch trade history', e)
}
},
updateConnection(endpointUrl) {
const set = get().set
const client = mangoStore.getState().client

View File

@ -23,3 +23,25 @@ export type SpotBalances = Record<
string,
{ inOrders: number; unsettled: number }
>
export interface SpotTradeHistory {
signature: string
block_datetime: string
market: string
open_orders: string
mango_account: string
bid: boolean
maker: boolean
referrer_rebate: any
order_id: string
client_order_id: string
fee_tier: number
instruction_num: number
size: number
price: number
side: string
fee_cost: number
open_orders_owner: string
base_symbol: string
quote_symbol: string
}