update open orders and balances on account change
This commit is contained in:
parent
d57aa19533
commit
029bc99c0a
|
@ -5,27 +5,18 @@ import TabButtons from '@components/shared/TabButtons'
|
|||
import { LinkIcon, QuestionMarkCircleIcon } from '@heroicons/react/20/solid'
|
||||
import { Order } from '@project-serum/serum/lib/market'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Image from 'next/image'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { notify } from 'utils/notifications'
|
||||
import { formatDecimal, formatFixedDecimals } from 'utils/numbers'
|
||||
import { formatDecimal } from 'utils/numbers'
|
||||
|
||||
const TABS = ['Balances', 'Orders']
|
||||
|
||||
const BalanceAndOpenOrders = () => {
|
||||
const [selectedTab, setSelectedTab] = useState('Balances')
|
||||
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
|
||||
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
|
||||
|
||||
useEffect(() => {
|
||||
const actions = mangoStore.getState().actions
|
||||
|
||||
if (mangoAccount && selectedMarket) {
|
||||
actions.fetchOpenOrdersForMarket(selectedMarket)
|
||||
}
|
||||
}, [mangoAccount, selectedMarket])
|
||||
|
||||
return (
|
||||
<div className="hide-scroll h-full overflow-y-scroll">
|
||||
|
@ -46,6 +37,7 @@ const BalanceAndOpenOrders = () => {
|
|||
const Balances = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
|
||||
const spotBalances = mangoStore((s) => s.mangoAccount.spotBalances)
|
||||
const group = mangoStore((s) => s.group)
|
||||
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
|
||||
|
||||
|
@ -124,8 +116,12 @@ const Balances = () => {
|
|||
: 0}
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right font-mono">0.00</td>
|
||||
<td className="text-right font-mono">0.00</td>
|
||||
<td className="text-right font-mono">
|
||||
{spotBalances[bank.mint.toString()]?.inOrders || 0.0}
|
||||
</td>
|
||||
<td className="text-right font-mono">
|
||||
{spotBalances[bank.mint.toString()]?.unsettled || 0.0}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
|
@ -145,6 +141,7 @@ const OpenOrders = () => {
|
|||
const group = mangoStore.getState().group
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const selectedMarket = mangoStore.getState().selectedMarket.current
|
||||
const actions = mangoStore.getState().actions
|
||||
|
||||
if (!group || !mangoAccount) return
|
||||
|
||||
|
@ -156,6 +153,7 @@ const OpenOrders = () => {
|
|||
o.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
|
||||
o.orderId
|
||||
)
|
||||
actions.fetchSerumOpenOrders()
|
||||
notify({
|
||||
type: 'success',
|
||||
title: 'Transaction successful',
|
||||
|
@ -175,10 +173,11 @@ const OpenOrders = () => {
|
|||
)
|
||||
|
||||
return connected ? (
|
||||
openOrders.length ? (
|
||||
Object.values(openOrders).flat().length ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr className="">
|
||||
<th className="text-left">Token</th>
|
||||
<th className="text-right">Side</th>
|
||||
<th className="text-right">Size</th>
|
||||
<th className="text-right">Price</th>
|
||||
|
@ -186,9 +185,18 @@ const OpenOrders = () => {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{openOrders.map((o) => {
|
||||
{Object.entries(openOrders)
|
||||
.map(([marketPk, orders]) => {
|
||||
return orders.map((o) => {
|
||||
const group = mangoStore.getState().group
|
||||
return (
|
||||
<tr key={`${o.side}${o.size}${o.price}`} className="my-1 p-2">
|
||||
<td className="">
|
||||
{
|
||||
group?.getSerum3MarketByPk(new PublicKey(marketPk))
|
||||
?.name
|
||||
}
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<SideBadge side={o.side} />
|
||||
</td>
|
||||
|
@ -201,7 +209,9 @@ const OpenOrders = () => {
|
|||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
})
|
||||
})
|
||||
.flat()}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
|
|
|
@ -12,16 +12,17 @@
|
|||
"postinstall": "tar -xzC public -f vendor/charting_library.tgz;tar -xzC public -f vendor/datafeeds.tgz"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-v4": "git+https://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:x-oauth-basic@github.com/blockworks-foundation/mango-v4.git#main",
|
||||
"@blockworks-foundation/mango-v4": "git+https://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:x-oauth-basic@github.com/blockworks-foundation/mango-v4.git#main",
|
||||
"@headlessui/react": "^1.6.6",
|
||||
"@heroicons/react": "^2.0.10",
|
||||
"@jup-ag/core": "^2.0.0-beta.3",
|
||||
"@project-serum/anchor": "^0.24.2",
|
||||
"@solana/wallet-adapter-base": "^0.9.16",
|
||||
"@solana/wallet-adapter-base": "^0.9.17",
|
||||
"@solana/wallet-adapter-react": "^0.15.18",
|
||||
"@solana/wallet-adapter-wallets": "^0.16.0",
|
||||
"@solana/wallet-adapter-wallets": "^0.18.2",
|
||||
"@solflare-wallet/pfp": "^0.0.6",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/lodash": "^4.14.185",
|
||||
"assert": "^2.0.0",
|
||||
"big.js": "^6.2.1",
|
||||
"dayjs": "^1.11.3",
|
||||
|
@ -29,7 +30,6 @@
|
|||
"immer": "^9.0.12",
|
||||
"jsbi": "^4.3.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"next": "12.2.2",
|
||||
"next-i18next": "^11.1.1",
|
||||
"next-themes": "^0.1.1",
|
||||
|
@ -50,7 +50,6 @@
|
|||
"@project-serum/serum": ">=0.13.62"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "17.0.23",
|
||||
"@types/react": "18.0.3",
|
||||
"@types/react-dom": "18.0.0",
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
|
||||
import { getProfilePicture, ProfilePicture } from '@solflare-wallet/pfp'
|
||||
import { TOKEN_LIST_URL } from '@jup-ag/core'
|
||||
import { Order } from '@project-serum/serum/lib/market'
|
||||
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
|
||||
import { Wallet } from '@solana/wallet-adapter-react'
|
||||
import {
|
||||
MangoClient,
|
||||
|
@ -37,11 +37,14 @@ import {
|
|||
OUTPUT_TOKEN_DEFAULT,
|
||||
} from '../utils/constants'
|
||||
import { retryFn } from '../utils'
|
||||
import { Orderbook, SpotBalances } from 'types'
|
||||
import spotBalancesUpdater from './spotBalancesUpdater'
|
||||
import shallow from 'zustand/shallow'
|
||||
|
||||
const GROUP = new PublicKey('DLdcpC6AsAJ9xeKMR3WhHrN5sM5o7GVVXQhQ5vwisTtz')
|
||||
|
||||
export const connection = new web3.Connection(
|
||||
'https://mango.rpcpool.com/946ef7337da3f5b8d3e4a34e7f88',
|
||||
'https://mango.rpcpool.com/0f9acc0d45173b51bf7d7e09c1e5',
|
||||
'processed'
|
||||
)
|
||||
const options = AnchorProvider.defaultOptions()
|
||||
|
@ -120,7 +123,9 @@ export type MangoStore = {
|
|||
current: MangoAccount | undefined
|
||||
initialLoad: boolean
|
||||
lastUpdatedAt: string
|
||||
openOrders: Order[]
|
||||
openOrderAccounts: OpenOrders[]
|
||||
openOrders: Record<string, Order[]>
|
||||
spotBalances: SpotBalances
|
||||
stats: {
|
||||
interestTotals: { data: TotalInterestDataItem[]; loading: boolean }
|
||||
performance: { data: PerformanceDataItem[]; loading: boolean }
|
||||
|
@ -138,10 +143,7 @@ export type MangoStore = {
|
|||
selectedMarket: {
|
||||
name: string
|
||||
current: Serum3Market | undefined
|
||||
orderbook: {
|
||||
bids: number[][]
|
||||
asks: number[][]
|
||||
}
|
||||
orderbook: Orderbook
|
||||
}
|
||||
serumMarkets: Serum3Market[]
|
||||
serumOrders: Order[] | undefined
|
||||
|
@ -187,7 +189,7 @@ export type MangoStore = {
|
|||
reloadMangoAccount: () => Promise<void>
|
||||
fetchMangoAccounts: (wallet: AnchorWallet) => Promise<void>
|
||||
fetchNfts: (connection: Connection, walletPk: PublicKey) => void
|
||||
fetchOpenOrdersForMarket: (market: Serum3Market) => Promise<void>
|
||||
fetchSerumOpenOrders: (ma?: MangoAccount) => Promise<void>
|
||||
fetchProfilePicture: (wallet: AnchorWallet) => void
|
||||
fetchProfileDetails: (walletPk: string) => void
|
||||
fetchTradeHistory: (mangoAccountPk: string) => Promise<void>
|
||||
|
@ -213,7 +215,9 @@ const mangoStore = create<MangoStore>()(
|
|||
current: undefined,
|
||||
initialLoad: true,
|
||||
lastUpdatedAt: '',
|
||||
openOrders: [],
|
||||
openOrderAccounts: [],
|
||||
openOrders: {},
|
||||
spotBalances: {},
|
||||
stats: {
|
||||
interestTotals: { data: [], loading: false },
|
||||
performance: { data: [], loading: false },
|
||||
|
@ -469,6 +473,7 @@ const mangoStore = create<MangoStore>()(
|
|||
await retryFn(() =>
|
||||
newSelectedMangoAccount!.reloadAccountData(client, group)
|
||||
)
|
||||
await actions.fetchSerumOpenOrders(newSelectedMangoAccount)
|
||||
}
|
||||
|
||||
set((state) => {
|
||||
|
@ -503,21 +508,37 @@ const mangoStore = create<MangoStore>()(
|
|||
}
|
||||
return []
|
||||
},
|
||||
fetchOpenOrdersForMarket: async (market) => {
|
||||
fetchSerumOpenOrders: async (providedMangoAccount) => {
|
||||
const set = get().set
|
||||
const client = get().client
|
||||
const group = await client.getGroup(GROUP)
|
||||
const mangoAccount = get().mangoAccount.current
|
||||
const mangoAccount =
|
||||
providedMangoAccount || get().mangoAccount.current
|
||||
|
||||
if (!mangoAccount) return
|
||||
console.log('mangoAccount', mangoAccount)
|
||||
|
||||
try {
|
||||
let openOrders: Record<string, Order[]> = {}
|
||||
for (const serum3Orders of mangoAccount.serum3) {
|
||||
const market = group.getSerum3MarketByIndex(
|
||||
serum3Orders.marketIndex
|
||||
)
|
||||
if (market) {
|
||||
const orders = await mangoAccount.loadSerum3OpenOrdersForMarket(
|
||||
client,
|
||||
group,
|
||||
market.serumMarketExternal
|
||||
)
|
||||
openOrders[market.serumMarketExternal.toString()] = orders
|
||||
}
|
||||
}
|
||||
const serumOpenOrderAccounts =
|
||||
await mangoAccount.loadSerum3OpenOrdersAccounts(client)
|
||||
|
||||
set((s) => {
|
||||
s.mangoAccount.openOrders = orders
|
||||
s.mangoAccount.openOrders = openOrders
|
||||
s.mangoAccount.openOrderAccounts = serumOpenOrderAccounts
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('Failed loading open orders ', e)
|
||||
|
@ -695,7 +716,7 @@ const mangoStore = create<MangoStore>()(
|
|||
})
|
||||
} catch (e) {
|
||||
notify({ type: 'error', title: 'Failed to load profile details' })
|
||||
console.log(e)
|
||||
console.error(e)
|
||||
set((state) => {
|
||||
state.profile.loadDetails = false
|
||||
})
|
||||
|
@ -706,6 +727,8 @@ const mangoStore = create<MangoStore>()(
|
|||
})
|
||||
)
|
||||
|
||||
mangoStore.subscribe((state) => state.mangoAccount.current, spotBalancesUpdater)
|
||||
|
||||
const getDefaultSelectedMarket = (markets: Serum3Market[]): Serum3Market => {
|
||||
return markets.find((m) => m.name === DEFAULT_MARKET_NAME) || markets[0]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import { toUiDecimals } from '@blockworks-foundation/mango-v4'
|
||||
import { SpotBalances } from 'types'
|
||||
import mangoStore from './mangoStore'
|
||||
|
||||
const spotBalancesUpdater = (_newState: any, _prevState: any) => {
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
const group = mangoStore.getState().group
|
||||
const openOrdersAccounts =
|
||||
mangoStore.getState().mangoAccount.openOrderAccounts
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
if (!mangoAccount || !group) return
|
||||
|
||||
const balances: SpotBalances = {}
|
||||
|
||||
for (const serumMarket of mangoAccount.serum3Active()) {
|
||||
const market = group.getSerum3MarketByIndex(serumMarket.marketIndex)
|
||||
if (!market) continue
|
||||
const openOrdersAccForMkt = openOrdersAccounts.find((oo) =>
|
||||
oo.market.equals(market.serumMarketExternal)
|
||||
)
|
||||
|
||||
let baseTokenUnsettled = 0
|
||||
let quoteTokenUnsettled = 0
|
||||
let baseTokenLockedInOrder = 0
|
||||
let quoteTokenLockedInOrder = 0
|
||||
if (openOrdersAccForMkt) {
|
||||
baseTokenUnsettled = toUiDecimals(
|
||||
openOrdersAccForMkt.baseTokenFree.toNumber(),
|
||||
group.getFirstBankByTokenIndex(serumMarket.baseTokenIndex).mintDecimals
|
||||
)
|
||||
quoteTokenUnsettled = toUiDecimals(
|
||||
openOrdersAccForMkt.quoteTokenFree
|
||||
// @ts-ignore
|
||||
.add(openOrdersAccForMkt['referrerRebatesAccrued'])
|
||||
.toNumber(),
|
||||
group.getFirstBankByTokenIndex(serumMarket.quoteTokenIndex).mintDecimals
|
||||
)
|
||||
baseTokenLockedInOrder = toUiDecimals(
|
||||
openOrdersAccForMkt.baseTokenTotal
|
||||
.sub(openOrdersAccForMkt.baseTokenFree)
|
||||
.toNumber(),
|
||||
group.getFirstBankByTokenIndex(serumMarket.baseTokenIndex).mintDecimals
|
||||
)
|
||||
quoteTokenLockedInOrder = toUiDecimals(
|
||||
openOrdersAccForMkt.quoteTokenTotal
|
||||
.sub(openOrdersAccForMkt.quoteTokenFree)
|
||||
.toNumber(),
|
||||
group.getFirstBankByTokenIndex(serumMarket.quoteTokenIndex).mintDecimals
|
||||
)
|
||||
}
|
||||
|
||||
let quoteBalances =
|
||||
balances[
|
||||
market.getSerum3ExternalMarket(group)!.quoteMintAddress.toString()
|
||||
]
|
||||
if (!quoteBalances) {
|
||||
quoteBalances = balances[
|
||||
market.getSerum3ExternalMarket(group)!.quoteMintAddress.toString()
|
||||
] = { inOrders: 0, unsettled: 0 }
|
||||
}
|
||||
quoteBalances.inOrders += quoteTokenLockedInOrder || 0
|
||||
quoteBalances.unsettled += quoteTokenUnsettled
|
||||
|
||||
let baseBalances =
|
||||
balances[
|
||||
market.getSerum3ExternalMarket(group)!.baseMintAddress.toString()
|
||||
]
|
||||
if (!baseBalances) {
|
||||
baseBalances = balances[
|
||||
market.getSerum3ExternalMarket(group)!.baseMintAddress.toString()
|
||||
] = { inOrders: 0, unsettled: 0 }
|
||||
}
|
||||
baseBalances.inOrders += baseTokenLockedInOrder
|
||||
baseBalances.unsettled += baseTokenUnsettled
|
||||
}
|
||||
|
||||
set((s) => {
|
||||
s.mangoAccount.spotBalances = balances
|
||||
})
|
||||
}
|
||||
|
||||
export default spotBalancesUpdater
|
|
@ -8,3 +8,13 @@ export interface ChartTradeType {
|
|||
feeCost: number
|
||||
marketAddress: string
|
||||
}
|
||||
|
||||
export interface Orderbook {
|
||||
bids: number[][]
|
||||
asks: number[][]
|
||||
}
|
||||
|
||||
export type SpotBalances = Record<
|
||||
string,
|
||||
{ inOrders: number; unsettled: number }
|
||||
>
|
||||
|
|
Loading…
Reference in New Issue