mango-ui-v2/stores/useMangoStore.tsx

367 lines
11 KiB
TypeScript
Raw Normal View History

2021-04-02 11:26:21 -07:00
import create, { State } from 'zustand'
import produce from 'immer'
import { Market } from '@project-serum/serum'
import {
2021-04-06 15:11:42 -07:00
IDS,
2021-04-02 11:26:21 -07:00
MangoClient,
MangoGroup,
MarginAccount,
2021-04-12 20:39:08 -07:00
nativeToUi,
2021-04-02 11:26:21 -07:00
} from '@blockworks-foundation/mango-client'
2021-04-12 20:39:08 -07:00
import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
2021-04-09 17:01:00 -07:00
import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'
2021-04-13 22:23:50 -07:00
import { EndpointInfo, WalletAdapter } from '../@types/types'
2021-04-09 17:01:00 -07:00
import { getOwnedTokenAccounts } from '../utils/tokens'
2021-04-14 15:46:36 -07:00
import { isDefined } from '../utils/index'
2021-04-20 07:09:25 -07:00
import { notify } from '../utils/notifications'
export const ENDPOINTS: EndpointInfo[] = [
{
name: 'mainnet-beta',
url: 'https://mango.rpcpool.com/',
websocket: 'https://mango.rpcpool.com/',
custom: false,
},
{
name: 'devnet',
url: 'https://devnet.solana.com',
websocket: 'https://devnet.solana.com',
custom: false,
},
]
2021-04-25 09:22:28 -07:00
type ClusterType = 'mainnet-beta' | 'devnet'
const CLUSTER =
(process.env.NEXT_PUBLIC_CLUSTER as ClusterType) || 'mainnet-beta'
const ENDPOINT = ENDPOINTS.find((e) => e.name === CLUSTER)
const DEFAULT_CONNECTION = new Connection(ENDPOINT.url, 'recent')
const WEBSOCKET_CONNECTION = new Connection(ENDPOINT.websocket, 'recent')
const DEFAULT_MANGO_GROUP_NAME = 'BTC_ETH_SOL_SRM_USDT'
2021-04-13 16:41:04 -07:00
export const INITIAL_STATE = {
WALLET: {
2021-04-13 22:23:50 -07:00
providerUrl: null,
2021-04-13 16:41:04 -07:00
connected: false,
current: null,
balances: [],
srmAccountsForOwner: [],
contributedSrm: 0,
},
}
2021-04-10 14:12:15 -07:00
// an object with keys of Solana account addresses that we are
// subscribing to with connection.onAccountChange() in the
// useHydrateStore hook
interface AccountInfoList {
[key: string]: AccountInfo<Buffer>
}
2021-04-02 11:26:21 -07:00
interface MangoStore extends State {
2021-04-11 21:17:23 -07:00
notifications: Array<{
type: string
message: string
description?: string
txid?: string
}>
accountInfos: AccountInfoList
connection: {
2021-04-25 09:22:28 -07:00
cluster: ClusterType
current: Connection
websocket: Connection
endpoint: string
2021-04-12 20:39:08 -07:00
srmMint: string
}
2021-04-29 07:38:28 -07:00
selectedMarket: {
name: string
address: string
2021-04-02 11:26:21 -07:00
current: Market | null
2021-04-06 15:11:42 -07:00
mangoProgramId: number | null
2021-04-02 11:26:21 -07:00
markPrice: number
orderBook: any[]
}
mangoClient: MangoClient
mangoGroups: Array<MangoGroup>
selectedMangoGroup: {
name: string
current: MangoGroup | null
srmAccount: AccountInfo<Buffer> | null
2021-04-07 08:44:22 -07:00
markets: {
2021-04-29 08:08:36 -07:00
[address: string]: Market
2021-04-07 08:44:22 -07:00
}
2021-04-09 17:01:00 -07:00
mintDecimals: number[]
prices: number[]
}
2021-04-24 19:10:28 -07:00
marginAccounts: MarginAccount[]
selectedMarginAccount: {
current: MarginAccount | null
initialLoad: boolean
}
2021-04-02 11:26:21 -07:00
tradeForm: {
side: 'buy' | 'sell'
price: number | ''
baseSize: number | ''
quoteSize: number | ''
tradeType: 'Market' | 'Limit'
2021-04-02 11:26:21 -07:00
}
wallet: {
2021-04-13 22:23:50 -07:00
providerUrl: string
connected: boolean
2021-04-13 22:23:50 -07:00
current: WalletAdapter | undefined
2021-04-09 17:01:00 -07:00
balances: Array<{ account: any; publicKey: PublicKey }>
2021-04-13 16:41:04 -07:00
srmAccountsForOwner: any[]
contributedSrm: number
}
settings: {
uiLocked: boolean
}
2021-04-14 15:46:36 -07:00
tradeHistory: any[]
2021-04-02 11:26:21 -07:00
set: (x: any) => void
2021-04-25 09:22:28 -07:00
actions: {
[key: string]: () => void
}
2021-04-02 11:26:21 -07:00
}
2021-04-12 20:39:08 -07:00
const useMangoStore = create<MangoStore>((set, get) => ({
notifications: [],
accountInfos: {},
connection: {
cluster: CLUSTER,
current: DEFAULT_CONNECTION,
websocket: WEBSOCKET_CONNECTION,
endpoint: ENDPOINT.url,
2021-04-12 20:39:08 -07:00
srmMint: IDS[CLUSTER].symbols['SRM'],
},
selectedMangoGroup: {
name: DEFAULT_MANGO_GROUP_NAME,
current: null,
markets: {},
srmAccount: null,
mintDecimals: [],
prices: [],
2021-04-12 20:39:08 -07:00
},
selectedMarket: {
name: Object.entries(
IDS[CLUSTER].mango_groups[DEFAULT_MANGO_GROUP_NAME].spot_market_symbols
)[0][0],
address: Object.entries(
IDS[CLUSTER].mango_groups[DEFAULT_MANGO_GROUP_NAME].spot_market_symbols
)[0][1],
current: null,
mangoProgramId: null,
markPrice: 0,
orderBook: [],
},
mangoClient: new MangoClient(),
mangoGroups: [],
marginAccounts: [],
selectedMarginAccount: {
current: null,
initialLoad: false,
2021-04-12 20:39:08 -07:00
},
tradeForm: {
side: 'buy',
baseSize: '',
quoteSize: '',
tradeType: 'Limit',
price: '',
},
2021-04-13 16:41:04 -07:00
wallet: INITIAL_STATE.WALLET,
settings: {
uiLocked: true,
},
2021-04-14 15:46:36 -07:00
tradeHistory: [],
2021-04-12 20:39:08 -07:00
set: (fn) => set(produce(fn)),
actions: {
async fetchWalletBalances() {
const connection = get().connection.current
const wallet = get().wallet.current
const connected = get().wallet.connected
const set = get().set
2021-04-14 15:46:36 -07:00
if (wallet?.publicKey && connected) {
2021-04-12 20:39:08 -07:00
const ownerAddress = wallet.publicKey
const ownedTokenAccounts = await getOwnedTokenAccounts(
connection,
ownerAddress
)
set((state) => {
state.wallet.balances = ownedTokenAccounts
})
} else {
set((state) => {
state.wallet.balances = []
})
}
},
2021-04-12 20:39:08 -07:00
async fetchMangoSrmAccounts() {
const connection = get().connection.current
const wallet = get().wallet.current
const connected = get().wallet.connected
const selectedMangoGroup = get().selectedMangoGroup.current
const cluster = get().connection.cluster
const mangoClient = get().mangoClient
const set = get().set
2021-04-14 15:46:36 -07:00
if (wallet?.publicKey && connected) {
const usersMangoSrmAccounts =
await mangoClient.getMangoSrmAccountsForOwner(
connection,
new PublicKey(IDS[cluster].mango_program_id),
selectedMangoGroup,
wallet
)
2021-04-12 20:39:08 -07:00
if (usersMangoSrmAccounts.length) {
set((state) => {
2021-04-13 17:41:28 -07:00
state.wallet.srmAccountsForOwner = usersMangoSrmAccounts
2021-04-12 20:39:08 -07:00
const totalSrmDeposits = usersMangoSrmAccounts.reduce(
(prev, cur) => prev + nativeToUi(cur.amount, SRM_DECIMALS),
2021-04-12 20:39:08 -07:00
0
)
state.wallet.contributedSrm = totalSrmDeposits
2021-04-12 20:39:08 -07:00
})
}
}
2021-04-02 11:26:21 -07:00
},
2021-04-14 15:46:36 -07:00
async fetchMarginAccounts() {
2021-04-12 20:39:08 -07:00
const connection = get().connection.current
const mangoGroup = get().selectedMangoGroup.current
const selectedMarginAcount = get().selectedMarginAccount.current
2021-04-12 20:39:08 -07:00
const wallet = get().wallet.current
const cluster = get().connection.cluster
const mangoClient = get().mangoClient
const programId = IDS[cluster].mango_program_id
const set = get().set
2021-04-14 15:46:36 -07:00
if (!wallet?.publicKey || !wallet.publicKey) return
2021-04-12 20:39:08 -07:00
if (!selectedMarginAcount) {
set((state) => {
state.selectedMarginAccount.initialLoad = true
})
}
return mangoClient
2021-04-12 20:39:08 -07:00
.getMarginAccountsForOwner(
connection,
new PublicKey(programId),
mangoGroup,
wallet
)
.then((marginAccounts) => {
if (marginAccounts.length > 0) {
set((state) => {
2021-04-14 15:46:36 -07:00
state.marginAccounts = marginAccounts
2021-04-25 22:47:45 -07:00
if (state.selectedMarginAccount.current) {
state.selectedMarginAccount.current = marginAccounts.find(
(ma) =>
ma.publicKey.equals(
state.selectedMarginAccount.current.publicKey
)
)
} else {
state.selectedMarginAccount.current = marginAccounts[0]
}
2021-04-12 20:39:08 -07:00
})
}
set((state) => {
state.selectedMarginAccount.initialLoad = false
})
2021-04-12 20:39:08 -07:00
})
.catch((err) => {
console.error('Could not get margin accounts for wallet', err)
2021-04-12 20:39:08 -07:00
})
},
2021-04-24 19:10:28 -07:00
async fetchAllMangoGroups() {
const connection = get().connection.current
const cluster = get().connection.cluster
const mangoClient = get().mangoClient
const set = get().set
const mangoGroups = Object.keys(IDS[cluster].mango_groups)
const allMangoGroups = await Promise.all(
mangoGroups.map((mangoGroupName) => {
const mangoGroupIds = IDS[cluster].mango_groups[mangoGroupName]
const mangoGroupPk = new PublicKey(mangoGroupIds.mango_group_pk)
const srmVaultPk = new PublicKey(mangoGroupIds.srm_vault_pk)
return mangoClient.getMangoGroup(connection, mangoGroupPk, srmVaultPk)
})
)
set((state) => {
state.mangoGroups = allMangoGroups
})
},
2021-04-12 20:39:08 -07:00
async fetchMangoGroup() {
const connection = get().connection.current
const mangoGroupName = get().selectedMangoGroup.name
const cluster = get().connection.cluster
const mangoClient = get().mangoClient
const set = get().set
const mangoGroupIds = IDS[cluster].mango_groups[mangoGroupName]
if (!mangoClient) return
const mangoGroupPk = new PublicKey(mangoGroupIds.mango_group_pk)
const srmVaultPk = new PublicKey(mangoGroupIds.srm_vault_pk)
return mangoClient
2021-04-12 20:39:08 -07:00
.getMangoGroup(connection, mangoGroupPk, srmVaultPk)
.then(async (mangoGroup) => {
const srmAccountInfoPromise = connection.getAccountInfo(
2021-04-12 20:39:08 -07:00
mangoGroup.srmVault
2021-04-09 17:01:00 -07:00
)
const pricesPromise = mangoGroup.getPrices(connection)
const [srmAccountInfo, prices] = await Promise.all([
srmAccountInfoPromise,
pricesPromise,
])
2021-04-12 20:39:08 -07:00
// Set the mango group
2021-04-09 17:01:00 -07:00
set((state) => {
2021-04-12 20:39:08 -07:00
state.selectedMangoGroup.current = mangoGroup
state.selectedMangoGroup.srmAccount = srmAccountInfo
state.selectedMangoGroup.mintDecimals = mangoGroup.mintDecimals
state.selectedMangoGroup.prices = prices
2021-04-09 17:01:00 -07:00
})
2021-04-12 20:39:08 -07:00
})
.catch((err) => {
2021-04-20 07:09:25 -07:00
notify({
message: 'Could not get mango group: ',
description: `${err}`,
type: 'error',
})
console.log('Could not get mango group: ', err)
2021-04-12 20:39:08 -07:00
})
2021-04-09 17:01:00 -07:00
},
2021-04-15 15:45:26 -07:00
async fetchTradeHistory(marginAccount = null) {
const selectedMarginAccount =
marginAccount || get().selectedMarginAccount.current
2021-04-14 15:46:36 -07:00
const set = get().set
if (!selectedMarginAccount) return
if (selectedMarginAccount.openOrdersAccounts.length === 0) return
const openOrdersAccounts =
selectedMarginAccount.openOrdersAccounts.filter(isDefined)
2021-04-14 15:46:36 -07:00
const publicKeys = openOrdersAccounts.map((act) =>
act.publicKey.toString()
)
const results = await Promise.all(
publicKeys.map(async (pk) => {
const response = await fetch(
`https://stark-fjord-45757.herokuapp.com/trades/open_orders/${pk.toString()}`
)
const parsedResponse = await response.json()
return parsedResponse?.data ? parsedResponse.data : []
})
)
set((state) => {
state.tradeHistory = results
})
},
2021-04-12 20:39:08 -07:00
},
}))
2021-04-02 11:26:21 -07:00
export default useMangoStore