mango-v4-ui/store/mangoStore.ts

1247 lines
40 KiB
TypeScript
Raw Normal View History

import dayjs from 'dayjs'
import produce from 'immer'
2022-05-03 21:20:14 -07:00
import create from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
import { AnchorProvider, BN, Wallet, web3 } from '@coral-xyz/anchor'
2023-08-29 12:34:40 -07:00
import {
ConfirmOptions,
Connection,
Keypair,
PublicKey,
RecentPrioritizationFees,
TransactionInstruction,
2023-08-29 12:34:40 -07:00
} from '@solana/web3.js'
import { OpenOrders, Order } from '@project-serum/serum/lib/market'
import { Orderbook } from '@project-serum/serum'
import { Wallet as WalletAdapter } from '@solana/wallet-adapter-react'
2022-05-03 21:20:14 -07:00
import {
MangoClient,
Group,
MangoAccount,
Serum3Market,
2022-06-21 03:58:57 -07:00
MANGO_V4_ID,
Bank,
2022-10-31 11:26:17 -07:00
PerpOrder,
2022-11-01 06:10:08 -07:00
PerpPosition,
2022-11-30 07:46:20 -08:00
BookSide,
ParsedFillEvent,
2023-07-03 05:29:10 -07:00
getLargestPerpPositions,
getClosestToLiquidationPerpPositions,
2022-05-03 21:20:14 -07:00
} from '@blockworks-foundation/mango-v4'
2022-05-03 21:20:14 -07:00
import EmptyWallet from '../utils/wallet'
import { TransactionNotification, notify } from '../utils/notifications'
2022-07-05 20:37:49 -07:00
import {
2023-04-14 04:48:09 -07:00
getNFTsByOwner,
2022-07-05 20:37:49 -07:00
getTokenAccountsByOwnerWithWrappedSol,
TokenAccount,
} from '../utils/tokens'
import { Token } from '../types/jupiter'
2022-08-20 11:17:57 -07:00
import {
2023-01-02 14:21:41 -08:00
CONNECTION_COMMITMENT,
2022-09-13 23:24:26 -07:00
DEFAULT_MARKET_NAME,
2022-08-20 11:17:57 -07:00
INPUT_TOKEN_DEFAULT,
LAST_ACCOUNT_KEY,
MANGO_DATA_API_URL,
2023-08-29 12:34:40 -07:00
MAX_PRIORITY_FEE_KEYS,
2022-08-20 11:17:57 -07:00
OUTPUT_TOKEN_DEFAULT,
2023-01-12 16:16:10 -08:00
PAGINATION_PAGE_LENGTH,
2022-12-15 18:19:11 -08:00
RPC_PROVIDER_KEY,
2023-07-26 17:37:35 -07:00
SWAP_MARGIN_KEY,
2022-08-20 11:17:57 -07:00
} from '../utils/constants'
2023-01-18 20:34:25 -08:00
import {
2023-02-23 16:28:49 -08:00
ActivityFeed,
EmptyObject,
2023-01-18 20:34:25 -08:00
OrderbookL2,
2023-02-23 16:28:49 -08:00
PerpStatsItem,
2023-01-18 20:34:25 -08:00
PerpTradeHistory,
SerumEvent,
2023-01-18 20:34:25 -08:00
SpotBalances,
SpotTradeHistory,
2023-02-23 16:28:49 -08:00
SwapHistoryItem,
TotalInterestDataItem,
TradeForm,
TokenStatsItem,
NFT,
TourSettings,
ProfileDetails,
MangoTokenStatsItem,
2023-06-06 22:00:59 -07:00
ThemeData,
2023-07-04 17:05:03 -07:00
PositionStat,
2023-07-25 03:43:19 -07:00
OrderbookTooltip,
2023-01-18 20:34:25 -08:00
} from 'types'
import spotBalancesUpdater from './spotBalancesUpdater'
2022-10-10 19:16:13 -07:00
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
2022-11-01 06:10:08 -07:00
import perpPositionsUpdater from './perpPositionsUpdater'
2023-07-16 20:41:13 -07:00
import {
DEFAULT_PRIORITY_FEE,
TRITON_DEDICATED_URL,
} from '@components/settings/RpcSettings'
2023-02-14 15:41:47 -08:00
import {
2023-04-13 04:25:58 -07:00
IExecutionLineAdapter,
2023-02-14 15:41:47 -08:00
IOrderLineAdapter,
} from '@public/charting_library/charting_library'
import { nftThemeMeta } from 'utils/theme'
2023-08-29 12:34:40 -07:00
import maxBy from 'lodash/maxBy'
import mapValues from 'lodash/mapValues'
import groupBy from 'lodash/groupBy'
import sampleSize from 'lodash/sampleSize'
2022-05-03 21:20:14 -07:00
2022-12-08 11:58:54 -08:00
const GROUP = new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX')
2022-12-15 18:19:11 -08:00
const ENDPOINTS = [
{
name: 'mainnet-beta',
2023-07-16 20:41:13 -07:00
url: process.env.NEXT_PUBLIC_ENDPOINT || TRITON_DEDICATED_URL,
websocket: process.env.NEXT_PUBLIC_ENDPOINT || TRITON_DEDICATED_URL,
2022-12-15 18:19:11 -08:00
custom: false,
},
{
name: 'devnet',
url: 'https://realms-develope-935c.devnet.rpcpool.com/67f608dc-a353-4191-9c34-293a5061b536',
websocket:
'https://realms-develope-935c.devnet.rpcpool.com/67f608dc-a353-4191-9c34-293a5061b536',
2022-12-15 18:19:11 -08:00
custom: false,
},
]
2023-06-26 15:27:41 -07:00
const options = {
...AnchorProvider.defaultOptions(),
preflightCommitment: 'confirmed',
} as ConfirmOptions
2022-11-18 11:11:06 -08:00
export const CLUSTER: 'mainnet-beta' | 'devnet' = 'mainnet-beta'
2022-12-27 13:13:15 -08:00
const ENDPOINT = ENDPOINTS.find((e) => e.name === CLUSTER) || ENDPOINTS[0]
2023-05-22 07:50:44 -07:00
export const emptyWallet = new EmptyWallet(Keypair.generate())
2022-12-15 18:19:11 -08:00
2023-01-19 12:31:45 -08:00
const initMangoClient = (
provider: AnchorProvider,
opts: {
prioritizationFee: number
prependedGlobalAdditionalInstructions: TransactionInstruction[]
} = {
prioritizationFee: DEFAULT_PRIORITY_FEE,
prependedGlobalAdditionalInstructions: [],
},
2023-01-19 12:31:45 -08:00
): MangoClient => {
2022-12-15 18:19:11 -08:00
return MangoClient.connect(provider, CLUSTER, MANGO_V4_ID[CLUSTER], {
2023-01-19 12:31:45 -08:00
prioritizationFee: opts.prioritizationFee,
prependedGlobalAdditionalInstructions:
opts.prependedGlobalAdditionalInstructions,
idsSource: 'api',
2022-12-15 18:19:11 -08:00
postSendTxCallback: ({ txid }: { txid: string }) => {
notify({
title: 'Transaction sent',
description: 'Waiting for confirmation',
type: 'confirm',
txid: txid,
})
},
})
}
2022-05-03 21:20:14 -07:00
export const DEFAULT_TRADE_FORM: TradeForm = {
side: 'buy',
price: undefined,
baseSize: '',
quoteSize: '',
2023-08-02 03:45:06 -07:00
tradeType: 'Limit',
2023-06-15 03:45:42 -07:00
triggerPrice: '',
postOnly: false,
ioc: false,
2023-01-16 20:00:42 -08:00
reduceOnly: false,
}
2022-05-03 21:20:14 -07:00
export type MangoStore = {
2022-09-26 23:22:51 -07:00
activityFeed: {
2023-02-23 16:28:49 -08:00
feed: Array<ActivityFeed>
2022-09-26 23:22:51 -07:00
loading: boolean
2023-02-03 00:27:54 -08:00
queryParams: string
2022-09-26 23:22:51 -07:00
}
2022-07-05 20:37:49 -07:00
connected: boolean
2022-06-22 04:55:03 -07:00
connection: Connection
2022-05-03 21:20:14 -07:00
group: Group | undefined
groupLoaded: boolean
2022-05-03 21:20:14 -07:00
client: MangoClient
2023-07-21 07:28:26 -07:00
showUserSetup: boolean
2022-07-27 23:35:18 -07:00
mangoAccount: {
current: MangoAccount | undefined
initialLoad: boolean
lastSlot: number
openOrderAccounts: OpenOrders[]
2022-10-31 11:26:17 -07:00
openOrders: Record<string, Order[] | PerpOrder[]>
2022-11-01 06:10:08 -07:00
perpPositions: PerpPosition[]
spotBalances: SpotBalances
2023-01-15 21:13:34 -08:00
interestTotals: { data: TotalInterestDataItem[]; loading: boolean }
swapHistory: {
data: SwapHistoryItem[]
2023-04-13 22:22:39 -07:00
initialLoad: boolean
2023-01-18 20:33:51 -08:00
loading: boolean
2022-08-12 05:13:11 -07:00
}
2023-01-18 20:34:25 -08:00
tradeHistory: {
data: Array<SpotTradeHistory | PerpTradeHistory>
loading: boolean
}
2022-07-27 23:35:18 -07:00
}
2022-10-07 04:47:15 -07:00
mangoAccounts: MangoAccount[]
2022-05-03 21:20:14 -07:00
markets: Serum3Market[] | undefined
transactionNotificationIdCounter: number
transactionNotifications: Array<TransactionNotification>
2022-10-10 19:16:13 -07:00
perpMarkets: PerpMarket[]
2022-12-07 17:31:50 -08:00
perpStats: {
loading: boolean
2023-04-05 17:22:20 -07:00
data: PerpStatsItem[] | null
2023-07-03 05:29:10 -07:00
positions: {
initialLoad: boolean
loading: boolean
largest: PositionStat[]
closestToLiq: PositionStat[]
}
2022-12-07 17:31:50 -08:00
}
2023-07-25 03:43:19 -07:00
orderbookTooltip: OrderbookTooltip | undefined
2022-09-20 18:03:59 -07:00
profile: {
2022-12-23 16:12:32 -08:00
details: ProfileDetails | null
2022-09-20 18:03:59 -07:00
loadDetails: boolean
}
prependedGlobalAdditionalInstructions: TransactionInstruction[]
2023-08-29 12:34:40 -07:00
priorityFee: number
2022-09-13 23:24:26 -07:00
selectedMarket: {
2023-06-29 21:00:28 -07:00
name: string | undefined
2022-10-10 19:16:13 -07:00
current: Serum3Market | PerpMarket | undefined
fills: (ParsedFillEvent | SerumEvent)[]
bidsAccount: BookSide | Orderbook | undefined
asksAccount: BookSide | Orderbook | undefined
orderbook: OrderbookL2
2022-11-25 02:10:23 -08:00
markPrice: number
lastSeenSlot: {
bids: number
asks: number
}
2022-09-13 23:24:26 -07:00
}
serumMarkets: Serum3Market[]
2022-05-03 21:20:14 -07:00
serumOrders: Order[] | undefined
2022-09-21 21:25:24 -07:00
settings: {
loading: boolean
2022-09-22 21:00:42 -07:00
tours: TourSettings
2022-09-21 21:25:24 -07:00
uiLocked: boolean
}
2023-01-02 04:19:30 -08:00
successAnimation: {
swap: boolean
2023-06-05 19:49:51 -07:00
theme: boolean
2023-01-02 04:19:30 -08:00
trade: boolean
}
2022-07-25 22:27:53 -07:00
swap: {
inputBank: Bank | undefined
outputBank: Bank | undefined
2022-08-02 11:04:00 -07:00
inputTokenInfo: Token | undefined
outputTokenInfo: Token | undefined
2022-08-08 10:42:18 -07:00
margin: boolean
slippage: number
swapMode: 'ExactIn' | 'ExactOut'
amountIn: string
amountOut: string
flipPrices: boolean
2022-07-25 22:27:53 -07:00
}
2022-05-03 21:20:14 -07:00
set: (x: (x: MangoStore) => void) => void
2023-06-06 22:00:59 -07:00
themeData: ThemeData
2022-12-05 19:23:22 -08:00
tokenStats: {
2022-12-11 02:08:50 -08:00
initialLoad: boolean
2022-12-05 19:23:22 -08:00
loading: boolean
2022-12-24 08:38:25 -08:00
data: TokenStatsItem[] | null
mangoStats: MangoTokenStatsItem[]
2022-12-05 19:23:22 -08:00
}
tradeForm: TradeForm
2023-02-09 03:41:15 -08:00
tradingView: {
2023-02-08 01:58:03 -08:00
orderLines: Map<string | BN, IOrderLineAdapter>
2023-04-13 04:25:58 -07:00
tradeExecutions: Map<string, IExecutionLineAdapter>
2023-02-09 03:41:15 -08:00
}
2022-07-05 20:37:49 -07:00
wallet: {
tokens: TokenAccount[]
2022-08-01 22:32:21 -07:00
nfts: {
data: NFT[] | []
loading: boolean
}
2022-07-05 20:37:49 -07:00
}
2023-08-31 19:23:53 -07:00
window: {
width: number
height: number
}
2022-05-03 21:20:14 -07:00
actions: {
fetchAccountInterestTotals: (mangoAccountPk: string) => Promise<void>
2022-09-26 23:22:51 -07:00
fetchActivityFeed: (
mangoAccountPk: string,
2022-09-28 20:35:34 -07:00
offset?: number,
2023-04-20 03:55:52 -07:00
params?: string,
2023-07-21 11:47:53 -07:00
limit?: number,
2022-08-12 05:13:11 -07:00
) => Promise<void>
2022-05-03 21:20:14 -07:00
fetchGroup: () => Promise<void>
reloadMangoAccount: (slot?: number) => Promise<void>
fetchMangoAccounts: (ownerPk: PublicKey) => Promise<void>
2022-08-01 22:32:21 -07:00
fetchNfts: (connection: Connection, walletPk: PublicKey) => void
fetchOpenOrders: (refetchMangoAccount?: boolean) => Promise<void>
2022-12-07 17:31:50 -08:00
fetchPerpStats: () => void
2023-07-03 05:29:10 -07:00
fetchPositionsStats: () => void
2022-09-20 18:03:59 -07:00
fetchProfileDetails: (walletPk: string) => void
2022-12-19 11:42:28 -08:00
fetchSwapHistory: (
mangoAccountPk: string,
2023-01-11 19:21:23 -08:00
timeout?: number,
2023-04-20 03:55:52 -07:00
offset?: number,
2023-07-21 11:47:53 -07:00
limit?: number,
2022-12-19 11:42:28 -08:00
) => Promise<void>
2022-12-05 19:23:22 -08:00
fetchTokenStats: () => void
2022-09-22 21:00:42 -07:00
fetchTourSettings: (walletPk: string) => void
fetchWalletTokens: (walletPk: PublicKey) => Promise<void>
connectMangoClientWithWallet: (wallet: WalletAdapter) => Promise<void>
loadMarketFills: () => Promise<void>
2022-12-15 18:19:11 -08:00
updateConnection: (url: string) => void
setPrependedGlobalAdditionalInstructions: (
instructions: TransactionInstruction[],
) => void
2023-08-29 12:34:40 -07:00
estimatePriorityFee: (feeMultiplier: number) => Promise<void>
2022-05-03 21:20:14 -07:00
}
}
2022-09-01 12:06:21 -07:00
const mangoStore = create<MangoStore>()(
subscribeWithSelector((_set, get) => {
2022-12-27 13:34:05 -08:00
let rpcUrl = ENDPOINT.url
2023-07-26 17:37:35 -07:00
let swapMargin = true
2022-12-27 13:13:15 -08:00
2022-12-15 18:19:11 -08:00
if (typeof window !== 'undefined' && CLUSTER === 'mainnet-beta') {
const urlFromLocalStorage = localStorage.getItem(RPC_PROVIDER_KEY)
2023-07-26 17:37:35 -07:00
const swapMarginFromLocalStorage = localStorage.getItem(SWAP_MARGIN_KEY)
2022-12-15 18:19:11 -08:00
rpcUrl = urlFromLocalStorage
2023-07-02 19:31:00 -07:00
? JSON.parse(urlFromLocalStorage)
2022-12-27 13:34:05 -08:00
: ENDPOINT.url
2023-07-26 17:37:35 -07:00
swapMargin = swapMarginFromLocalStorage
? JSON.parse(swapMarginFromLocalStorage)
: true
2022-12-15 18:19:11 -08:00
}
2022-12-27 13:34:05 -08:00
let connection: Connection
try {
2023-01-02 14:21:41 -08:00
connection = new web3.Connection(rpcUrl, CONNECTION_COMMITMENT)
2022-12-27 13:34:05 -08:00
} catch {
2023-01-02 14:21:41 -08:00
connection = new web3.Connection(ENDPOINT.url, CONNECTION_COMMITMENT)
2022-12-27 13:34:05 -08:00
}
2022-12-27 13:13:15 -08:00
const provider = new AnchorProvider(connection, emptyWallet, options)
2022-12-15 18:19:11 -08:00
provider.opts.skipPreflight = true
const client = initMangoClient(provider)
2022-12-27 13:13:15 -08:00
2022-05-03 21:20:14 -07:00
return {
2022-09-26 23:22:51 -07:00
activityFeed: {
feed: [],
2022-09-28 20:35:34 -07:00
loading: true,
2023-02-03 00:27:54 -08:00
queryParams: '',
2022-09-26 23:22:51 -07:00
},
2022-07-05 20:37:49 -07:00
connected: false,
2022-06-22 04:55:03 -07:00
connection,
2022-05-03 21:20:14 -07:00
group: undefined,
groupLoaded: false,
2022-12-15 18:19:11 -08:00
client,
2023-07-21 07:28:26 -07:00
showUserSetup: false,
2022-07-27 23:35:18 -07:00
mangoAccount: {
current: undefined,
initialLoad: true,
lastSlot: 0,
openOrderAccounts: [],
openOrders: {},
2022-11-01 06:10:08 -07:00
perpPositions: [],
spotBalances: {},
2023-01-18 16:19:46 -08:00
interestTotals: { data: [], loading: false },
2023-04-13 22:22:39 -07:00
swapHistory: { data: [], loading: true, initialLoad: true },
2023-01-12 16:16:10 -08:00
tradeHistory: { data: [], loading: true },
2022-07-27 23:35:18 -07:00
},
2022-10-07 04:47:15 -07:00
mangoAccounts: [],
2022-05-03 21:20:14 -07:00
markets: undefined,
transactionNotificationIdCounter: 0,
transactionNotifications: [],
2022-10-10 19:16:13 -07:00
perpMarkets: [],
2022-12-07 17:31:50 -08:00
perpStats: {
loading: false,
data: [],
2023-07-03 05:29:10 -07:00
positions: {
initialLoad: true,
loading: true,
largest: [],
closestToLiq: [],
},
2022-12-07 17:31:50 -08:00
},
2023-07-25 03:43:19 -07:00
orderbookTooltip: undefined,
2022-09-20 18:03:59 -07:00
profile: {
loadDetails: false,
details: { profile_name: '', trader_category: '', wallet_pk: '' },
},
2023-08-29 12:34:40 -07:00
priorityFee: DEFAULT_PRIORITY_FEE,
prependedGlobalAdditionalInstructions: [],
2022-09-13 23:24:26 -07:00
selectedMarket: {
name: 'SOL/USDC',
2022-09-13 23:24:26 -07:00
current: undefined,
fills: [],
2022-11-30 07:46:20 -08:00
bidsAccount: undefined,
asksAccount: undefined,
lastSeenSlot: {
bids: 0,
asks: 0,
},
2022-09-13 23:24:26 -07:00
orderbook: {
bids: [],
asks: [],
},
2022-11-25 02:10:23 -08:00
markPrice: 0,
2022-09-13 23:24:26 -07:00
},
serumMarkets: [],
2022-05-03 21:20:14 -07:00
serumOrders: undefined,
2022-09-01 12:06:21 -07:00
set: (fn) => _set(produce(fn)),
2022-09-13 23:24:26 -07:00
settings: {
2022-09-21 21:25:24 -07:00
loading: false,
2022-09-22 21:00:42 -07:00
tours: {
account_tour_seen: true,
swap_tour_seen: true,
trade_tour_seen: true,
wallet_pk: '',
},
2022-09-13 23:24:26 -07:00
uiLocked: true,
},
2023-01-02 04:19:30 -08:00
successAnimation: {
swap: false,
2023-06-05 19:49:51 -07:00
theme: false,
2023-01-02 04:19:30 -08:00
trade: false,
},
swap: {
inputBank: undefined,
outputBank: undefined,
inputTokenInfo: undefined,
outputTokenInfo: undefined,
2023-07-26 17:37:35 -07:00
margin: swapMargin,
slippage: 0.5,
swapMode: 'ExactIn',
amountIn: '',
amountOut: '',
flipPrices: false,
},
themeData: nftThemeMeta.default,
2022-12-05 19:23:22 -08:00
tokenStats: {
2022-12-11 02:08:50 -08:00
initialLoad: false,
loading: true,
2022-12-05 19:23:22 -08:00
data: [],
mangoStats: [],
2022-12-05 19:23:22 -08:00
},
tradeForm: DEFAULT_TRADE_FORM,
2023-02-09 03:41:15 -08:00
tradingView: {
2023-02-08 01:58:03 -08:00
orderLines: new Map(),
2023-04-13 04:25:58 -07:00
tradeExecutions: new Map(),
2023-02-09 03:41:15 -08:00
},
2022-07-05 20:37:49 -07:00
wallet: {
tokens: [],
2022-08-01 22:32:21 -07:00
nfts: {
data: [],
loading: false,
},
2022-07-05 20:37:49 -07:00
},
2023-08-31 19:23:53 -07:00
window: {
width: 0,
height: 0,
},
2022-05-03 21:20:14 -07:00
actions: {
fetchAccountInterestTotals: async (mangoAccountPk: string) => {
2022-08-12 05:13:11 -07:00
const set = get().set
set((state) => {
2023-01-15 21:13:34 -08:00
state.mangoAccount.interestTotals.loading = true
2022-08-12 05:13:11 -07:00
})
try {
const response = await fetch(
2023-07-21 11:47:53 -07:00
`${MANGO_DATA_API_URL}/stats/interest-account-total?mango-account=${mangoAccountPk}`,
2022-08-12 05:13:11 -07:00
)
2023-02-23 09:28:09 -08:00
const parsedResponse:
| Omit<TotalInterestDataItem, 'symbol'>[]
| null = await response.json()
if (parsedResponse) {
const entries: [string, Omit<TotalInterestDataItem, 'symbol'>][] =
Object.entries(parsedResponse).sort((a, b) =>
2023-07-21 11:47:53 -07:00
b[0].localeCompare(a[0]),
2023-02-23 09:28:09 -08:00
)
2022-08-12 05:13:11 -07:00
2023-02-23 09:28:09 -08:00
const stats: TotalInterestDataItem[] = entries
.map(([key, value]) => {
return { ...value, symbol: key }
})
.filter((x) => x)
2022-08-12 05:13:11 -07:00
2023-02-23 09:28:09 -08:00
set((state) => {
state.mangoAccount.interestTotals.data = stats
state.mangoAccount.interestTotals.loading = false
})
}
2022-08-12 05:13:11 -07:00
} catch {
set((state) => {
2023-01-15 21:13:34 -08:00
state.mangoAccount.interestTotals.loading = false
2022-08-12 05:13:11 -07:00
})
console.error({
2022-08-12 05:13:11 -07:00
title: 'Failed to load account interest totals',
type: 'error',
})
}
},
2022-09-28 20:35:34 -07:00
fetchActivityFeed: async (
mangoAccountPk: string,
offset = 0,
2023-04-20 03:55:52 -07:00
params = '',
2023-07-21 11:47:53 -07:00
limit = PAGINATION_PAGE_LENGTH,
2022-09-28 20:35:34 -07:00
) => {
2022-09-26 23:22:51 -07:00
const set = get().set
2023-01-11 19:21:23 -08:00
const loadedFeed = mangoStore.getState().activityFeed.feed
2022-12-19 21:33:43 -08:00
2022-09-26 23:22:51 -07:00
try {
const response = await fetch(
2023-04-20 03:55:52 -07:00
`${MANGO_DATA_API_URL}/stats/activity-feed?mango-account=${mangoAccountPk}&offset=${offset}&limit=${limit}${
2022-09-29 20:22:55 -07:00
params ? params : ''
2023-07-21 11:47:53 -07:00
}`,
2022-09-26 23:22:51 -07:00
)
2023-02-23 18:22:24 -08:00
const parsedResponse: null | EmptyObject | Array<ActivityFeed> =
await response.json()
2022-09-26 23:22:51 -07:00
2023-02-23 18:22:24 -08:00
if (Array.isArray(parsedResponse)) {
2023-02-23 16:28:49 -08:00
const entries = Object.entries(parsedResponse).sort((a, b) =>
2023-07-21 11:47:53 -07:00
b[0].localeCompare(a[0]),
)
2023-02-23 16:28:49 -08:00
const latestFeed = entries
.map(([key, value]) => {
2023-06-23 03:57:53 -07:00
// ETH should be renamed to ETH (Portal) in the database
2023-06-26 15:27:41 -07:00
const symbol = value.activity_details.symbol
2023-06-23 03:57:53 -07:00
if (symbol === 'ETH') {
value.activity_details.symbol = 'ETH (Portal)'
}
return {
...value,
symbol: key,
}
2023-02-23 16:28:49 -08:00
})
.sort(
(a, b) =>
dayjs(b.block_datetime).unix() -
2023-07-21 11:47:53 -07:00
dayjs(a.block_datetime).unix(),
2023-02-23 16:28:49 -08:00
)
2022-09-26 23:22:51 -07:00
2023-02-23 16:28:49 -08:00
// only add to current feed if data request is offset and the mango account hasn't changed
const combinedFeed =
offset !== 0 ? loadedFeed.concat(latestFeed) : latestFeed
set((state) => {
state.activityFeed.feed = combinedFeed
})
}
2023-02-03 00:27:54 -08:00
} catch (e) {
console.error('Failed to fetch account activity feed', e)
2022-09-26 23:22:51 -07:00
} finally {
set((state) => {
state.activityFeed.loading = false
})
}
},
2022-05-03 21:20:14 -07:00
fetchGroup: async () => {
try {
2022-06-21 03:58:57 -07:00
const set = get().set
2022-05-03 21:20:14 -07:00
const client = get().client
2022-08-03 14:46:37 -07:00
const group = await client.getGroup(GROUP)
2023-06-29 21:00:28 -07:00
let selectedMarketName = get().selectedMarket.name
if (!selectedMarketName) {
selectedMarketName = DEFAULT_MARKET_NAME
}
2022-09-13 23:24:26 -07:00
2022-08-19 21:03:26 -07:00
const inputBank =
group?.banksMapByName.get(INPUT_TOKEN_DEFAULT)?.[0]
const outputBank =
group?.banksMapByName.get(OUTPUT_TOKEN_DEFAULT)?.[0]
2022-09-13 23:24:26 -07:00
const serumMarkets = Array.from(
2023-07-21 11:47:53 -07:00
group.serum3MarketsMapByExternal.values(),
2023-06-18 22:57:43 -07:00
).map((m) => {
// remove this when market name is updated
if (m.name === 'MSOL/SOL') {
m.name = 'mSOL/SOL'
}
return m
})
2023-03-07 15:12:40 -08:00
const perpMarkets = Array.from(group.perpMarketsMapByName.values())
.filter(
(p) =>
p.publicKey.toString() !==
2023-07-21 11:47:53 -07:00
'9Y8paZ5wUpzLFfQuHz8j2RtPrKsDtHx9sbgFmWb5abCw',
2023-03-07 15:12:40 -08:00
)
.sort((a, b) => a.name.localeCompare(b.name))
2022-08-03 14:46:37 -07:00
2023-02-14 19:56:44 -08:00
const selectedMarket =
2022-11-21 19:56:56 -08:00
serumMarkets.find((m) => m.name === selectedMarketName) ||
2023-02-14 19:56:44 -08:00
perpMarkets.find((m) => m.name === selectedMarketName) ||
serumMarkets[0]
2022-11-21 19:56:56 -08:00
2022-05-03 21:20:14 -07:00
set((state) => {
state.group = group
state.groupLoaded = true
2022-09-13 23:24:26 -07:00
state.serumMarkets = serumMarkets
2022-10-10 19:16:13 -07:00
state.perpMarkets = perpMarkets
2023-02-14 19:56:44 -08:00
state.selectedMarket.current = selectedMarket
2022-08-19 21:03:26 -07:00
if (!state.swap.inputBank && !state.swap.outputBank) {
state.swap.inputBank = inputBank
state.swap.outputBank = outputBank
2022-08-25 17:39:09 -07:00
} else {
state.swap.inputBank = group.getFirstBankByMint(
2023-07-21 11:47:53 -07:00
state.swap.inputBank!.mint,
)
2022-08-25 17:39:09 -07:00
state.swap.outputBank = group.getFirstBankByMint(
2023-07-21 11:47:53 -07:00
state.swap.outputBank!.mint,
2022-08-25 17:39:09 -07:00
)
2022-08-19 21:03:26 -07:00
}
2022-05-03 21:20:14 -07:00
})
} catch (e) {
notify({ type: 'info', title: 'Unable to refresh data' })
console.error('Error fetching group', e)
2022-05-03 21:20:14 -07:00
}
},
reloadMangoAccount: async (confirmationSlot) => {
2022-08-03 14:46:37 -07:00
const set = get().set
const actions = get().actions
2022-05-03 21:20:14 -07:00
try {
const group = get().group
const client = get().client
const mangoAccount = get().mangoAccount.current
if (!group) throw new Error('Group not loaded')
if (!mangoAccount)
throw new Error('No mango account exists for reload')
2022-05-03 21:20:14 -07:00
const { value: reloadedMangoAccount, slot } =
await mangoAccount.reloadWithSlot(client)
2023-08-12 13:07:00 -07:00
const lastSlot = get().mangoAccount.lastSlot
if (
!confirmationSlot ||
(confirmationSlot && slot > confirmationSlot)
) {
if (slot > lastSlot) {
const ma = get().mangoAccounts.find((ma) =>
ma.publicKey.equals(reloadedMangoAccount.publicKey),
)
if (ma) {
Object.assign(ma, reloadedMangoAccount)
}
set((state) => {
state.mangoAccount.current = reloadedMangoAccount
state.mangoAccount.lastSlot = slot
})
}
} else if (confirmationSlot && slot < confirmationSlot) {
actions.reloadMangoAccount(confirmationSlot)
}
2022-05-03 21:20:14 -07:00
} catch (e) {
console.error('Error reloading mango acct', e)
actions.reloadMangoAccount()
} finally {
2022-07-27 23:35:18 -07:00
set((state) => {
state.mangoAccount.initialLoad = false
2022-07-27 23:35:18 -07:00
})
2022-05-03 21:20:14 -07:00
}
},
fetchMangoAccounts: async (ownerPk: PublicKey) => {
const set = get().set
const actions = get().actions
2022-08-01 18:08:08 -07:00
try {
const group = get().group
const client = get().client
const selectedMangoAccount = get().mangoAccount.current
2022-08-01 18:08:08 -07:00
if (!group) throw new Error('Group not loaded')
if (!client) throw new Error('Client not loaded')
2023-03-04 09:06:27 -08:00
const [ownerMangoAccounts, delegateAccounts] = await Promise.all([
client.getMangoAccountsForOwner(group, ownerPk),
client.getMangoAccountsForDelegate(group, ownerPk),
])
const mangoAccounts = [...ownerMangoAccounts, ...delegateAccounts]
const selectedAccountIsNotInAccountsList = mangoAccounts.find(
(x) =>
x.publicKey.toBase58() ===
2023-07-21 11:47:53 -07:00
selectedMangoAccount?.publicKey.toBase58(),
)
if (!mangoAccounts?.length) {
set((state) => {
state.mangoAccounts = []
state.mangoAccount.current = undefined
})
return
}
let newSelectedMangoAccount = selectedMangoAccount
if (!selectedMangoAccount || !selectedAccountIsNotInAccountsList) {
const lastAccount = localStorage.getItem(LAST_ACCOUNT_KEY)
newSelectedMangoAccount = mangoAccounts[0]
2023-03-02 17:18:52 -08:00
let lastViewedAccount
if (typeof lastAccount === 'string') {
2023-03-02 17:18:52 -08:00
try {
lastViewedAccount = mangoAccounts.find(
2023-07-21 11:47:53 -07:00
(m) => m.publicKey.toString() === JSON.parse(lastAccount),
2023-03-02 17:18:52 -08:00
)
} catch (e) {
console.error('Error parsing last account', e)
}
newSelectedMangoAccount = lastViewedAccount || mangoAccounts[0]
}
}
if (newSelectedMangoAccount) {
2023-02-09 13:22:54 -08:00
await newSelectedMangoAccount.reloadSerum3OpenOrders(client)
2023-01-14 12:02:15 -08:00
set((state) => {
state.mangoAccount.current = newSelectedMangoAccount
state.mangoAccount.initialLoad = false
})
actions.fetchOpenOrders()
}
2023-01-14 12:02:15 -08:00
await Promise.all(
2023-07-21 11:47:53 -07:00
mangoAccounts.map((ma) => ma.reloadSerum3OpenOrders(client)),
2023-01-14 12:02:15 -08:00
)
set((state) => {
2022-10-07 04:47:15 -07:00
state.mangoAccounts = mangoAccounts
})
2022-08-01 18:08:08 -07:00
} catch (e) {
console.error('Error fetching mango accts', e)
} finally {
set((state) => {
state.mangoAccount.initialLoad = false
})
2022-08-01 18:08:08 -07:00
}
},
2022-08-01 22:32:21 -07:00
fetchNfts: async (connection: Connection, ownerPk: PublicKey) => {
const set = get().set
set((state) => {
state.wallet.nfts.loading = true
})
try {
2023-04-14 04:48:09 -07:00
const nfts = await getNFTsByOwner(ownerPk, connection)
2022-08-01 22:32:21 -07:00
set((state) => {
2023-04-14 04:48:09 -07:00
state.wallet.nfts.data = nfts
2022-08-01 22:32:21 -07:00
})
} catch (error) {
2023-08-26 14:25:22 -07:00
console.warn('Error: unable to fetch nfts.', error)
2023-07-23 06:57:50 -07:00
} finally {
set((state) => {
state.wallet.nfts.loading = false
})
2022-08-01 22:32:21 -07:00
}
},
fetchOpenOrders: async (refetchMangoAccount = false) => {
2022-09-13 23:24:26 -07:00
const set = get().set
const client = get().client
const group = get().group
if (refetchMangoAccount) {
2022-12-14 17:07:48 -08:00
await get().actions.reloadMangoAccount()
}
const mangoAccount = get().mangoAccount.current
if (!mangoAccount || !group) return
2022-09-13 23:24:26 -07:00
try {
2022-11-19 17:40:06 -08:00
const openOrders: Record<string, Order[] | PerpOrder[]> = {}
2022-10-31 11:26:17 -07:00
let serumOpenOrderAccounts: OpenOrders[] = []
2023-02-21 18:24:46 -08:00
const activeSerumMarketIndices = [
...new Set(mangoAccount.serum3Active().map((s) => s.marketIndex)),
]
2023-03-08 16:34:13 -08:00
if (activeSerumMarketIndices.length) {
await Promise.all(
activeSerumMarketIndices.map(async (serum3Orders) => {
const market =
group.getSerum3MarketByMarketIndex(serum3Orders)
if (market) {
const orders =
await mangoAccount.loadSerum3OpenOrdersForMarket(
client,
group,
2023-07-21 11:47:53 -07:00
market.serumMarketExternal,
2023-03-08 16:34:13 -08:00
)
openOrders[market.serumMarketExternal.toString()] = orders
}
2023-07-21 11:47:53 -07:00
}),
2023-03-08 16:34:13 -08:00
)
}
2023-02-21 18:24:46 -08:00
if (mangoAccount.serum3Active().length) {
serumOpenOrderAccounts = Array.from(
2023-07-21 11:47:53 -07:00
mangoAccount.serum3OosMapByMarketIndex.values(),
)
await mangoAccount.loadSerum3OpenOrdersAccounts(client)
}
2022-10-31 11:26:17 -07:00
2023-02-21 18:24:46 -08:00
const activePerpMarketIndices = [
...new Set(
2023-07-21 11:47:53 -07:00
mangoAccount.perpOrdersActive().map((p) => p.orderMarket),
2023-02-21 18:24:46 -08:00
),
]
await Promise.all(
activePerpMarketIndices.map(async (perpMktIndex) => {
const market = group.getPerpMarketByMarketIndex(perpMktIndex)
const orders = await mangoAccount.loadPerpOpenOrdersForMarket(
client,
group,
2023-03-08 16:34:13 -08:00
perpMktIndex,
2023-07-21 11:47:53 -07:00
market._bids ? false : true,
2023-02-21 18:24:46 -08:00
)
openOrders[market.publicKey.toString()] = orders
2023-07-21 11:47:53 -07:00
}),
2023-02-21 18:24:46 -08:00
)
2022-10-31 11:26:17 -07:00
set((s) => {
s.mangoAccount.openOrders = openOrders
s.mangoAccount.openOrderAccounts = serumOpenOrderAccounts
})
2022-09-13 23:24:26 -07:00
} catch (e) {
console.error('Failed loading open orders ', e)
}
},
2022-12-07 17:31:50 -08:00
fetchPerpStats: async () => {
const set = get().set
const group = get().group
2023-04-03 20:49:28 -07:00
if (!group) return []
2022-12-07 17:31:50 -08:00
set((state) => {
state.perpStats.loading = true
})
try {
const response = await fetch(
2023-07-21 11:47:53 -07:00
`${MANGO_DATA_API_URL}/perp-historical-stats?mango-group=${group?.publicKey.toString()}`,
2022-12-07 17:31:50 -08:00
)
const data = await response.json()
set((state) => {
state.perpStats.data = data
state.perpStats.loading = false
})
} catch {
set((state) => {
state.perpStats.loading = false
})
notify({
title: 'Failed to fetch token stats data',
type: 'error',
})
}
},
2023-07-03 05:29:10 -07:00
fetchPositionsStats: async () => {
const set = get().set
const group = get().group
const client = get().client
if (!group) return
try {
2023-07-04 17:05:03 -07:00
const allMangoAccounts = await client.getAllMangoAccounts(
group,
2023-07-21 11:47:53 -07:00
true,
2023-07-04 17:05:03 -07:00
)
if (allMangoAccounts && allMangoAccounts.length) {
const [largestPositions, closestToLiq]: [
PositionStat[],
2023-07-21 11:47:53 -07:00
PositionStat[],
2023-07-04 17:05:03 -07:00
] = await Promise.all([
getLargestPerpPositions(client, group, allMangoAccounts),
getClosestToLiquidationPerpPositions(
client,
group,
2023-07-21 11:47:53 -07:00
allMangoAccounts,
2023-07-04 17:05:03 -07:00
),
])
set((state) => {
if (largestPositions && largestPositions.length) {
const positionsToShow = largestPositions.slice(0, 5)
for (const position of positionsToShow) {
const ma = allMangoAccounts.find(
2023-07-21 11:47:53 -07:00
(acc) => acc.publicKey === position.mangoAccount,
2023-07-04 17:05:03 -07:00
)
position.account = ma
}
state.perpStats.positions.largest = positionsToShow
}
if (closestToLiq && closestToLiq.length) {
const positionsToShow = closestToLiq.slice(0, 5)
for (const position of positionsToShow) {
const ma = allMangoAccounts.find(
2023-07-21 11:47:53 -07:00
(acc) => acc.publicKey === position.mangoAccount,
2023-07-04 17:05:03 -07:00
)
position.account = ma
}
state.perpStats.positions.closestToLiq = positionsToShow
}
})
}
2023-07-03 05:29:10 -07:00
} catch (e) {
console.log('failed to fetch perp positions stats', e)
} finally {
const notLoaded =
mangoStore.getState().perpStats.positions.initialLoad
set((state) => {
state.perpStats.positions.loading = false
if (notLoaded) {
state.perpStats.positions.initialLoad = false
}
})
}
},
2023-01-11 19:21:23 -08:00
fetchSwapHistory: async (
mangoAccountPk: string,
timeout = 0,
2023-04-20 03:55:52 -07:00
offset = 0,
2023-07-21 11:47:53 -07:00
limit = PAGINATION_PAGE_LENGTH,
2023-01-11 19:21:23 -08:00
) => {
2022-08-12 23:06:09 -07:00
const set = get().set
2023-01-11 19:21:23 -08:00
const loadedSwapHistory =
2023-01-18 20:33:51 -08:00
mangoStore.getState().mangoAccount.swapHistory.data
2023-01-11 19:21:23 -08:00
2022-12-19 11:42:28 -08:00
setTimeout(async () => {
try {
const history = await fetch(
2023-07-21 11:47:53 -07:00
`${MANGO_DATA_API_URL}/stats/swap-history?mango-account=${mangoAccountPk}&offset=${offset}&limit=${limit}`,
2022-12-19 11:42:28 -08:00
)
const parsedHistory = await history.json()
const sortedHistory =
parsedHistory && parsedHistory.length
? parsedHistory.sort(
(a: SwapHistoryItem, b: SwapHistoryItem) =>
dayjs(b.block_datetime).unix() -
2023-07-21 11:47:53 -07:00
dayjs(a.block_datetime).unix(),
2022-12-19 11:42:28 -08:00
)
: []
2022-08-12 23:06:09 -07:00
2023-01-11 19:21:23 -08:00
const combinedHistory =
2023-01-18 02:36:45 -08:00
offset !== 0
2023-01-11 19:21:23 -08:00
? loadedSwapHistory.concat(sortedHistory)
: sortedHistory
2022-12-19 11:42:28 -08:00
set((state) => {
2023-01-18 20:33:51 -08:00
state.mangoAccount.swapHistory.data = combinedHistory
2022-12-19 11:42:28 -08:00
})
2023-01-18 02:36:45 -08:00
} catch (e) {
console.error('Unable to fetch swap history', e)
2023-01-11 19:21:23 -08:00
} finally {
2023-04-13 22:22:39 -07:00
const notLoaded =
mangoStore.getState().mangoAccount.swapHistory.initialLoad
2022-12-19 11:42:28 -08:00
set((state) => {
2023-01-18 20:33:51 -08:00
state.mangoAccount.swapHistory.loading = false
2023-04-13 22:22:39 -07:00
if (notLoaded) {
state.mangoAccount.swapHistory.initialLoad = false
}
2022-12-19 11:42:28 -08:00
})
}
}, timeout)
2022-08-12 23:06:09 -07:00
},
2022-12-05 19:23:22 -08:00
fetchTokenStats: async () => {
const set = get().set
const group = get().group
2022-12-11 02:08:50 -08:00
if (!group) return
2022-12-05 19:23:22 -08:00
set((state) => {
state.tokenStats.loading = true
})
try {
const response = await fetch(
2023-07-21 11:47:53 -07:00
`${MANGO_DATA_API_URL}/token-historical-stats?mango-group=${group?.publicKey.toString()}`,
2022-12-05 19:23:22 -08:00
)
const data = await response.json()
let mangoStats: MangoTokenStatsItem[] = []
if (data && data.length) {
mangoStats = data.reduce(
(a: MangoTokenStatsItem[], c: TokenStatsItem) => {
const banks = Array.from(group.banksMapByMint)
.map(([_mintAddress, banks]) => banks)
.map((b) => b[0])
const bank: Bank | undefined = banks.find(
2023-07-21 11:47:53 -07:00
(b) => b.tokenIndex === c.token_index,
)
const hasDate = a.find(
2023-07-21 11:47:53 -07:00
(d: MangoTokenStatsItem) => d.date === c.date_hour,
)
if (!hasDate) {
a.push({
date: c.date_hour,
depositValue: Math.floor(c.total_deposits * c.price),
borrowValue: Math.floor(c.total_borrows * c.price),
feesCollected: c.collected_fees * bank!.uiPrice,
})
} else {
hasDate.depositValue =
hasDate.depositValue +
Math.floor(c.total_deposits * c.price)
hasDate.borrowValue =
hasDate.borrowValue +
Math.floor(c.total_borrows * c.price)
hasDate.feesCollected =
hasDate.feesCollected + c.collected_fees * bank!.uiPrice
}
return a.sort(
(a, b) =>
2023-07-21 11:47:53 -07:00
new Date(a.date).getTime() - new Date(b.date).getTime(),
)
},
2023-07-21 11:47:53 -07:00
[],
)
}
2022-12-05 19:23:22 -08:00
set((state) => {
state.tokenStats.data = data
state.tokenStats.mangoStats = mangoStats
2022-12-11 02:08:50 -08:00
state.tokenStats.initialLoad = true
2022-12-05 19:23:22 -08:00
state.tokenStats.loading = false
})
} catch {
set((state) => {
state.tokenStats.loading = false
})
notify({
2022-12-07 17:31:50 -08:00
title: 'Failed to fetch token stats data',
2022-12-05 19:23:22 -08:00
type: 'error',
})
}
},
fetchWalletTokens: async (walletPk: PublicKey) => {
2022-07-05 20:37:49 -07:00
const set = get().set
const connection = get().connection
if (walletPk) {
try {
const token = await getTokenAccountsByOwnerWithWrappedSol(
connection,
2023-07-21 11:47:53 -07:00
walletPk,
)
2022-07-05 20:37:49 -07:00
set((state) => {
state.wallet.tokens = token
})
} catch (e) {
notify({
title: 'Failed to refresh wallet balances.',
type: 'info',
})
}
2022-07-05 20:37:49 -07:00
} else {
set((state) => {
state.wallet.tokens = []
})
}
},
connectMangoClientWithWallet: async (wallet: WalletAdapter) => {
2022-09-01 12:06:21 -07:00
const set = get().set
try {
const provider = new AnchorProvider(
connection,
wallet.adapter as unknown as Wallet,
2023-07-21 11:47:53 -07:00
options,
)
provider.opts.skipPreflight = true
2023-08-29 12:34:40 -07:00
const priorityFee = get().priorityFee ?? DEFAULT_PRIORITY_FEE
2023-08-29 12:34:40 -07:00
const client = initMangoClient(provider, {
prioritizationFee: priorityFee,
prependedGlobalAdditionalInstructions:
get().prependedGlobalAdditionalInstructions,
2023-08-29 12:34:40 -07:00
})
2022-12-27 13:13:15 -08:00
set((s) => {
s.client = client
})
2023-02-23 16:28:49 -08:00
} catch (e) {
if (e instanceof Error && e.name.includes('WalletLoadError')) {
notify({
title: `${wallet.adapter.name} Error`,
type: 'error',
description: `Please install ${wallet.adapter.name} and then reload this page.`,
})
}
}
},
async setPrependedGlobalAdditionalInstructions(
instructions: TransactionInstruction[],
) {
const set = get().set
const client = mangoStore.getState().client
const provider = client.program.provider as AnchorProvider
provider.opts.skipPreflight = true
const newClient = initMangoClient(provider, {
prioritizationFee: get().priorityFee,
prependedGlobalAdditionalInstructions: instructions,
})
set((s) => {
s.client = newClient
s.prependedGlobalAdditionalInstructions = instructions
})
},
2022-09-21 21:25:24 -07:00
async fetchProfileDetails(walletPk: string) {
2022-07-26 21:40:17 -07:00
const set = get().set
2022-09-21 21:25:24 -07:00
set((state) => {
state.profile.loadDetails = true
})
2022-07-26 21:40:17 -07:00
try {
2022-09-21 21:25:24 -07:00
const response = await fetch(
2023-07-21 11:47:53 -07:00
`${MANGO_DATA_API_URL}/user-data/profile-details?wallet-pk=${walletPk}`,
2022-09-21 21:25:24 -07:00
)
const data = await response.json()
2022-07-26 21:40:17 -07:00
set((state) => {
2022-09-21 21:25:24 -07:00
state.profile.details = data
state.profile.loadDetails = false
2022-07-26 21:40:17 -07:00
})
} catch (e) {
2022-11-01 11:13:02 -07:00
console.error(e)
2022-07-26 21:40:17 -07:00
set((state) => {
2022-09-21 21:25:24 -07:00
state.profile.loadDetails = false
2022-07-26 21:40:17 -07:00
})
}
},
2022-09-22 21:00:42 -07:00
async fetchTourSettings(walletPk: string) {
2022-09-20 18:03:59 -07:00
const set = get().set
set((state) => {
2022-09-21 21:25:24 -07:00
state.settings.loading = true
2022-09-20 18:03:59 -07:00
})
try {
const response = await fetch(
2023-07-21 11:47:53 -07:00
`${MANGO_DATA_API_URL}/user-data/settings-unsigned?wallet-pk=${walletPk}`,
2022-09-20 18:03:59 -07:00
)
const data = await response.json()
set((state) => {
2022-09-22 21:00:42 -07:00
state.settings.tours = data
2022-09-21 21:25:24 -07:00
state.settings.loading = false
2022-09-20 18:03:59 -07:00
})
} catch (e) {
console.error(e)
2022-09-20 18:03:59 -07:00
set((state) => {
2022-09-21 21:25:24 -07:00
state.settings.loading = false
2022-09-20 18:03:59 -07:00
})
}
},
async loadMarketFills() {
const set = get().set
const selectedMarket = get().selectedMarket.current
const group = get().group
const client = get().client
const connection = get().connection
try {
let serumMarket
let perpMarket
if (!group || !selectedMarket) return
if (selectedMarket instanceof Serum3Market) {
serumMarket = group.getSerum3ExternalMarket(
2023-07-21 11:47:53 -07:00
selectedMarket.serumMarketExternal,
)
} else {
perpMarket = selectedMarket
}
let loadedFills: (ParsedFillEvent | SerumEvent)[] = []
if (serumMarket) {
const serumFills = (await serumMarket.loadFills(
connection,
2023-07-21 11:47:53 -07:00
10000,
)) as SerumEvent[]
loadedFills = serumFills.filter((f) => !f?.eventFlags?.maker)
} else if (perpMarket) {
const perpFills = (await perpMarket.loadFills(
2023-07-21 11:47:53 -07:00
client,
)) as unknown as ParsedFillEvent[]
loadedFills = perpFills.reverse()
}
set((state) => {
state.selectedMarket.fills = loadedFills
})
} catch (err) {
2023-01-20 05:45:43 -08:00
console.error('Error fetching fills:', err)
}
},
2022-12-15 18:19:11 -08:00
updateConnection(endpointUrl) {
const set = get().set
2022-12-27 13:13:15 -08:00
const client = mangoStore.getState().client
2023-01-02 14:21:41 -08:00
const newConnection = new web3.Connection(
endpointUrl,
2023-07-21 11:47:53 -07:00
CONNECTION_COMMITMENT,
2023-01-02 14:21:41 -08:00
)
2022-12-27 13:13:15 -08:00
const oldProvider = client.program.provider as AnchorProvider
const newProvider = new AnchorProvider(
newConnection,
oldProvider.wallet,
2023-07-21 11:47:53 -07:00
options,
2022-12-27 13:13:15 -08:00
)
2022-12-15 18:19:11 -08:00
newProvider.opts.skipPreflight = true
const newClient = initMangoClient(newProvider, {
prependedGlobalAdditionalInstructions:
get().prependedGlobalAdditionalInstructions,
prioritizationFee: DEFAULT_PRIORITY_FEE,
})
2022-12-15 18:19:11 -08:00
set((state) => {
state.connection = newConnection
state.client = newClient
})
},
2023-08-29 12:34:40 -07:00
estimatePriorityFee: async (feeMultiplier) => {
const set = get().set
const group = mangoStore.getState().group
const client = mangoStore.getState().client
const mangoAccount = get().mangoAccount.current
if (!mangoAccount || !group || !client) return
const altResponse = await connection.getAddressLookupTable(
group.addressLookupTables[0],
)
const altKeys = altResponse.value?.state.addresses
if (!altKeys) return
const addresses = sampleSize(altKeys, MAX_PRIORITY_FEE_KEYS)
const fees = await connection.getRecentPrioritizationFees({
lockedWritableAccounts: addresses,
})
if (fees.length < 1) return
// get max priority fee per slot (and sort by slot from old to new)
const maxFeeBySlot = mapValues(groupBy(fees, 'slot'), (items) =>
maxBy(items, 'prioritizationFee'),
)
const maximumFees = Object.values(maxFeeBySlot).sort(
(a, b) => a!.slot - b!.slot,
) as RecentPrioritizationFees[]
// get median of last 20 fees
const recentFees = maximumFees.slice(
Math.max(maximumFees.length - 20, 0),
)
const mid = Math.floor(recentFees.length / 2)
const medianFee =
recentFees.length % 2 !== 0
? recentFees[mid].prioritizationFee
: (recentFees[mid - 1].prioritizationFee +
recentFees[mid].prioritizationFee) /
2
const feeEstimate = Math.ceil(medianFee * feeMultiplier)
const provider = client.program.provider as AnchorProvider
provider.opts.skipPreflight = true
const newClient = initMangoClient(provider, {
prioritizationFee: feeEstimate,
prependedGlobalAdditionalInstructions:
get().prependedGlobalAdditionalInstructions,
2023-08-29 12:34:40 -07:00
})
set((state) => {
state.priorityFee = feeEstimate
state.client = newClient
})
},
2022-05-03 21:20:14 -07:00
},
}
2023-07-21 11:47:53 -07:00
}),
2022-05-03 21:20:14 -07:00
)
mangoStore.subscribe((state) => state.mangoAccount.current, spotBalancesUpdater)
mangoStore.subscribe(
(state) => state.mangoAccount.openOrderAccounts,
2023-07-21 11:47:53 -07:00
spotBalancesUpdater,
)
2022-11-01 06:10:08 -07:00
mangoStore.subscribe(
(state) => state.mangoAccount.current,
2023-07-21 11:47:53 -07:00
perpPositionsUpdater,
2022-11-01 06:10:08 -07:00
)
2022-05-03 21:20:14 -07:00
export default mangoStore