diff --git a/components/MangoProvider.tsx b/components/MangoProvider.tsx index 54e6f879..a00941ed 100644 --- a/components/MangoProvider.tsx +++ b/components/MangoProvider.tsx @@ -126,6 +126,7 @@ const ReadOnlyMangoAccount = () => { state.mangoAccount.current = readOnlyMangoAccount state.mangoAccount.initialLoad = false }) + actions.fetchTradeHistory() } catch (error) { console.error('error', error) } diff --git a/components/modals/MangoAccountsListModal.tsx b/components/modals/MangoAccountsListModal.tsx index 5e11e501..859ab02c 100644 --- a/components/modals/MangoAccountsListModal.tsx +++ b/components/modals/MangoAccountsListModal.tsx @@ -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 diff --git a/components/shared/TableElements.tsx b/components/shared/TableElements.tsx index a8b7c01f..f4856774 100644 --- a/components/shared/TableElements.tsx +++ b/components/shared/TableElements.tsx @@ -65,7 +65,9 @@ export const TableDateDisplay = ({ showSeconds?: boolean }) => ( <> -

{dayjs(date).format('DD MMM YYYY')}

+

+ {dayjs(date).format('DD MMM YYYY')} +

{dayjs(date).format(showSeconds ? 'h:mm:ssa' : 'h:mma')}

diff --git a/components/trade/TradeHistory.tsx b/components/trade/TradeHistory.tsx index 2f9eee42..c184afff 100644 --- a/components/trade/TradeHistory.tsx +++ b/components/trade/TradeHistory.tsx @@ -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 ? (
@@ -141,17 +167,31 @@ const TradeHistory = () => { - {selectedMarket instanceof PerpMarket ? ( - - ) : null} + - {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 ( - + - {selectedMarket instanceof PerpMarket ? ( - - ) : null} + ) : ( + 'Recent' + )} + ) })}
Price Value FeeTimeTime
- + @@ -161,37 +201,36 @@ const TradeHistory = () => { {formatDecimal(trade.price)} - ${trade.value.toFixed(2)} + {trade.price * trade.size} - {trade.feeCost} -

{`${ - trade.liquidity ? trade.liquidity : '' - }`}

+ + {trade.fee_cost || trade.feeCost} + +

+ {makerTaker} +

+ + + {trade.block_datetime ? ( -
-
- -
) : (
- {tradeHistoryFromEventQueue.map((trade: any) => { + {eventQueueFillsForAccount.map((trade: any) => { return (
{ ) ) : (
-
- -
-

No trade history for {selectedMarket?.name}

+

No trade history

) } diff --git a/components/trade/TradeInfoTabs.tsx b/components/trade/TradeInfoTabs.tsx index 5be25559..d12cc8cd 100644 --- a/components/trade/TradeInfoTabs.tsx +++ b/components/trade/TradeInfoTabs.tsx @@ -38,7 +38,7 @@ const TradeInfoTabs = () => { return (
-
+
setSelectedTab(tab)} diff --git a/components/wallet/ConnectedMenu.tsx b/components/wallet/ConnectedMenu.tsx index c059f7ec..a0515a53 100644 --- a/components/wallet/ConnectedMenu.tsx +++ b/components/wallet/ConnectedMenu.tsx @@ -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(() => { diff --git a/store/mangoStore.ts b/store/mangoStore.ts index 5d528f9d..2a9c30b9 100644 --- a/store/mangoStore.ts +++ b/store/mangoStore.ts @@ -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 fetchTokenStats: () => void fetchTourSettings: (walletPk: string) => void + fetchTradeHistory: () => Promise fetchWalletTokens: (wallet: Wallet) => Promise connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise loadMarketFills: () => Promise @@ -377,6 +380,7 @@ const mangoStore = create()( performance: { data: [], loading: false }, swapHistory: { data: [], initialLoad: false }, }, + tradeHistory: [], }, mangoAccounts: [], markets: undefined, @@ -997,6 +1001,24 @@ const mangoStore = create()( 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 diff --git a/types/index.ts b/types/index.ts index 6be4675a..261d77d7 100644 --- a/types/index.ts +++ b/types/index.ts @@ -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 +}