optimize rerenders
This commit is contained in:
parent
a0c0768473
commit
35be5f1276
|
@ -54,13 +54,10 @@ const calculatePNL = (tradeHistory, prices, mangoGroup) => {
|
|||
}
|
||||
|
||||
export default function MarginInfo() {
|
||||
// Connection hook
|
||||
console.log('loading margin info')
|
||||
|
||||
const { connection } = useConnection()
|
||||
// Wallet hook
|
||||
// Get our account info
|
||||
const { marginAccount, mangoGroup } = useMarginAccount()
|
||||
// Working state
|
||||
// Hold the margin account info
|
||||
const [mAccountInfo, setMAccountInfo] = useState<
|
||||
| {
|
||||
label: string
|
||||
|
@ -74,8 +71,6 @@ export default function MarginInfo() {
|
|||
const { tradeHistory } = useTradeHistory()
|
||||
|
||||
useEffect(() => {
|
||||
console.log('marginInfo useEffect')
|
||||
|
||||
if (mangoGroup) {
|
||||
mangoGroup.getPrices(connection).then((prices) => {
|
||||
const collateralRatio = marginAccount
|
||||
|
|
|
@ -19,6 +19,30 @@ const Line = styled.div<any>`
|
|||
props['data-bgcolor'] && `background-color: ${props['data-bgcolor']};`}
|
||||
`
|
||||
|
||||
function getCumulativeOrderbookSide(
|
||||
orders,
|
||||
totalSize,
|
||||
depth,
|
||||
backwards = false
|
||||
) {
|
||||
let cumulative = orders
|
||||
.slice(0, depth)
|
||||
.reduce((cumulative, [price, size], i) => {
|
||||
const cumulativeSize = (cumulative[i - 1]?.cumulativeSize || 0) + size
|
||||
cumulative.push({
|
||||
price,
|
||||
size,
|
||||
cumulativeSize,
|
||||
sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100),
|
||||
})
|
||||
return cumulative
|
||||
}, [])
|
||||
if (backwards) {
|
||||
cumulative = cumulative.reverse()
|
||||
}
|
||||
return cumulative
|
||||
}
|
||||
|
||||
export default function Orderbook({ depth = 7 }) {
|
||||
const markPrice = useMarkPrice()
|
||||
const [orderbook] = useOrderbook()
|
||||
|
@ -42,8 +66,18 @@ export default function Orderbook({ depth = 7 }) {
|
|||
index < depth ? total + size : total
|
||||
const totalSize = bids.reduce(sum, 0) + asks.reduce(sum, 0)
|
||||
|
||||
const bidsToDisplay = getCumulativeOrderbookSide(bids, totalSize, false)
|
||||
const asksToDisplay = getCumulativeOrderbookSide(asks, totalSize, true)
|
||||
const bidsToDisplay = getCumulativeOrderbookSide(
|
||||
bids,
|
||||
totalSize,
|
||||
depth,
|
||||
false
|
||||
)
|
||||
const asksToDisplay = getCumulativeOrderbookSide(
|
||||
asks,
|
||||
totalSize,
|
||||
depth,
|
||||
true
|
||||
)
|
||||
|
||||
currentOrderbookData.current = {
|
||||
bids: orderbook?.bids,
|
||||
|
@ -61,25 +95,6 @@ export default function Orderbook({ depth = 7 }) {
|
|||
}
|
||||
}, [orderbook])
|
||||
|
||||
function getCumulativeOrderbookSide(orders, totalSize, backwards = false) {
|
||||
let cumulative = orders
|
||||
.slice(0, depth)
|
||||
.reduce((cumulative, [price, size], i) => {
|
||||
const cumulativeSize = (cumulative[i - 1]?.cumulativeSize || 0) + size
|
||||
cumulative.push({
|
||||
price,
|
||||
size,
|
||||
cumulativeSize,
|
||||
sizePercent: Math.round((cumulativeSize / (totalSize || 1)) * 100),
|
||||
})
|
||||
return cumulative
|
||||
}, [])
|
||||
if (backwards) {
|
||||
cumulative = cumulative.reverse()
|
||||
}
|
||||
return cumulative
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ElementTitle>Orderbook</ElementTitle>
|
||||
|
|
|
@ -8,20 +8,25 @@ import useMarket from '../hooks/useMarket'
|
|||
import useInterval from '../hooks/useInterval'
|
||||
import ChartApi from '../utils/chartDataConnector'
|
||||
import { ElementTitle } from './styles'
|
||||
import { isEqual } from '../utils/index'
|
||||
|
||||
export default function PublicTrades() {
|
||||
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
|
||||
const [trades, setTrades] = useState([])
|
||||
|
||||
useInterval(async () => {
|
||||
const trades = await ChartApi.getRecentTrades(marketAddress)
|
||||
setTrades(trades)
|
||||
const newTrades = await ChartApi.getRecentTrades(marketAddress)
|
||||
if (trades.length === 0) {
|
||||
setTrades(newTrades)
|
||||
} else if (!isEqual(newTrades[0], trades[0], Object.keys(newTrades[0]))) {
|
||||
setTrades(newTrades)
|
||||
}
|
||||
}, 5000)
|
||||
|
||||
return (
|
||||
<FloatingElement>
|
||||
<ElementTitle>Recent Market Trades</ElementTitle>
|
||||
<div css={xw`grid grid-cols-3 text-gray-500 font-light mb-2`}>
|
||||
<div css={xw`grid grid-cols-3 text-gray-500 mb-2`}>
|
||||
<div>Price ({quoteCurrency}) </div>
|
||||
<div css={xw`text-right`}>Size ({baseCurrency})</div>
|
||||
<div css={xw`text-right`}>Time</div>
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import xw from 'xwind'
|
||||
import { getDecimalCount } from '../utils'
|
||||
import { ChartTradeType } from '../@types/types'
|
||||
import FloatingElement from './FloatingElement'
|
||||
import useMarket from '../hooks/useMarket'
|
||||
import useInterval from '../hooks/useInterval'
|
||||
import ChartApi from '../utils/chartDataConnector'
|
||||
import { ElementTitle } from './styles'
|
||||
import useSerumStore from '../stores/useSerumStore'
|
||||
|
||||
export default function PublicTrades() {
|
||||
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarket()
|
||||
const setSerumStore = useSerumStore((s) => s.set)
|
||||
|
||||
const fetchTrades = useCallback(async () => {
|
||||
const trades = await ChartApi.getRecentTrades(marketAddress)
|
||||
console.log('trades in interval', trades)
|
||||
|
||||
setSerumStore((state) => {
|
||||
state.chartApiTrades = trades
|
||||
})
|
||||
}, [marketAddress])
|
||||
|
||||
// fetch trades on load
|
||||
useEffect(() => {
|
||||
fetchTrades()
|
||||
}, [])
|
||||
|
||||
// refresh trades on interval
|
||||
useInterval(async () => {
|
||||
fetchTrades()
|
||||
}, 5000)
|
||||
|
||||
const tradesRef = useRef(useSerumStore.getState().chartApiTrades)
|
||||
const trades = tradesRef.current
|
||||
useEffect(
|
||||
() =>
|
||||
useSerumStore.subscribe(
|
||||
(trades) => (tradesRef.current = trades as any[]),
|
||||
(state) => state.chartApiTrades
|
||||
),
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<FloatingElement>
|
||||
<ElementTitle>Recent Market Trades</ElementTitle>
|
||||
<div css={xw`grid grid-cols-3 text-gray-500 mb-2`}>
|
||||
<div>Price ({quoteCurrency}) </div>
|
||||
<div css={xw`text-right`}>Size ({baseCurrency})</div>
|
||||
<div css={xw`text-right`}>Time</div>
|
||||
</div>
|
||||
{!!trades.length && (
|
||||
<div>
|
||||
{trades.map((trade: ChartTradeType, i: number) => (
|
||||
<div key={i} css={xw`mb-2 font-light grid grid-cols-3`}>
|
||||
<div
|
||||
style={{
|
||||
color: trade.side === 'buy' ? '#AFD803' : '#E54033',
|
||||
}}
|
||||
>
|
||||
{market?.tickSize && !isNaN(trade.price)
|
||||
? Number(trade.price).toFixed(
|
||||
getDecimalCount(market.tickSize)
|
||||
)
|
||||
: trade.price}
|
||||
</div>
|
||||
<div css={xw`text-right`}>
|
||||
{market?.minOrderSize && !isNaN(trade.size)
|
||||
? Number(trade.size).toFixed(
|
||||
getDecimalCount(market.minOrderSize)
|
||||
)
|
||||
: trade.size}
|
||||
</div>
|
||||
<div css={xw`text-right text-gray-500`}>
|
||||
{trade.time && new Date(trade.time).toLocaleTimeString()}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</FloatingElement>
|
||||
)
|
||||
}
|
|
@ -36,7 +36,7 @@ export default function TradeForm({
|
|||
ref: ({ size, price }: { size?: number; price?: number }) => void
|
||||
) => void
|
||||
}) {
|
||||
// console.log('reloading trade form')
|
||||
console.log('reloading trade form')
|
||||
|
||||
const [side, setSide] = useState<'buy' | 'sell'>('buy')
|
||||
const { baseCurrency, quoteCurrency, market } = useMarket()
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import styled from '@emotion/styled'
|
||||
import xw from 'xwind'
|
||||
|
||||
export const ElementTitle = styled.div(xw`
|
||||
flex justify-center mb-4 text-lg tracking-tight
|
||||
`)
|
||||
export const ElementTitle = styled.div(xw`flex justify-center mb-4 text-lg`)
|
||||
|
|
|
@ -4,10 +4,12 @@ import { IDS } from '@blockworks-foundation/mango-client'
|
|||
import useMangoStore from '../stores/useMangoStore'
|
||||
|
||||
const useConnection = () => {
|
||||
// console.log('loading useConnection')
|
||||
|
||||
const setSolanaStore = useMangoStore((s) => s.set)
|
||||
const { cluster, current: connection, endpoint } = useMangoStore(
|
||||
(s) => s.connection
|
||||
)
|
||||
const setSolanaStore = useMangoStore((s) => s.set)
|
||||
|
||||
const sendConnection = useMemo(() => new Connection(endpoint, 'recent'), [
|
||||
endpoint,
|
||||
|
@ -29,17 +31,17 @@ const useConnection = () => {
|
|||
return () => {
|
||||
connection.removeAccountChangeListener(id)
|
||||
}
|
||||
}, [connection])
|
||||
}, [endpoint])
|
||||
|
||||
useEffect(() => {
|
||||
const id = connection.onSlotChange(() => null)
|
||||
return () => {
|
||||
connection.removeSlotChangeListener(id)
|
||||
}
|
||||
}, [connection])
|
||||
}, [endpoint])
|
||||
|
||||
const programId = IDS[cluster].mango_program_id
|
||||
const dexProgramId = IDS[cluster]?.dex_program_id
|
||||
const programId = useMemo(() => IDS[cluster].mango_program_id, [cluster])
|
||||
const dexProgramId = useMemo(() => IDS[cluster]?.dex_program_id, [cluster])
|
||||
|
||||
return { connection, dexProgramId, cluster, programId, sendConnection }
|
||||
}
|
||||
|
|
|
@ -1,49 +1,168 @@
|
|||
import { useEffect } from 'react'
|
||||
import { Market } from '@project-serum/serum'
|
||||
import { PublicKey, AccountInfo } from '@solana/web3.js'
|
||||
import useConnection from './useConnection'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import useMarketList from './useMarketList'
|
||||
|
||||
// function useOrderBookSubscribe(market) {
|
||||
// const setMangoStore = useMangoStore((s) => s.set)
|
||||
// const { connection } = useConnection()
|
||||
// const accountPkAsString = account ? account.toString() : null
|
||||
|
||||
// useInterval(async () => {
|
||||
// if (!account) return
|
||||
|
||||
// const info = await connection.getAccountInfo(account)
|
||||
// console.log('fetching account info on interval', accountPkAsString)
|
||||
|
||||
// setMangoStore((state) => {
|
||||
// state.accountInfos[accountPkAsString] = info
|
||||
// })
|
||||
// }, 60000)
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!account) return
|
||||
// let previousInfo: AccountInfo<Buffer> | null = null
|
||||
|
||||
// const subscriptionId = connection.onAccountChange(account, (info) => {
|
||||
// if (
|
||||
// !previousInfo ||
|
||||
// !previousInfo.data.equals(info.data) ||
|
||||
// previousInfo.lamports !== info.lamports
|
||||
// ) {
|
||||
// previousInfo = info
|
||||
// setMangoStore((state) => {
|
||||
// state.accountInfos[accountPkAsString] = previousInfo
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
|
||||
// return () => {
|
||||
// connection.removeAccountChangeListener(subscriptionId)
|
||||
// }
|
||||
// }, [account, connection])
|
||||
// }
|
||||
|
||||
const marketAddressSelector = (s) => s.selectedMarket.address
|
||||
const mangoGroupMarketsSelector = (s) => s.selectedMangoGroup.markets
|
||||
|
||||
const useHydrateStore = () => {
|
||||
const setMangoStore = useMangoStore((s) => s.set)
|
||||
const selectedMarket = useMangoStore((s) => s.selectedMarket)
|
||||
const selectedMarketAddress = useMangoStore(marketAddressSelector)
|
||||
const marketsForSelectedMangoGroup = useMangoStore(mangoGroupMarketsSelector)
|
||||
const { connection, dexProgramId } = useConnection()
|
||||
const { marketList } = useMarketList()
|
||||
|
||||
// load selected market
|
||||
useEffect(() => {
|
||||
console.log(
|
||||
'useEffect loading market',
|
||||
selectedMarket.address,
|
||||
dexProgramId
|
||||
)
|
||||
console.log('useEffect loading market', selectedMarketAddress, dexProgramId)
|
||||
Market.load(
|
||||
connection,
|
||||
new PublicKey(selectedMarket.address),
|
||||
new PublicKey(selectedMarketAddress),
|
||||
{},
|
||||
new PublicKey(dexProgramId)
|
||||
)
|
||||
.then((market) => {
|
||||
.then(async (market) => {
|
||||
// @ts-ignore
|
||||
const bidAcccount = market._decoded.bids
|
||||
const bidInfo = await connection.getAccountInfo(bidAcccount)
|
||||
// @ts-ignore
|
||||
const askAccount = market._decoded.asks
|
||||
const askInfo = await connection.getAccountInfo(askAccount)
|
||||
setMangoStore((state) => {
|
||||
state.market.current = market
|
||||
// @ts-ignore
|
||||
state.accountInfos[market._decoded.bids.toString()] = null
|
||||
// @ts-ignore
|
||||
state.accountInfos[market._decoded.asks.toString()] = null
|
||||
state.accountInfos[askAccount.toString()] = askInfo
|
||||
state.accountInfos[bidAcccount.toString()] = bidInfo
|
||||
})
|
||||
})
|
||||
.catch(
|
||||
(e) => {
|
||||
console.error('failed to load market', e)
|
||||
}
|
||||
// TODO
|
||||
// notify({
|
||||
// message: 'Error loading market',
|
||||
// description: e.message,
|
||||
// type: 'error',
|
||||
// }),
|
||||
)
|
||||
}, [selectedMarket])
|
||||
}, [selectedMarketAddress])
|
||||
|
||||
return null
|
||||
// load all markets for mangoGroup
|
||||
useEffect(() => {
|
||||
console.log('loading all markets for mangoGroup')
|
||||
|
||||
Promise.all(
|
||||
marketList.map((mkt) => {
|
||||
return Market.load(connection, mkt.address, {}, mkt.programId)
|
||||
})
|
||||
).then((markets) => {
|
||||
setMangoStore((state) => {
|
||||
markets.forEach((market) => {
|
||||
state.selectedMangoGroup.markets[market.publicKey.toString()] = market
|
||||
// @ts-ignore
|
||||
state.accountInfos[market._decoded.bids.toString()] = null
|
||||
// @ts-ignore
|
||||
state.accountInfos[market._decoded.asks.toString()] = null
|
||||
})
|
||||
})
|
||||
})
|
||||
}, [marketList])
|
||||
|
||||
// hydrate orderbook for all markets in mango group
|
||||
useEffect(() => {
|
||||
const subscriptionIds = Object.entries(marketsForSelectedMangoGroup).map(
|
||||
([, market]) => {
|
||||
let previousBidInfo: AccountInfo<Buffer> | null = null
|
||||
let previousAskInfo: AccountInfo<Buffer> | null = null
|
||||
return [
|
||||
connection.onAccountChange(
|
||||
// @ts-ignore
|
||||
market._decoded.bids,
|
||||
(info) => {
|
||||
if (
|
||||
!previousBidInfo ||
|
||||
!previousBidInfo.data.equals(info.data) ||
|
||||
previousBidInfo.lamports !== info.lamports
|
||||
) {
|
||||
previousBidInfo = info
|
||||
setMangoStore((state) => {
|
||||
// @ts-ignore
|
||||
const pkString = market._decoded.bids.toString()
|
||||
state.accountInfos[pkString] = previousBidInfo
|
||||
})
|
||||
}
|
||||
}
|
||||
),
|
||||
connection.onAccountChange(
|
||||
// @ts-ignore
|
||||
market._decoded.asks,
|
||||
(info) => {
|
||||
if (
|
||||
!previousAskInfo ||
|
||||
!previousAskInfo.data.equals(info.data) ||
|
||||
previousAskInfo.lamports !== info.lamports
|
||||
) {
|
||||
previousAskInfo = info
|
||||
setMangoStore((state) => {
|
||||
// @ts-ignore
|
||||
const pkString = market._decoded.bids.toString()
|
||||
state.accountInfos[pkString] = previousAskInfo
|
||||
})
|
||||
}
|
||||
}
|
||||
),
|
||||
]
|
||||
}
|
||||
)
|
||||
console.log('subscription ids', subscriptionIds)
|
||||
|
||||
return () => {
|
||||
for (const id of subscriptionIds.flat()) {
|
||||
connection.removeAccountChangeListener(id)
|
||||
}
|
||||
}
|
||||
}, [marketsForSelectedMangoGroup])
|
||||
}
|
||||
|
||||
export default useHydrateStore
|
||||
|
|
|
@ -3,7 +3,7 @@ import { PublicKey } from '@solana/web3.js'
|
|||
import { IDS } from '@blockworks-foundation/mango-client'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import useConnection from './useConnection'
|
||||
// import useInterval from './useInterval'
|
||||
import useInterval from './useInterval'
|
||||
import useWallet from './useWallet'
|
||||
|
||||
const useMarginAccount = () => {
|
||||
|
@ -46,7 +46,6 @@ const useMarginAccount = () => {
|
|||
|
||||
const fetchMarginAccounts = useCallback(() => {
|
||||
if (!mangoClient || !mangoGroup || !connected || !wallet.publicKey) return
|
||||
console.log('fetching margin accounts from: ', wallet.publicKey.toString())
|
||||
|
||||
mangoClient
|
||||
.getMarginAccountsForOwner(
|
||||
|
@ -80,10 +79,10 @@ const useMarginAccount = () => {
|
|||
fetchMarginAccounts()
|
||||
}, [connected, fetchMarginAccounts])
|
||||
|
||||
// useInterval(() => {
|
||||
// fetchMarginAccounts()
|
||||
// fetchMangoGroup()
|
||||
// }, 20000)
|
||||
useInterval(() => {
|
||||
fetchMarginAccounts()
|
||||
// fetchMangoGroup()
|
||||
}, 2000)
|
||||
|
||||
return {
|
||||
mangoClient,
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
import useConnection from './useConnection'
|
||||
import useMarket from './useMarket'
|
||||
import useMarginAccount from './useMarginAcccount'
|
||||
|
||||
export function useOpenOrders() {
|
||||
const { market } = useMarket()
|
||||
const { marginAccount, mangoGroup } = useMarginAccount()
|
||||
const { bidOrderbook, askOrderbook } = useOrderbookAccounts()
|
||||
const { cluster } = useConnection()
|
||||
|
||||
if (!market || !mangoGroup || !marginAccount) return null
|
||||
|
||||
const marketIndex = mangoGroup.getMarketIndex(market)
|
||||
const openOrdersAccount = marginAccount.openOrdersAccounts[marketIndex]
|
||||
|
||||
if (!openOrdersAccount || !bidOrderbook || !askOrderbook) {
|
||||
return null
|
||||
}
|
||||
|
||||
const spotMarketFromIDs = Object.entries(IDS[cluster].spot_markets).find(
|
||||
([symbol, address]) => {
|
||||
return market.address.toString() === address
|
||||
}
|
||||
)
|
||||
|
||||
const marketName = spotMarketFromIDs ? spotMarketFromIDs[0] : ''
|
||||
|
||||
return market
|
||||
.filterForOpenOrders(bidOrderbook, askOrderbook, [openOrdersAccount])
|
||||
.map((order) => ({ ...order, marketName, market }))
|
||||
}
|
|
@ -1,53 +1,9 @@
|
|||
import { useEffect, useMemo } from 'react'
|
||||
import { PublicKey, AccountInfo } from '@solana/web3.js'
|
||||
import { Orderbook } from '@project-serum/serum'
|
||||
import useMarket from './useMarket'
|
||||
import useInterval from './useInterval'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import useConnection from './useConnection'
|
||||
|
||||
function useAccountInfo(account: PublicKey) {
|
||||
const setSolanaStore = useMangoStore((s) => s.set)
|
||||
const { connection } = useConnection()
|
||||
const accountPkAsString = account ? account.toString() : null
|
||||
|
||||
useInterval(async () => {
|
||||
if (!account) return
|
||||
|
||||
const info = await connection.getAccountInfo(account)
|
||||
console.log('fetching account info on interval', accountPkAsString)
|
||||
|
||||
setSolanaStore((state) => {
|
||||
state.accountInfos[accountPkAsString] = info
|
||||
})
|
||||
}, 60000)
|
||||
|
||||
useEffect(() => {
|
||||
if (!account) return
|
||||
let previousInfo: AccountInfo<Buffer> | null = null
|
||||
|
||||
const subscriptionId = connection.onAccountChange(account, (info) => {
|
||||
if (
|
||||
!previousInfo ||
|
||||
!previousInfo.data.equals(info.data) ||
|
||||
previousInfo.lamports !== info.lamports
|
||||
) {
|
||||
previousInfo = info
|
||||
setSolanaStore((state) => {
|
||||
state.accountInfos[accountPkAsString] = previousInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
connection.removeAccountChangeListener(subscriptionId)
|
||||
}
|
||||
}, [account, connection])
|
||||
}
|
||||
|
||||
export function useAccountData(publicKey) {
|
||||
useAccountInfo(publicKey)
|
||||
|
||||
const account = publicKey ? publicKey.toString() : null
|
||||
const accountInfo = useMangoStore((s) => s.accountInfos[account])
|
||||
return accountInfo && Buffer.from(accountInfo.data)
|
||||
|
|
|
@ -56,6 +56,9 @@ interface MangoStore extends State {
|
|||
selectedMangoGroup: {
|
||||
name: string
|
||||
current: MangoGroup | null
|
||||
markets: {
|
||||
[key: string]: Market
|
||||
}
|
||||
}
|
||||
selectedMarginAccount: {
|
||||
current: MarginAccount | null
|
||||
|
@ -82,6 +85,7 @@ const useMangoStore = create<MangoStore>(
|
|||
selectedMangoGroup: {
|
||||
name: DEFAULT_MANGO_GROUP,
|
||||
current: null,
|
||||
markets: {},
|
||||
},
|
||||
selectedMarket: {
|
||||
name: Object.entries(
|
||||
|
|
|
@ -9,6 +9,7 @@ interface SerumStore extends State {
|
|||
asks: any[]
|
||||
}
|
||||
fills: any[]
|
||||
chartApiTrades: any[]
|
||||
set: (x: any) => void
|
||||
}
|
||||
|
||||
|
@ -18,6 +19,7 @@ const useSerumStore = create<SerumStore>((set) => ({
|
|||
asks: [],
|
||||
},
|
||||
fills: [],
|
||||
chartApiTrades: [], // TODO remove transient updates
|
||||
set: (fn) => set(produce(fn)),
|
||||
}))
|
||||
|
||||
|
|
Loading…
Reference in New Issue