open orders are kinda adapted

This commit is contained in:
Maximilian Schneider 2021-06-18 23:07:57 -04:00
parent 3f3ffb1a37
commit 2c487348f6
7 changed files with 115 additions and 83 deletions

View File

@ -13,6 +13,8 @@ import { notify } from '../utils/notifications'
import { Table, Thead, Tbody, Tr, Th, Td } from 'react-super-responsive-table'
import SideBadge from './SideBadge'
import { useSortableData } from '../hooks/useSortableData'
import { Order, Market } from '@project-serum/serum/lib/market'
import { Order as PerpOrder, PerpMarket } from '@blockworks-foundation/mango-client'
const OpenOrdersTable = () => {
const { asPath } = useRouter()
@ -22,22 +24,27 @@ const OpenOrdersTable = () => {
// const { connection, programId } = useConnection()
const actions = useMangoStore((s) => s.actions)
const handleCancelOrder = async (order) => {
const handleCancelOrder = async (order: Order | PerpOrder, market: Market | PerpMarket) => {
const wallet = useMangoStore.getState().wallet.current
const selectedMangoGroup =
useMangoStore.getState().selectedMangoGroup.current
const selectedMarginAccount =
useMangoStore.getState().selectedMarginAccount.current
setCancelId(order?.orderId)
setCancelId(order.orderId)
try {
if (!selectedMangoGroup || !selectedMarginAccount) return
if (market instanceof Market) {
await mangoClient.cancelSpotOrder(
selectedMangoGroup,
selectedMarginAccount,
wallet,
order.market,
order
market,
order as Order
)
} else if (market instanceof PerpMarket) {
console.log('TBD');
}
actions.fetchMarginAccounts()
} catch (e) {
notify({
@ -149,7 +156,7 @@ const OpenOrdersTable = () => {
</Tr>
</Thead>
<Tbody>
{items.map((order, index) => (
{items.map( ({order, market}, index) => (
<Tr
key={`${order.orderId}${order.side}`}
className={`border-b border-th-bkg-3
@ -164,12 +171,11 @@ const OpenOrdersTable = () => {
alt=""
width="20"
height="20"
src={`/assets/icons/${order.marketName
.split('/')[0]
src={`/assets/icons/${market.config.baseSymbol
.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{order.marketName}</div>
<div>{market.config.name}</div>
</div>
</Td>
<Td
@ -199,10 +205,10 @@ const OpenOrdersTable = () => {
Modify
</Button> */}
<Button
onClick={() => handleCancelOrder(order)}
onClick={() => handleCancelOrder(order, market.account)}
className={`ml-3 text-xs pt-0 pb-0 h-8 pl-3 pr-3`}
>
{cancelId + '' === order?.orderId + '' ? (
{cancelId + '' === order.orderId + '' ? (
<Loading />
) : (
<span>Cancel</span>

View File

@ -19,7 +19,7 @@ const SECONDS = 1000
const useHydrateStore = () => {
const setMangoStore = useMangoStore((s) => s.set)
const actions = useMangoStore((s) => s.actions)
const spotMarkets = useMangoStore((s) => s.selectedMangoGroup.markets)
const markets = useMangoStore((s) => s.selectedMangoGroup.markets)
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
const selectedMarket = useMangoStore((s) => s.selectedMarket.current)
@ -32,24 +32,10 @@ const useHydrateStore = () => {
}, 60 * SECONDS)
useEffect(() => {
if (marketConfig.kind === 'spot') {
setMangoStore((state) => {
state.selectedMarket.current =
spotMarkets[marketConfig.publicKey.toString()]
markets[marketConfig.publicKey.toString()]
})
} else {
mangoClient
.getPerpMarket(
marketConfig.publicKey,
marketConfig.baseDecimals,
marketConfig.quoteDecimals
)
.then(async (market) => {
setMangoStore((state) => {
state.selectedMarket.current = market
})
})
}
}, [marketConfig])
// hydrate orderbook with all markets in mango group

View File

@ -1,52 +1,64 @@
import { Orderbook } from '@project-serum/serum'
import { Order as PerpOrder, BookSide, BookSideLayout, getMarketByPublicKey, MarketConfig, PerpMarket, MerpsAccount as MarginAccount, LeafNode, PerpMarketConfig } from '@blockworks-foundation/mango-client'
import { Market, OpenOrders, Orderbook } from '@project-serum/serum'
import { Order } from '@project-serum/serum/lib/market'
import { PublicKey } from '@solana/web3.js'
import useMangoStore from '../stores/useMangoStore'
const getOrderBookAccounts = (market, accountInfos) => {
const bidData = accountInfos[market._decoded.bids.toString()]?.data
const askData = accountInfos[market._decoded.asks.toString()]?.data
type OrderInfo = {
order: Order | PerpOrder,
market: { account: Market | PerpMarket, config: MarketConfig }}
return {
bidOrderBook:
market && bidData ? Orderbook.decode(market, Buffer.from(bidData)) : [],
askOrderBook:
market && askData ? Orderbook.decode(market, Buffer.from(askData)) : [],
}
function parseSpotOrders(market: Market, config: MarketConfig, marginAccount: MarginAccount, accountInfos) {
const openOrders = marginAccount.spotOpenOrdersAccounts[config.marketIndex];
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, marginAccount: MarginAccount, 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(marginAccount.publicKey)
)
return openOrdersForMarket.map<OrderInfo>((order) => ({
order,
market: { account: market, config: config },
}));
}
export function useOpenOrders() {
const markets = useMangoStore((s) => s.selectedMangoGroup.markets)
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoGroupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const accountInfos = useMangoStore((s) => s.accountInfos)
if (!mangoGroup || !marginAccount || !accountInfos) return null
const openOrders = Object.entries(markets).map(([address, market]) => {
const marketIndex = mangoGroup.getSpotMarketIndex(new PublicKey(address))
const openOrdersAccount = marginAccount.spotOpenOrdersAccounts[marketIndex]
const marketName = mangoGroupConfig.spotMarkets.find(
(mkt) => mkt.publicKey.toString() === address
).name
if (!openOrdersAccount) return []
const { bidOrderBook, askOrderBook } = getOrderBookAccounts(
market,
accountInfos
)
const openOrdersForMarket = [...bidOrderBook, ...askOrderBook].filter((o) =>
o.openOrdersAddress.equals(openOrdersAccount.address)
)
return openOrdersForMarket.map((order) => ({
...order,
marketName,
market,
}))
const openOrders = Object.entries(markets).map(([address, market]) => {
const marketConfig = getMarketByPublicKey(groupConfig, address)
if (market instanceof Market) {
return parseSpotOrders(market, marketConfig, marginAccount, accountInfos);
} else if (market instanceof PerpMarket) {
return parsePerpOpenOrders(market, marketConfig, marginAccount, accountInfos);
}
})
return openOrders.flat()

View File

@ -1,6 +1,6 @@
import { useMemo, useState } from 'react'
export const useSortableData = (items, config = null) => {
export function useSortableData<T>(items: T[], config = null): { items: T[], requestSort: any, sortConfig: any } {
const [sortConfig, setSortConfig] = useState(config)
const sortedItems = useMemo(() => {

View File

@ -18,7 +18,11 @@ import {
nativeToUi,
MerpsCache,
PerpMarket,
getAllMarkets,
getMultipleAccounts,
PerpMarketConfig,
SpotMarketConfig,
PerpMarketLayout,
} from '@blockworks-foundation/mango-client'
// import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
import {
@ -31,9 +35,11 @@ import {
import { EndpointInfo, WalletAdapter } from '../@types/types'
import { getWalletTokenInfo } from '../utils/tokens'
import {
chunks,
decodeAndLoadMarkets,
getOrderBookAccountInfos,
isDefined,
zipDict,
} from '../utils'
import { notify } from '../utils/notifications'
import useAllMarkets from '../hooks/useAllMarkets'
@ -139,10 +145,10 @@ interface MangoStore extends State {
name: string
current: MangoGroup | null
markets: {
[address: string]: Market
[address: string]: Market | PerpMarket
}
rootBanks: any[]
cache: MerpsCache | null
cache: MerpsCache | null
}
marginAccounts: MarginAccount[]
selectedMarginAccount: {
@ -316,34 +322,42 @@ const useMangoStore = create<MangoStore>((set, get) => ({
return mangoClient
.getMerpsGroup(mangoGroupPk)
.then(async (mangoGroup) => {
// TODO also perps
const rootBanks = await mangoGroup.loadRootBanks(DEFAULT_CONNECTION)
const merpsCache = await mangoGroup.loadCache(DEFAULT_CONNECTION)
const spotMarketAccountInfos = await getMultipleAccounts(
DEFAULT_CONNECTION,
mangoGroupConfig.spotMarkets.map((mkt) => mkt.publicKey)
)
const spotOrderBookAccountInfos = await getOrderBookAccountInfos(
mangoGroup.dexProgramId,
spotMarketAccountInfos.map(({ accountInfo }) => accountInfo)
)
const spotMarkets = await decodeAndLoadMarkets(
mangoGroupConfig,
spotMarketAccountInfos
)
const allMarketConfigs = getAllMarkets(mangoGroupConfig);
const allMarketPks = allMarketConfigs.map(m => m.publicKey);
const allMarketAccountInfos = await getMultipleAccounts(DEFAULT_CONNECTION, allMarketPks);
const allMarketAccounts = allMarketConfigs.map((config, i) => {
if (config.kind == 'spot') {
const decoded = Market.getLayout(programId).decode(allMarketAccountInfos[i].accountInfo.data);
return new Market(decoded, config.baseDecimals, config.quoteDecimals, undefined, mangoGroupConfig.serumProgramId);
}
if (config.kind == 'perp') {
const decoded = PerpMarketLayout.decode(allMarketAccountInfos[i].accountInfo.data);
return new PerpMarket(config.publicKey, config.baseDecimals, config.quoteDecimals, decoded);
}
})
const allBidsAndAsksPks = allMarketConfigs.map(m => [m.bidsKey, m.asksKey]).flat()
const allBidsAndAsksAccountInfos = await getMultipleAccounts(DEFAULT_CONNECTION, allBidsAndAsksPks);
const allMarkets = zipDict(allMarketPks.map(pk => pk.toBase58()), allMarketAccounts);
console.log('all', allMarkets);
set((state) => {
state.selectedMangoGroup.current = mangoGroup
state.selectedMangoGroup.rootBanks = rootBanks
state.selectedMangoGroup.cache = merpsCache
state.selectedMangoGroup.markets = spotMarkets
state.selectedMarket.current =
spotMarkets[selectedMarketConfig.publicKey.toString()]
state.selectedMangoGroup.markets = allMarkets
state.selectedMarket.current = allMarkets[selectedMarketConfig.publicKey.toBase58()]
spotMarketAccountInfos
.concat(spotOrderBookAccountInfos)
allMarketAccountInfos
.concat(allBidsAndAsksAccountInfos)
.forEach(({ publicKey, accountInfo }) => {
state.accountInfos[publicKey.toString()] = accountInfo
console.log(publicKey.toBase58(), accountInfo)
state.accountInfos[publicKey.toBase58()] = accountInfo
})
})
})

View File

@ -208,6 +208,20 @@ export const capitalize = (s) => {
return s.charAt(0).toUpperCase() + s.slice(1)
}
export function* chunks(arr, n) {
for (let i = 0; i < arr.length; i += n) {
yield arr.slice(i, i + n)
}
}
export function zipDict<K, V>(keys: K[], values: V[]): Record<K, V> {
let result: Record<K, V> = {}
keys.forEach((key, index) => {
result[key] = values[index]
})
return result
}
export const copyToClipboard = (copyThis) => {
const el = document.createElement('textarea')
el.value = copyThis.toString()

View File

@ -988,7 +988,7 @@
"@blockworks-foundation/mango-client@git+ssh://git@github.com/blockworks-foundation/merps-ts#main":
version "0.0.0"
resolved "git+ssh://git@github.com/blockworks-foundation/merps-ts#67e15d85ee1120a049358c12a46929bdc9ae31ce"
resolved "git+ssh://git@github.com/blockworks-foundation/merps-ts#1f2e298d58badd27069f89e2355550ff5d036ed3"
dependencies:
"@project-serum/serum" "^0.13.38"
"@project-serum/sol-wallet-adapter" "^0.2.0"