mango-v4-ui/store/state.ts

411 lines
12 KiB
TypeScript
Raw Normal View History

2022-05-03 21:20:14 -07:00
import create from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
import produce from 'immer'
2022-06-30 13:12:36 -07:00
import { AnchorProvider, Wallet, web3 } from '@project-serum/anchor'
2022-05-03 21:20:14 -07:00
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
import {
MangoClient,
Group,
MangoAccount,
Serum3Market,
2022-06-21 03:58:57 -07:00
MANGO_V4_ID,
2022-05-03 21:20:14 -07:00
} from '@blockworks-foundation/mango-v4'
import EmptyWallet from '../utils/wallet'
2022-05-04 14:46:04 -07:00
import { Order } from '@project-serum/serum/lib/market'
2022-08-01 22:32:21 -07:00
import { Notification, notify } from '../utils/notifications'
2022-07-05 20:37:49 -07:00
import {
2022-08-02 17:17:42 -07:00
COINGECKO_IDS,
2022-08-01 22:32:21 -07:00
fetchNftsFromHolaplexIndexer,
2022-07-05 20:37:49 -07:00
getTokenAccountsByOwnerWithWrappedSol,
TokenAccount,
} from '../utils/tokens'
import { Token } from '../types/jupiter'
2022-07-26 21:40:17 -07:00
import { getProfilePicture, ProfilePicture } from '@solflare-wallet/pfp'
import { TOKEN_LIST_URL } from '@jup-ag/core'
2022-05-03 21:20:14 -07:00
2022-08-03 14:46:37 -07:00
const GROUP = new PublicKey('A9XhGqUUjV992cD36qWDY8wDiZnGuCaUWtSE3NGXjDCb')
2022-06-30 13:12:36 -07:00
export const connection = new web3.Connection(
2022-06-29 20:37:25 -07:00
'https://mango.rpcpool.com/946ef7337da3f5b8d3e4a34e7f88',
'processed'
)
2022-06-30 13:12:36 -07:00
const options = AnchorProvider.defaultOptions()
2022-07-05 20:37:49 -07:00
export const CLUSTER = 'mainnet-beta'
export const CLIENT_TX_TIMEOUT = 90000
2022-07-12 19:02:36 -07:00
const DEFAULT_PROVIDER = new AnchorProvider(
2022-05-03 21:20:14 -07:00
connection,
new EmptyWallet(Keypair.generate()),
options
)
2022-07-12 19:02:36 -07:00
DEFAULT_PROVIDER.opts.skipPreflight = true
const DEFAULT_CLIENT = MangoClient.connect(
DEFAULT_PROVIDER,
CLUSTER,
MANGO_V4_ID[CLUSTER]
)
2022-05-03 21:20:14 -07:00
2022-08-01 22:32:21 -07:00
interface NFT {
address: string
image: string
}
2022-05-03 21:20:14 -07:00
export type MangoStore = {
2022-08-02 17:17:42 -07:00
coingeckoPrices: {
data: any[]
loading: boolean
}
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
client: MangoClient
jupiterTokens: Token[]
2022-07-27 23:35:18 -07:00
mangoAccount: {
current: MangoAccount | undefined
loading: boolean
}
2022-08-01 18:08:08 -07:00
mangoAccounts: MangoAccount[]
2022-05-03 21:20:14 -07:00
markets: Serum3Market[] | undefined
2022-07-05 20:37:49 -07:00
notificationIdCounter: number
notifications: Array<Notification>
2022-05-03 21:20:14 -07:00
serumOrders: Order[] | undefined
2022-07-25 22:27:53 -07:00
swap: {
inputToken: string
outputToken: string
2022-08-02 11:04:00 -07:00
inputTokenInfo: Token | undefined
outputTokenInfo: Token | undefined
2022-07-25 22:27:53 -07:00
}
2022-05-03 21:20:14 -07:00
set: (x: (x: MangoStore) => void) => void
2022-07-05 20:37:49 -07:00
wallet: {
2022-07-26 21:40:17 -07:00
loadProfilePic: boolean
profilePic: ProfilePicture | undefined
2022-07-05 20:37:49 -07:00
tokens: TokenAccount[]
2022-08-01 22:32:21 -07:00
nfts: {
data: NFT[] | []
loading: boolean
}
2022-07-05 20:37:49 -07:00
}
2022-05-03 21:20:14 -07:00
actions: {
2022-08-02 17:17:42 -07:00
fetchCoingeckoPrices: () => Promise<void>
2022-05-03 21:20:14 -07:00
fetchGroup: () => Promise<void>
fetchMangoAccount: (wallet: Wallet) => Promise<void>
2022-08-01 18:08:08 -07:00
fetchMangoAccounts: (wallet: Wallet) => Promise<void>
2022-08-01 22:32:21 -07:00
fetchNfts: (connection: Connection, walletPk: PublicKey) => void
2022-07-26 21:40:17 -07:00
fetchProfilePicture: (wallet: Wallet) => void
fetchJupiterTokens: () => Promise<void>
fetchWalletTokens: (wallet: Wallet) => Promise<void>
reloadAccount: () => Promise<void>
reloadGroup: () => Promise<void>
loadSerumMarket: () => Promise<void>
2022-05-03 21:20:14 -07:00
}
}
const mangoStore = create<MangoStore>(
subscribeWithSelector((set, get) => {
return {
2022-08-02 17:17:42 -07:00
coingeckoPrices: {
data: [],
loading: false,
},
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,
2022-07-12 19:02:36 -07:00
client: DEFAULT_CLIENT,
2022-07-05 20:37:49 -07:00
jupiterTokens: [],
2022-07-27 23:35:18 -07:00
mangoAccount: {
current: undefined,
loading: false,
},
2022-08-01 18:08:08 -07:00
mangoAccounts: [],
2022-05-03 21:20:14 -07:00
markets: undefined,
2022-07-05 20:37:49 -07:00
notificationIdCounter: 0,
notifications: [],
2022-05-03 21:20:14 -07:00
serumOrders: undefined,
set: (fn) => set(produce(fn)),
2022-07-25 22:27:53 -07:00
swap: {
inputToken: 'SOL',
outputToken: 'USDC',
inputTokenInfo: undefined,
outputTokenInfo: undefined,
},
2022-07-05 20:37:49 -07:00
wallet: {
2022-07-26 21:40:17 -07:00
loadProfilePic: true,
profilePic: undefined,
2022-07-05 20:37:49 -07:00
tokens: [],
2022-08-01 22:32:21 -07:00
nfts: {
data: [],
loading: false,
},
2022-07-05 20:37:49 -07:00
},
2022-05-03 21:20:14 -07:00
actions: {
2022-08-02 17:17:42 -07:00
fetchCoingeckoPrices: async () => {
const set = get().set
set((state) => {
state.coingeckoPrices.loading = true
})
try {
const promises: any = []
for (const asset of COINGECKO_IDS) {
promises.push(
fetch(
`https://api.coingecko.com/api/v3/coins/${asset.id}/market_chart?vs_currency=usd&days=1`
).then((res) => res.json())
)
}
const data = await Promise.all(promises)
for (let i = 0; i < data.length; i++) {
data[i].symbol = COINGECKO_IDS[i].symbol
}
set((state) => {
state.coingeckoPrices.data = data
state.coingeckoPrices.loading = false
})
} catch (e) {
console.log('ERORR: Unable to load Coingecko prices')
set((state) => {
state.coingeckoPrices.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)
2022-06-22 04:55:03 -07:00
const markets = await client.serum3GetMarkets(
group,
group.banksMap.get('BTC')?.tokenIndex,
group.banksMap.get('USDC')?.tokenIndex
)
2022-05-03 21:20:14 -07:00
set((state) => {
state.group = group
2022-06-22 04:55:03 -07:00
state.markets = markets
2022-05-03 21:20:14 -07:00
})
} catch (e) {
console.error('Error fetching group', e)
}
},
fetchMangoAccount: async (wallet) => {
2022-08-03 14:46:37 -07:00
const set = get().set
2022-05-03 21:20:14 -07:00
try {
const group = get().group
if (!group) throw new Error('Group not loaded')
2022-05-03 21:20:14 -07:00
const provider = new AnchorProvider(connection, wallet, options)
2022-07-05 20:37:49 -07:00
provider.opts.skipPreflight = true
2022-06-21 03:58:57 -07:00
const client = await MangoClient.connect(
provider,
2022-07-05 20:37:49 -07:00
CLUSTER,
MANGO_V4_ID[CLUSTER]
2022-06-21 03:58:57 -07:00
)
2022-05-03 21:20:14 -07:00
2022-07-27 23:35:18 -07:00
set((state) => {
state.mangoAccount.loading = true
})
2022-05-03 21:20:14 -07:00
const mangoAccount = await client.getOrCreateMangoAccount(
group,
wallet.publicKey,
0,
2022-07-28 03:51:36 -07:00
'Account'
2022-05-03 21:20:14 -07:00
)
// let orders = await client.getSerum3Orders(
// group,
2022-06-21 03:58:57 -07:00
// SERUM3_PROGRAM_ID['devnet'],
// 'BTC/USDC'
// )
2022-05-03 21:20:14 -07:00
2022-07-12 19:02:36 -07:00
await mangoAccount.reloadAccountData(client, group)
2022-05-03 21:20:14 -07:00
set((state) => {
state.client = client
2022-07-27 23:35:18 -07:00
state.mangoAccount.current = mangoAccount
state.mangoAccount.loading = false
2022-07-05 20:37:49 -07:00
state.connected = true
// state.serumOrders = orders
2022-05-03 21:20:14 -07:00
})
} catch (e) {
2022-07-27 23:35:18 -07:00
set((state) => {
state.mangoAccount.loading = false
})
2022-05-03 21:20:14 -07:00
console.error('Error fetching mango acct', e)
}
},
2022-08-01 18:08:08 -07:00
fetchMangoAccounts: async (wallet) => {
try {
const set = get().set
const group = get().group
const client = get().client
if (!group) throw new Error('Group not loaded')
if (!client) throw new Error('Client not loaded')
2022-08-04 09:42:03 -07:00
const mangoAccounts = await client.getMangoAccountsForOwner(
2022-08-01 18:08:08 -07:00
group,
wallet.publicKey
)
if (mangoAccounts.length) {
set((state) => {
state.mangoAccounts = mangoAccounts
})
}
} catch (e) {
console.error('Error fetching mango accts', e)
}
},
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 {
const data = await fetchNftsFromHolaplexIndexer(ownerPk)
// for (const nft of data.nfts) {
// const tokenAccount = await getTokenAccountsByMint(
// connection,
// nft.mintAddress
// )
// nft.tokenAccount = tokenAccount[0] || null
// }
set((state) => {
state.wallet.nfts.data = data.nfts
state.wallet.nfts.loading = false
})
} catch (error) {
notify({
type: 'error',
title: 'Unable to fetch nfts',
})
}
return []
},
2022-07-05 20:37:49 -07:00
fetchWalletTokens: async (wallet: Wallet) => {
const set = get().set
const connection = get().connection
if (wallet.publicKey) {
const token = await getTokenAccountsByOwnerWithWrappedSol(
connection,
wallet.publicKey
)
set((state) => {
state.wallet.tokens = token
})
} else {
set((state) => {
state.wallet.tokens = []
})
}
},
fetchJupiterTokens: async () => {
const set = mangoStore.getState().set
2022-08-02 11:04:00 -07:00
const group = mangoStore.getState().group
if (!group) {
console.error(
'Mango group unavailable; Loading jupiter tokens failed'
)
return
}
const banks = Array.from(group.banksMap.keys())
fetch(TOKEN_LIST_URL[CLUSTER])
.then((response) => response.json())
.then((result) => {
2022-08-02 11:04:00 -07:00
const groupTokens = result.filter((t: any) =>
banks.includes(t.symbol)
)
const inputTokenInfo = groupTokens.find(
(t: any) => t.symbol === 'SOL'
)
const outputTokenInfo = groupTokens.find(
(t: any) => t.symbol === 'USDC'
)
set((s) => {
s.swap.inputTokenInfo = inputTokenInfo
s.swap.outputTokenInfo = outputTokenInfo
2022-08-02 11:04:00 -07:00
s.jupiterTokens = groupTokens
})
})
},
reloadGroup: async () => {
try {
2022-06-21 03:58:57 -07:00
const set = get().set
const client = get().client
2022-08-03 14:46:37 -07:00
const group = await client.getGroup(GROUP)
set((state) => {
state.group = group
})
} catch (e) {
console.error('Error fetching group', e)
}
},
2022-05-03 21:20:14 -07:00
reloadAccount: async () => {
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-07-27 23:35:18 -07:00
const mangoAccount = get().mangoAccount.current
const group = get().group
2022-08-02 11:04:00 -07:00
const actions = get().actions
2022-05-03 21:20:14 -07:00
if (!mangoAccount || !group) return
2022-05-03 21:20:14 -07:00
try {
const newMangoAccount = await client.getMangoAccount(mangoAccount)
await newMangoAccount.reloadAccountData(client, group)
2022-05-03 21:20:14 -07:00
set((state) => {
2022-07-27 23:35:18 -07:00
state.mangoAccount.current = newMangoAccount
2022-05-03 21:20:14 -07:00
})
} catch {
console.error('Error reloading mango account')
}
},
loadSerumMarket: async () => {
2022-06-21 03:58:57 -07:00
const set = get().set
2022-05-03 21:20:14 -07:00
const client = get().client
const group = get().group
if (!group) return
2022-06-21 03:58:57 -07:00
const markets = await client.serum3GetMarkets(
2022-05-03 21:20:14 -07:00
group,
group.banksMap.get('BTC')?.tokenIndex,
group.banksMap.get('USDC')?.tokenIndex
)
2022-06-21 03:58:57 -07:00
let orders = await client.getSerum3Orders(group, 'BTC/USDC')
2022-05-03 21:20:14 -07:00
set((state) => {
state.markets = markets
state.serumOrders = orders
})
},
2022-07-26 21:40:17 -07:00
async fetchProfilePicture(wallet: Wallet) {
const set = get().set
const walletPk = wallet?.publicKey
const connection = get().connection
if (!walletPk) return
try {
const result = await getProfilePicture(connection, walletPk)
set((state) => {
state.wallet.profilePic = result
state.wallet.loadProfilePic = false
})
} catch (e) {
console.log('Could not get profile picture', e)
set((state) => {
state.wallet.loadProfilePic = false
})
}
},
2022-05-03 21:20:14 -07:00
},
}
})
)
export default mangoStore