expose market config in store and switch between spot & perp

This commit is contained in:
Maximilian Schneider 2021-06-17 01:37:35 -04:00
parent 4ef75d0b39
commit 8aed0f0840
9 changed files with 102 additions and 75 deletions

View File

@ -1,12 +1,12 @@
import { useState } from 'react'
import { useMemo, useState } from 'react'
import { Listbox } from '@headlessui/react'
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'
import { abbreviateAddress, getSymbolForTokenMintAddress } from '../utils'
import useMarketList from '../hooks/useMarketList'
import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
import { abbreviateAddress } from '../utils'
import { nativeToUi } from '@blockworks-foundation/mango-client/lib/src/utils'
import { getTokenByMint } from '@blockworks-foundation/mango-client'
import useMangoStore from '../stores/useMangoStore'
import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
import { RefreshClockwiseIcon } from './icons'
import useMangoGroupConfig from '../hooks/useMangoGroupConfig'
type AccountSelectProps = {
accounts: any[]
@ -23,10 +23,10 @@ const AccountSelect = ({
onSelectAccount,
getBalance,
hideAddress = false,
symbols,
}: AccountSelectProps) => {
const { getTokenIndex } = useMarketList()
const mintDecimals = useMangoStore((s) => s.selectedMangoGroup.mintDecimals)
const groupConfig = useMangoGroupConfig();
const tokenSymbols = useMemo(() => groupConfig.tokens.map(t => t.symbol), [groupConfig]);
const actions = useMangoStore((s) => s.actions)
const [loading, setLoading] = useState(false)
@ -36,11 +36,9 @@ const AccountSelect = ({
}
const getBalanceForAccount = (account) => {
const mintAddress = account?.account.mint.toString()
const symbol = getSymbolForTokenMintAddress(mintAddress)
const balance = nativeToUi(
account?.account?.amount,
symbol !== 'SRM' ? mintDecimals[getTokenIndex(mintAddress)] : SRM_DECIMALS
getTokenByMint(groupConfig, account?.account.mint).decimals
)
return balance.toString()
@ -52,19 +50,15 @@ const AccountSelect = ({
setLoading(false)
}
const symbolsForAccounts = accounts.map((a) =>
getSymbolForTokenMintAddress(a.account.mint.toString())
)
const missingTokens = symbols
? Object.keys(symbols).filter((sym) => !symbolsForAccounts.includes(sym))
: null
const getSymbolOfAccount = (a) => getTokenByMint(groupConfig, a.account.mint)?.symbol;
const symbolsForAccounts = accounts.map(getSymbolOfAccount)
const missingTokens = tokenSymbols.filter((sym) => !symbolsForAccounts.includes(sym))
return (
<div className={`relative inline-block w-full`}>
<div className="flex justify-between pb-2">
<div className="text-th-fgd-1">Asset</div>
{accounts.length < Object.keys(symbols).length ? (
{missingTokens.length > 0 ? (
<button
className="ml-2 text-th-fgd-1 hover:text-th-primary outline-none focus:outline-none"
onClick={handleRefreshBalances}
@ -97,15 +91,11 @@ const AccountSelect = ({
alt=""
width="20"
height="20"
src={`/assets/icons/${getSymbolForTokenMintAddress(
selectedAccount?.account?.mint.toString()
).toLowerCase()}.svg`}
src={`/assets/icons/${getSymbolOfAccount(selectedAccount).toLowerCase()}.svg`}
className={`mr-2`}
/>
<div className="text-left">
{getSymbolForTokenMintAddress(
selectedAccount?.account?.mint.toString()
)}
{getSymbolOfAccount(selectedAccount)}
{!hideAddress ? (
<div className="text-xs text-th-fgd-4">
{abbreviateAddress(selectedAccount?.publicKey)}
@ -133,9 +123,7 @@ const AccountSelect = ({
className={`z-20 p-1 absolute right-0 top-13 bg-th-bkg-1 divide-y divide-th-bkg-3 shadow-lg outline-none rounded-md w-full max-h-60 overflow-auto`}
>
{accounts.map((account) => {
const symbolForAccount = getSymbolForTokenMintAddress(
account?.account?.mint.toString()
)
const symbolForAccount = getSymbolOfAccount(account);
return (
<Listbox.Option
@ -179,8 +167,7 @@ const AccountSelect = ({
</Listbox.Option>
)
})}
{missingTokens && accounts.length !== Object.keys(symbols).length
? missingTokens.map((token) => (
{missingTokens.map((token) => (
<Listbox.Option disabled key={token} value={token}>
<div
className={`opacity-50 p-2 hover:cursor-not-allowed`}
@ -199,7 +186,7 @@ const AccountSelect = ({
</div>
</Listbox.Option>
))
: null}
}
</Listbox.Options>
</>
)}

View File

@ -9,7 +9,9 @@ import ManualRefresh from './ManualRefresh'
const MarketHeader = () => {
// const oraclePrice = useOraclePrice()
const selectedMarketName = useMangoStore((s) => s.selectedMarket.name)
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
const baseSymbol = marketConfig.base_symbol;
const selectedMarketName = marketConfig.name;
const previousMarketName: string = usePrevious(selectedMarketName)
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
const connected = useMangoStore((s) => s.wallet.connected)
@ -76,18 +78,16 @@ const MarketHeader = () => {
alt=""
width="24"
height="24"
src={`/assets/icons/${selectedMarketName
.split('/')[0]
.toLowerCase()}.svg`}
src={`/assets/icons/${baseSymbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div className="font-semibold pr-1.5 text-xl">
{selectedMarketName.split('/')[0]}
{baseSymbol}
</div>
<span className="text-th-fgd-4 text-xl">/</span>
<div className="font-semibold pl-1.5 text-xl">
{selectedMarketName.split('/')[1]}
{selectedMarketName.split(/\/|-/)[1]}
</div>
</div>
</div>
@ -125,7 +125,7 @@ const MarketHeader = () => {
<>
{volume.toFixed(2)}
<span className="ml-1 text-th-fgd-3 text-xs">
{selectedMarketName.split('/')[0]}
{baseSymbol}
</span>
</>
) : (

View File

@ -1,25 +1,22 @@
import useMarketList from '../hooks/useMarketList'
import useMangoGroupConfig from '../hooks/useMangoGroupConfig';
import useMangoStore from '../stores/useMangoStore'
import { getMarketByBaseSymbolAndKind } from '@blockworks-foundation/mango-client'
const MarketSelect = () => {
const { spotMarkets } = useMarketList()
const selectedMarketName = useMangoStore((s) => s.selectedMarket.name)
const selectedMangoGroupMarkets = useMangoStore(
(s) => s.selectedMangoGroup.markets
)
const groupConfig = useMangoGroupConfig();
const selectedMarket = useMangoStore((s) => s.selectedMarket.config)
const setMangoStore = useMangoStore((s) => s.set)
const handleChange = (mktName) => {
const newMarket = Object.entries(selectedMangoGroupMarkets).find(
(m) => m[0] == spotMarkets[mktName]
)[1]
const handleChange = (symbol, kind) => {
const newMarket = getMarketByBaseSymbolAndKind(groupConfig, symbol, kind);
setMangoStore((state) => {
state.selectedMarket.current = newMarket
state.selectedMarket.name = mktName
state.selectedMarket.address = spotMarkets[mktName]
state.selectedMarket.current = null
state.selectedMarket.config = newMarket
})
}
console.log(selectedMarket)
return (
<div className="bg-th-bkg-3 py-2">
<div className="flex justify-between items-center">
@ -27,19 +24,35 @@ const MarketSelect = () => {
<div className="border-r border-th-fgd-4 pr-4 text-th-fgd-4 text-xs">
MARKETS
</div>
{Object.entries(spotMarkets).map(([name, address]) => (
{groupConfig.perp_markets.map((s) => (
<div
className={`border-r border-th-fgd-4 cursor-pointer default-transition flex font-semibold px-4 text-xs hover:text-th-primary
${
selectedMarketName === name
selectedMarket.name === s.name
? `text-th-primary`
: `text-th-fgd-3`
}
`}
onClick={() => handleChange(name)}
key={address as string}
onClick={() => handleChange(s.base_symbol, 'perp')}
key={s.key.toBase58()}
>
{name.split('/')[0]}
{s.name}
</div>
))}
{groupConfig.spot_markets.map((s) => (
<div
className={`border-r border-th-fgd-4 cursor-pointer default-transition flex font-semibold px-4 text-xs hover:text-th-primary
${
selectedMarket.name === s.name
? `text-th-primary`
: `text-th-fgd-3`
}
`}
onClick={() => handleChange(s.base_symbol, 'spot')}
key={s.key.toBase58()}
>
{s.name}
</div>
))}
</div>

View File

@ -1,14 +1,14 @@
import React, { FunctionComponent, useEffect, useMemo, useState } from 'react'
import { ExclamationCircleIcon } from '@heroicons/react/outline'
import { getTokenByMint } from '@blockworks-foundation/mango-client'
import {
nativeToUi,
sleep,
} from '@blockworks-foundation/mango-client/lib/utils'
} from '@blockworks-foundation/mango-client/lib/utils/src'
import Input from './Input'
import AccountSelect from './AccountSelect'
import { ElementTitle } from './styles'
import useMangoStore from '../stores/useMangoStore'
import useMarketList from '../hooks/useMarketList'
import {
getSymbolForTokenMintAddress,
DECIMALS,
@ -16,11 +16,11 @@ import {
} from '../utils/index'
import useConnection from '../hooks/useConnection'
import { initMarginAccountAndDeposit } from '../utils/mango'
import { PublicKey } from '@solana/web3.js'
import Loading from './Loading'
import Button from './Button'
import Slider from './Slider'
import { notify } from '../utils/notifications'
import useMangoGroupConfig from '../hooks/useMangoGroupConfig'
interface NewAccountProps {
onAccountCreation?: (x?) => void
@ -29,22 +29,19 @@ interface NewAccountProps {
const NewAccount: FunctionComponent<NewAccountProps> = ({
onAccountCreation,
}) => {
const groupConfig = useMangoGroupConfig()
const tokenMints = useMemo(() => groupConfig.tokens.map(t => t.mint_key.toBase58()), [groupConfig]);
const [inputAmount, setInputAmount] = useState(0)
const [submitting, setSubmitting] = useState(false)
const [invalidAmountMessage, setInvalidAmountMessage] = useState('')
const [sliderPercentage, setSliderPercentage] = useState(0)
const [maxButtonTransition, setMaxButtonTransition] = useState(false)
const { getTokenIndex, symbols } = useMarketList()
const { connection, programId } = useConnection()
const mintDecimals = useMangoStore((s) => s.selectedMangoGroup.mintDecimals)
const { connection } = useConnection()
const walletAccounts = useMangoStore((s) => s.wallet.balances)
const actions = useMangoStore((s) => s.actions)
const depositAccounts = useMemo(
() =>
walletAccounts.filter((acc) =>
Object.values(symbols).includes(acc.account.mint.toString())
),
[symbols, walletAccounts]
() => walletAccounts.filter((acc) => tokenMints.includes(acc.account.mint.toString())),
[tokenMints, walletAccounts]
)
const [selectedAccount, setSelectedAccount] = useState(depositAccounts[0])
@ -61,10 +58,9 @@ const NewAccount: FunctionComponent<NewAccountProps> = ({
// TODO: remove duplication in AccountSelect
const getBalanceForAccount = (account) => {
const mintAddress = account?.account.mint.toString()
const balance = nativeToUi(
account?.account?.amount,
mintDecimals[getTokenIndex(mintAddress)]
getTokenByMint(groupConfig, account?.account.mint).decimals
)
return balance
@ -85,7 +81,7 @@ const NewAccount: FunctionComponent<NewAccountProps> = ({
initMarginAccountAndDeposit(
connection,
new PublicKey(programId),
groupConfig.merps_program_id,
mangoGroup,
wallet,
selectedAccount.account.mint,
@ -155,8 +151,8 @@ const NewAccount: FunctionComponent<NewAccountProps> = ({
<div className="text-th-fgd-3 text-center pb-4 pt-2">
Make a deposit to initialize a new margin account
</div>
<AccountSelect
symbols={symbols}
accounts={depositAccounts}
selectedAccount={selectedAccount}
onSelectAccount={handleAccountSelect}

View File

@ -13,6 +13,7 @@ const SECONDS = 1000
// const mangoGroupMarketsSelector = (s) => s.selectedMangoGroup.markets
// const websocketConnectionSelector = (s) => s.connection.websocket
const selectedMarketKindSelector = (s) => s.selectedMarket.kind
const selectedMarketAddressSelector = (s) => s.selectedMarket.address
const useHydrateStore = () => {

View File

@ -0,0 +1,18 @@
import { useMemo, useCallback } from 'react'
import useConnection from './useConnection'
import { PublicKey } from '@solana/web3.js'
import useMangoStore from '../stores/useMangoStore'
import { Config } from '@blockworks-foundation/mango-client'
import { GroupConfig } from '@blockworks-foundation/mango-client/lib/src/config'
export default function useMangoGroupConfig(): GroupConfig {
const mangoGroupName = useMangoStore((state) => state.selectedMangoGroup.name)
const { cluster } = useConnection()
const mangoGroupConfig = useMemo(
() => Config.ids().getGroup(cluster, mangoGroupName),
[cluster, mangoGroupName]
)
return mangoGroupConfig;
}

View File

@ -31,7 +31,7 @@
}
},
"dependencies": {
"@blockworks-foundation/mango-client": "https://github.com/blockworks-foundation/merps-ts#ts/ui-exports",
"@blockworks-foundation/mango-client": "git+ssh://git@github.com/blockworks-foundation/merps-ts#ts/ui-exports",
"@emotion/react": "^11.1.5",
"@emotion/styled": "^11.1.5",
"@headlessui/react": "^1.2.0",

View File

@ -3,9 +3,14 @@ import produce from 'immer'
import { Market } from '@project-serum/serum'
import {
IDS,
Config,
MarketKind,
MerpsClient as MangoClient,
MerpsGroup as MangoGroup,
MerpsAccount as MarginAccount,
MarketConfig,
getMarketByBaseSymbolAndKind,
GroupConfig,
} from '@blockworks-foundation/mango-client'
// import { SRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
import { AccountInfo, Connection, PublicKey } from '@solana/web3.js'
@ -37,6 +42,8 @@ const DEFAULT_CONNECTION = new Connection(ENDPOINT.url, 'recent')
const WEBSOCKET_CONNECTION = new Connection(ENDPOINT.websocket, 'recent')
const DEFAULT_MANGO_GROUP_NAME = 'merps_test_v1'
const DEFAULT_MANGO_GROUP_CONFIG = Config.ids().getGroup(CLUSTER, DEFAULT_MANGO_GROUP_NAME)
console.log(DEFAULT_MANGO_GROUP_CONFIG);
const defaultMangoGroupIds = IDS['groups'].find(
(group) => group.name === DEFAULT_MANGO_GROUP_NAME
@ -81,6 +88,7 @@ interface MangoStore extends State {
endpoint: string
}
selectedMarket: {
config: MarketConfig,
name: string
address: string
current: Market | null
@ -91,6 +99,7 @@ interface MangoStore extends State {
mangoClient: MangoClient
mangoGroups: Array<MangoGroup>
selectedMangoGroup: {
config: GroupConfig,
name: string
current: MangoGroup | null
markets: {
@ -140,6 +149,7 @@ const useMangoStore = create<MangoStore>((set, get) => ({
endpoint: ENDPOINT.url,
},
selectedMangoGroup: {
config: DEFAULT_MANGO_GROUP_CONFIG,
name: DEFAULT_MANGO_GROUP_NAME,
current: null,
markets: {},
@ -148,7 +158,9 @@ const useMangoStore = create<MangoStore>((set, get) => ({
rootBanks: [],
},
selectedMarket: {
name: defaultMangoGroupIds.spot_markets[0].base_symbol,
config: getMarketByBaseSymbolAndKind(DEFAULT_MANGO_GROUP_CONFIG, 'BTC', 'spot') as MarketConfig,
kind: 'spot',
name: 'BTC/USDC',
address: defaultMangoGroupIds.spot_markets[0].key,
current: null,
mangoProgramId: null,

View File

@ -993,9 +993,9 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@blockworks-foundation/mango-client@https://github.com/blockworks-foundation/merps-ts#ts/ui-exports":
"@blockworks-foundation/mango-client@git+ssh://git@github.com/blockworks-foundation/merps-ts#ts/ui-exports":
version "0.0.0"
resolved "https://github.com/blockworks-foundation/merps-ts#040842be8256c94cb18dff8a4a51ebb6d27cd49a"
resolved "git+ssh://git@github.com/blockworks-foundation/merps-ts#3d46aee81da15150aef93b036f6f6accbe67e6ec"
dependencies:
"@project-serum/serum" "^0.13.38"
"@project-serum/sol-wallet-adapter" "^0.2.0"