mango-ui-v3/hooks/useOpenOrders.tsx

148 lines
4.3 KiB
TypeScript

import { useEffect, useMemo } from 'react'
import {
PerpOrder,
BookSide,
BookSideLayout,
getMarketByPublicKey,
MarketConfig,
PerpMarket,
MangoAccount,
I80F48,
} from '@blockworks-foundation/mango-client'
import { Market, Orderbook } from '@project-serum/serum'
import { Order } from '@project-serum/serum/lib/market'
import { PerpTriggerOrder } from '../@types/types'
import {
accountInfosSelector,
mangoGroupConfigSelector,
mangoGroupSelector,
marketsSelector,
} from '../stores/selectors'
import useMangoStore from '../stores/useMangoStore'
import useMangoAccount from './useMangoAccount'
type OrderInfo = {
order: Order | PerpOrder | PerpTriggerOrder
market: { account: Market | PerpMarket; config: MarketConfig }
}
function parseSpotOrders(
market: Market,
config: MarketConfig,
mangoAccount: MangoAccount,
accountInfos
) {
const openOrders = mangoAccount.spotOpenOrdersAccounts[config.marketIndex]
if (!openOrders) return []
const bidData = accountInfos[market['_decoded'].bids.toBase58()]?.data
const askData = accountInfos[market['_decoded'].asks.toBase58()]?.data
const bidOrderBook =
market && bidData ? Orderbook.decode(market, bidData) : []
const askOrderBook =
market && askData ? Orderbook.decode(market, askData) : []
const openOrdersForMarket = [...bidOrderBook, ...askOrderBook].filter((o) =>
o.openOrdersAddress.equals(openOrders.address)
)
return openOrdersForMarket.map<OrderInfo>((order) => ({
order,
market: { account: market, config: config },
}))
}
function parsePerpOpenOrders(
market: PerpMarket,
config: MarketConfig,
mangoAccount: MangoAccount,
accountInfos
) {
const bidData = accountInfos[market.bids.toBase58()]?.data
const askData = accountInfos[market.asks.toBase58()]?.data
const bidOrderBook =
market && bidData
? new BookSide(market.bids, market, BookSideLayout.decode(bidData))
: []
const askOrderBook =
market && askData
? new BookSide(market.asks, market, BookSideLayout.decode(askData))
: []
const openOrdersForMarket = [...bidOrderBook, ...askOrderBook].filter((o) =>
o.owner.equals(mangoAccount.publicKey)
)
const advancedOrdersForMarket = mangoAccount.advancedOrders
.map((o, i) => {
const pto = o.perpTrigger
if (pto.isActive && pto.marketIndex == config.marketIndex) {
return {
...o,
orderId: i,
marketIndex: pto.marketIndex,
orderType: pto.orderType,
side: pto.side,
price: market.priceLotsToNumber(pto.price),
size: market.baseLotsToNumber(pto.quantity),
triggerCondition: pto.triggerCondition,
triggerPrice: pto.triggerPrice.mul(
I80F48.fromNumber(
Math.pow(10, market.baseDecimals - market.quoteDecimals)
)
),
}
} else {
return undefined
}
})
.filter((o) => !!o)
return openOrdersForMarket
.concat(advancedOrdersForMarket)
.map<OrderInfo>((order) => ({
order,
market: { account: market, config: config },
}))
}
export function useOpenOrders() {
const markets = useMangoStore(marketsSelector)
const { mangoAccount } = useMangoAccount()
const mangoGroup = useMangoStore(mangoGroupSelector)
const groupConfig = useMangoStore(mangoGroupConfigSelector)
const accountInfos = useMangoStore(accountInfosSelector)
const setMangoStore = useMangoStore((s) => s.set)
const openOrders = useMemo(() => {
if (!mangoGroup || !mangoAccount || !accountInfos) return []
const openOrders = Object.entries(markets).map(([address, market]) => {
const marketConfig = getMarketByPublicKey(groupConfig, address)
if (market instanceof Market) {
return parseSpotOrders(market, marketConfig, mangoAccount, accountInfos)
} else if (market instanceof PerpMarket) {
return parsePerpOpenOrders(
market,
marketConfig,
mangoAccount,
accountInfos
)
}
})
return openOrders.flat()
}, [markets, accountInfos, mangoAccount])
useEffect(() => {
if (mangoGroup && mangoAccount) {
setMangoStore((state) => {
state.selectedMangoAccount.openOrders = openOrders
state.selectedMangoAccount.totalOpenOrders = openOrders.length
})
}
})
}