expose market config in store and switch between spot & perp
This commit is contained in:
parent
4ef75d0b39
commit
8aed0f0840
|
@ -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>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
) : (
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue