wip: wiring up global state to components

This commit is contained in:
Tyler Shipe 2021-04-05 10:32:11 -04:00
parent f03f1919a4
commit 82b091434e
16 changed files with 542 additions and 208 deletions

184
components/MarginInfo.tsx Normal file
View File

@ -0,0 +1,184 @@
import { Row, Popover, Typography } from 'antd'
import React, { useEffect, useState } from 'react'
import xw from 'xwind'
import { nativeToUi } from '@blockworks-foundation/mango-client/lib/utils'
import { groupBy } from '../utils'
import useTradeHistory from '../hooks/useTradeHistory'
import useConnection from '../hooks/useConnection'
import FloatingElement from './FloatingElement'
import useMarginAccount from '../hooks/useMarginAcccount'
const { Text } = Typography
const calculatePNL = (tradeHistory, prices, mangoGroup) => {
if (!tradeHistory.length) return '0.00'
const profitAndLoss = {}
const groupedTrades = groupBy(tradeHistory, (trade) => trade.marketName)
if (!prices.length) return '-'
const assetIndex = {
'BTC/USDT': 0,
'BTC/WUSDT': 0,
'ETH/USDT': 1,
'ETH/WUSDT': 1,
USDT: 2,
WUSDT: 2,
}
groupedTrades.forEach((val, key) => {
profitAndLoss[key] = val.reduce(
(acc, current) =>
(current.side === 'sell' ? current.size * -1 : current.size) + acc,
0
)
})
const totalNativeUsdt = tradeHistory.reduce((acc, current) => {
const usdtAmount =
current.side === 'sell'
? parseInt(current.nativeQuantityReleased)
: parseInt(current.nativeQuantityPaid) * -1
return usdtAmount + acc
}, 0)
profitAndLoss['USDT'] = nativeToUi(
totalNativeUsdt,
mangoGroup.mintDecimals[2]
)
let total = 0
for (const assetName in profitAndLoss) {
total = total + profitAndLoss[assetName] * prices[assetIndex[assetName]]
}
return total.toFixed(2)
}
export default function MarginInfo() {
// Connection hook
const { connection } = useConnection()
// Wallet hook
// Get our account info
const { marginAccount, mangoGroup } = useMarginAccount()
// Working state
// Hold the margin account info
const [mAccountInfo, setMAccountInfo] = useState<
| {
label: string
value: string
unit: string
desc: string
currency: string
}[]
| null
>(null)
const { tradeHistory } = useTradeHistory()
// Settle bororows
useEffect(() => {
console.log('HERE mangoGroup: ', mangoGroup)
if (mangoGroup) {
mangoGroup.getPrices(connection).then((prices) => {
const collateralRatio = marginAccount
? marginAccount.getCollateralRatio(mangoGroup, prices)
: 200
const accountEquity = marginAccount
? marginAccount.computeValue(mangoGroup, prices)
: 0
let leverage
if (marginAccount) {
leverage = accountEquity
? (
1 /
(marginAccount.getCollateralRatio(mangoGroup, prices) - 1)
).toFixed(2)
: '∞'
} else {
leverage = '0'
}
setMAccountInfo([
{
label: 'Equity',
value: accountEquity.toFixed(2),
unit: '',
currency: '$',
desc: 'The value of the account',
},
{
label: 'Leverage',
value: leverage,
unit: 'x',
currency: '',
desc: 'Total position size divided by account value',
},
{
label: 'Total PNL',
value: calculatePNL(tradeHistory, prices, mangoGroup),
unit: '',
currency: '$',
desc:
'Total PNL reflects trades placed after March 15th 2021 04:00 AM UTC. Visit the Learn link in the top menu for more information.',
},
{
// TODO: Get collaterization ratio
label: 'Collateral Ratio',
value:
collateralRatio > 2 ? '>200' : (100 * collateralRatio).toFixed(0),
unit: '%',
currency: '',
desc: 'The current collateral ratio',
},
{
label: 'Maint. Collateral Ratio',
value: (mangoGroup.maintCollRatio * 100).toFixed(0),
unit: '%',
currency: '',
desc:
'The collateral ratio you must maintain to not get liquidated',
},
{
label: 'Initial Collateral Ratio',
value: (mangoGroup.initCollRatio * 100).toFixed(0),
currency: '',
unit: '%',
desc: 'The collateral ratio required to open a new margin position',
},
])
})
}
}, [marginAccount, mangoGroup])
return (
<FloatingElement>
<React.Fragment>
{mAccountInfo ? (
mAccountInfo.map((entry, i) => (
<Row key={i} justify="space-between" style={{ padding: '4px' }}>
<Popover content={entry.desc} placement="topLeft" trigger="hover">
<div>
<Text ellipsis={true} style={{ cursor: 'help' }}>
{entry.label}
</Text>
</div>
</Popover>
<div>
<Text strong>
{entry.currency + entry.value}
{entry.unit}
</Text>
</div>
</Row>
))
) : (
<div css={xw`flex align-middle justify-center`}>
{/* <BalanceCol></BalanceCol> */}
Connect Wallet
</div>
)}
</React.Fragment>
</FloatingElement>
)
}

View File

@ -9,10 +9,6 @@ import useMarkPrice from '../hooks/useMarkPrice'
import useOrderbook from '../hooks/useOrderbook'
import useMarkets from '../hooks/useMarkets'
const Title = styled.div`
color: rgba(255, 255, 255, 1);
`
const SizeTitle = styled.div`
padding: 4px 0 4px 0px;
color: #434a59;
@ -29,7 +25,7 @@ const Line = styled.div<any>`
const Price = styled.div<any>`
position: absolute;
${(props) => (props.invert ? `left: 5px;` : `right: 5px;`)}
${(props) => (props.invert ? `left: 5px;` : `right: 15px;`)}
${(props) => props['data-color'] && `color: ${props['data-color']};`}
`
@ -98,9 +94,7 @@ export default function Orderbook({ depth = 7 }) {
return (
<>
<div>
<Title>Orderbook</Title>
</div>
<div css={xw`flex justify-center pb-1 text-lg font-light`}>Orderbook</div>
{smallScreen ? (
<>
<MarkPriceComponent markPrice={markPrice} />
@ -160,8 +154,8 @@ export default function Orderbook({ depth = 7 }) {
) : (
<>
<SizeTitle>
<div style={{ textAlign: 'left' }}>Size ({baseCurrency})</div>
<div style={{ textAlign: 'right' }}>Price ({quoteCurrency})</div>
<div css={xw`text-left`}>Size ({baseCurrency})</div>
<div css={xw`text-right`}>Price ({quoteCurrency})</div>
</SizeTitle>
{orderbookData?.asks.map(({ price, size, sizePercent }) => (
<OrderbookRow

View File

@ -1,11 +1,16 @@
import { useState } from 'react'
import xw from 'xwind'
import styled from '@emotion/styled'
import MenuItem from './MenuItem'
import useWallet from '../hooks/useWallet'
const TopBar = () => {
console.log('load topbar')
const Code = styled.code`
border: 1px solid hsla(0, 0%, 39.2%, 0.2);
border-radius: 3px;
background: hsla(0, 0%, 58.8%, 0.1);
`
const TopBar = () => {
const { connected, wallet } = useWallet()
const [showMenu, setShowMenu] = useState(false)
@ -38,9 +43,20 @@ const TopBar = () => {
onClick={handleConnectDisconnect}
css={xw`bg-mango-dark-light hover:bg-mango-dark-lighter text-mango-yellow rounded-md px-4 py-2 focus:outline-none`}
>
<span css={xw`text-lg font-light`}>
{connected ? 'Disconnect' : 'Connect Wallet'}
</span>
<div css={xw`text-base font-light`}>
{connected ? (
<div onClick={wallet.disconnect}>
<span>Disconnect: </span>
<Code css={xw`text-xs p-1 text-white font-extralight`}>
{wallet.publicKey.toString().substr(0, 4) +
'...' +
wallet.publicKey.toString().substr(-4)}
</Code>
</div>
) : (
'Connect Wallet'
)}
</div>
</button>
</div>
</div>

View File

@ -1,10 +1,9 @@
import { useState, useEffect, useRef } from 'react'
import { Button, Input, Radio, Switch, Select } from 'antd'
import { Input, Radio, Switch, Select } from 'antd'
import xw from 'xwind'
import styled from '@emotion/styled'
import useMarkets from '../hooks/useMarkets'
import useWallet from '../hooks/useWallet'
import useMarkPrice from '../hooks/useMarkPrice'
import useOrderbook from '../hooks/useOrderbook'
import useIpAddress from '../hooks/useIpAddress'
import useConnection from '../hooks/useConnection'
import { PublicKey } from '@solana/web3.js'
@ -17,13 +16,13 @@ import { roundToDecimal } from '../utils/index'
import useMarginAccount from '../hooks/useMarginAcccount'
import useMangoStore from '../stores/useMangoStore'
const SellButton = styled(Button)`
const SellButton = styled.button`
margin: 20px 0px 0px 0px;
background: #e54033;
border-color: #e54033;
`
const BuyButton = styled(Button)`
const BuyButton = styled.button`
margin: 20px 0px 0px 0px;
color: #141026;
background: #9bd104;
@ -37,14 +36,16 @@ export default function TradeForm({
ref: ({ size, price }: { size?: number; price?: number }) => void
) => void
}) {
console.log('reloading trade form')
const [side, setSide] = useState<'buy' | 'sell'>('buy')
const { baseCurrency, quoteCurrency, market } = useMarkets()
const address = market?.publicKey
const { wallet, connected } = useWallet()
const { connection, cluster } = useConnection()
const { marginAccount, mangoGroup, tradeForm } = useMarginAccount()
console.log('margin accoun hook', { marginAccount, mangoGroup, tradeForm })
const { marginAccount, mangoGroup } = useMarginAccount()
const tradeForm = useMangoStore((s) => s.tradeForm)
const orderBookRef = useRef(useMangoStore.getState().market.orderBook)
const orderbook = orderBookRef.current[0]
@ -203,6 +204,8 @@ export default function TradeForm({
connection,
new PublicKey(IDS[cluster].mango_program_id),
mangoGroup,
// TODO:
// @ts-ignore
marginAccount,
market,
wallet,
@ -360,45 +363,35 @@ export default function TradeForm({
</div>
) : null}
</div>
{ipAllowed ? (
side === 'buy' ? (
<BuyButton
disabled={
(!price && tradeType === 'Limit') || !baseSize || !connected
}
onClick={onSubmit}
block
type="primary"
size="large"
loading={submitting}
>
{connected ? `Buy ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
</BuyButton>
<div css={xw`flex`}>
{ipAllowed ? (
side === 'buy' ? (
<BuyButton
disabled={
(!price && tradeType === 'Limit') || !baseSize || !connected
}
onClick={onSubmit}
loading={submitting}
>
{connected ? `Buy ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
</BuyButton>
) : (
<SellButton
disabled={
(!price && tradeType === 'Limit') || !baseSize || !connected
}
onClick={onSubmit}
loading={submitting}
>
{connected ? `Sell ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
</SellButton>
)
) : (
<SellButton
disabled={
(!price && tradeType === 'Limit') || !baseSize || !connected
}
onClick={onSubmit}
block
type="primary"
size="large"
loading={submitting}
>
{connected ? `Sell ${baseCurrency}` : 'CONNECT WALLET TO TRADE'}
</SellButton>
)
) : (
<Button
size="large"
style={{
margin: '20px 0px 0px 0px',
}}
disabled
>
Country Not Allowed
</Button>
)}
<button css={xw`flex-grow border`} disabled>
<div css={xw`text-lg font-light p-2`}>Country Not Allowed</div>
</button>
)}
</div>
</FloatingElement>
)
}

View File

@ -8,28 +8,40 @@ const TVChartContainer = dynamic(
)
import FloatingElement from '../components/FloatingElement'
import Orderbook from '../components/Orderbook'
import MarginInfo from './MarginInfo'
import TradeForm from './TradeForm'
import UserInfo from './UserInfo'
const ResponsiveGridLayout = WidthProvider(Responsive)
const layouts = {
xl: [
{ i: '1', x: 0, y: 0, w: 3, h: 2 },
{ i: '2', x: 3, y: 0, w: 1, h: 2 },
{ i: '3', x: 4, y: 0, w: 1, h: 1 },
{ i: '4', x: 4, y: 1, w: 1, h: 1 },
{ i: '5', x: 0, y: 2, w: 3, h: 1 },
{ i: '6', x: 3, y: 2, w: 1, h: 1 },
{ i: '7', x: 4, y: 2, w: 1, h: 1 },
],
lg: [
{ i: '1', x: 0, y: 0, w: 2, h: 2 },
{ i: '2', x: 2, y: 0, w: 1, h: 2 },
{ i: '3', x: 3, y: 0, w: 1, h: 1 },
{ i: '4', x: 3, y: 1, w: 1, h: 1 },
{ i: '5', x: 0, y: 2, w: 2, h: 1 },
{ i: '6', x: 2, y: 2, w: 1, h: 1 },
{ i: '7', x: 3, y: 2, w: 1, h: 1 },
],
}
const TradePageGrid = () => {
const layouts = {
lg: [
{ i: '1', x: 0, y: 0, w: 2, h: 2 },
{ i: '2', x: 2, y: 0, w: 1, h: 2 },
{ i: '3', x: 3, y: 0, w: 1, h: 1 },
{ i: '4', x: 3, y: 1, w: 1, h: 1 },
{ i: '5', x: 0, y: 2, w: 2, h: 1 },
{ i: '6', x: 2, y: 2, w: 1, h: 1 },
{ i: '7', x: 3, y: 2, w: 1, h: 1 },
],
}
return (
<ResponsiveGridLayout
className="layout"
layouts={layouts}
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 0 }}
cols={{ lg: 4, md: 3, sm: 2, xs: 1 }}
breakpoints={{ xl: 1600, lg: 1200, md: 996, sm: 768, xs: 0 }}
cols={{ xl: 5, lg: 4, md: 3, sm: 2, xs: 1 }}
rowHeight={300}
>
<div key="1">
@ -45,8 +57,12 @@ const TradePageGrid = () => {
<div key="3">
<TradeForm />
</div>
<div key="4">4</div>
<div key="5">5</div>
<div key="4">
<MarginInfo />
</div>
<div key="5">
<UserInfo />
</div>
<div key="6">6</div>
<div key="7">7</div>
</ResponsiveGridLayout>

20
components/UserInfo.tsx Normal file
View File

@ -0,0 +1,20 @@
import { useEffect } from 'react'
import FloatingElement from './FloatingElement'
const UserInfo = () => {
useEffect(() => {
console.log('user info')
})
return (
<FloatingElement>
<div>
User Info
<div>Tabs Here</div>
<div>More Stuff</div>
</div>
</FloatingElement>
)
}
export default UserInfo

View File

@ -1,13 +1,13 @@
import { useEffect, useMemo } from 'react'
import { Account, Connection } from '@solana/web3.js'
import { IDS } from '@blockworks-foundation/mango-client'
import useSolanaStore from '../stores/useSolanaStore'
import useMangoStore from '../stores/useMangoStore'
const useConnection = () => {
const { cluster, current: connection, endpoint } = useSolanaStore(
const { cluster, current: connection, endpoint } = useMangoStore(
(s) => s.connection
)
const setSolanaStore = useSolanaStore((s) => s.set)
const setSolanaStore = useMangoStore((s) => s.set)
const sendConnection = useMemo(() => new Connection(endpoint, 'recent'), [
endpoint,

View File

@ -3,25 +3,27 @@ import { PublicKey } from '@solana/web3.js'
import { IDS } from '@blockworks-foundation/mango-client'
import useMangoStore from '../stores/useMangoStore'
import useConnection from './useConnection'
import useInterval from './useInterval'
import useSolanaStore from '../stores/useSolanaStore'
// import useInterval from './useInterval'
import useWallet from './useWallet'
const useMarginAccount = () => {
const mangoClient = useMangoStore((s) => s.mangoClient)
const tradeForm = useMangoStore((s) => s.tradeForm)
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup)
const selectedMarginAccount = useMangoStore((s) => s.selectedMarginAccount)
const mangoGroup = useMangoStore((s) => s.mangoGroup)
const mangoGroupName = useMangoStore((s) => s.selectedMangoGroup.name)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const selectedMarginAccount = useMangoStore(
(s) => s.selectedMarginAccount.current
)
const setMangoStore = useMangoStore((s) => s.set)
const { current: wallet } = useSolanaStore((s) => s.wallet)
const { current: wallet } = useMangoStore((s) => s.wallet)
const { connected } = useWallet()
const { cluster, connection } = useConnection()
const clusterIds = useMemo(() => IDS[cluster], [cluster])
const mangoGroupIds = useMemo(
() => clusterIds.mango_groups[selectedMangoGroup],
[clusterIds, selectedMangoGroup]
)
const mangoGroupIds = useMemo(() => clusterIds.mango_groups[mangoGroupName], [
clusterIds,
mangoGroupName,
])
const fetchMangoGroup = useCallback(() => {
if (!mangoClient) return
@ -34,7 +36,7 @@ const useMarginAccount = () => {
.then((mangoGroup) => {
// Set the mango group
setMangoStore((state) => {
state.mangoGroup = mangoGroup
state.selectedMangoGroup.current = mangoGroup
})
})
.catch((err) => {
@ -43,7 +45,8 @@ const useMarginAccount = () => {
}, [connection, mangoClient, mangoGroupIds, setMangoStore])
const fetchMarginAccounts = useCallback(() => {
if (!mangoClient || !wallet || !mangoGroup) return
if (!mangoClient || !mangoGroup || !connected || !wallet.publicKey) return
console.log('fetching margin accounts from: ', wallet.publicKey.toString())
mangoClient
.getMarginAccountsForOwner(
@ -53,21 +56,29 @@ const useMarginAccount = () => {
wallet
)
.then((marginAccounts) => {
console.log('margin Accounts: ', marginAccounts)
if (marginAccounts.length > 0) {
setMangoStore((state) => {
state.marginAcccounts = marginAccounts
state.selectedMarginAccount = marginAccounts[0]
state.selectedMarginAccount.current = marginAccounts[0]
})
}
})
.catch((err) => {
console.error('Could not get margin accounts for user in effect ', err)
})
}, [mangoClient, connection, mangoGroup, wallet])
}, [mangoClient, connection, connected, mangoGroup, wallet])
// useEffect(() => {
// fetchMangoGroup()
// }, [fetchMangoGroup])
useEffect(() => {
fetchMangoGroup()
fetchMarginAccounts()
}, [fetchMangoGroup])
useEffect(() => {
if (!connected) return
fetchMarginAccounts()
}, [connected, fetchMarginAccounts])
// useInterval(() => {
// fetchMarginAccounts()
@ -77,7 +88,6 @@ const useMarginAccount = () => {
return {
mangoClient,
setMangoStore,
tradeForm,
mangoGroup,
marginAccount: selectedMarginAccount,
}

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { useEffect } from 'react'
import useMangoStore from '../stores/useMangoStore'
import useInterval from './useInterval'
import useSerumStore from '../stores/useSerumStore'

View File

@ -16,12 +16,14 @@ const formatTokenMints = (symbols: { [name: string]: string }) => {
const useMarkets = () => {
const setMangoStore = useMangoStore((state) => state.set)
const selectedMangoGroup = useMangoStore((state) => state.selectedMangoGroup)
const mangoGroupName = useMangoStore((state) => state.selectedMangoGroup.name)
const market = useMangoStore((state) => state.market.current)
const { connection, cluster, programId, dexProgramId } = useConnection()
const spotMarkets =
IDS[cluster]?.mango_groups[selectedMangoGroup]?.spot_market_symbols || {}
const spotMarkets = useMemo(
() => IDS[cluster]?.mango_groups[mangoGroupName]?.spot_market_symbols || {},
[cluster, mangoGroupName]
)
const marketList = useMemo(
() =>
@ -38,11 +40,16 @@ const useMarkets = () => {
useEffect(() => {
if (market) return
console.log('loading market', connection)
Market.load(connection, marketList[0].address, {}, marketList[0].programId)
.then((market) => {
setMangoStore((state) => {
state.market.current = market
// @ts-ignore
state.accountInfos[market._decoded.bids.toString()] = null
// @ts-ignore
state.accountInfos[market._decoded.asks.toString()] = null
})
})
.catch(
@ -56,7 +63,7 @@ const useMarkets = () => {
// type: 'error',
// }),
)
}, [connection])
}, [])
const TOKEN_MINTS = useMemo(() => formatTokenMints(IDS[cluster].symbols), [
cluster,
@ -69,7 +76,7 @@ const useMarkets = () => {
token.address.equals(market.baseMintAddress)
)?.name) ||
'UNKNOWN',
[market]
[market, TOKEN_MINTS]
)
const quoteCurrency = useMemo(
@ -79,7 +86,7 @@ const useMarkets = () => {
token.address.equals(market.quoteMintAddress)
)?.name) ||
'UNKNOWN',
[market]
[market, TOKEN_MINTS]
)
return { market, programId, marketList, baseCurrency, quoteCurrency }

View File

@ -1,14 +1,13 @@
import { useEffect } from 'react'
import { useEffect, useMemo } from 'react'
import { PublicKey, AccountInfo } from '@solana/web3.js'
import { Orderbook } from '@project-serum/serum'
import useMarkets from './useMarkets'
import useInterval from './useInterval'
import useMangoStore from '../stores/useMangoStore'
import useSolanaStore from '../stores/useSolanaStore'
import useConnection from './useConnection'
function useAccountInfo(account: PublicKey) {
const setSolanaStore = useSolanaStore((s) => s.set)
const setSolanaStore = useMangoStore((s) => s.set)
const { connection } = useConnection()
const accountPkAsString = account ? account.toString() : null
@ -50,7 +49,7 @@ export function useAccountData(publicKey) {
useAccountInfo(publicKey)
const account = publicKey ? publicKey.toString() : null
const accountInfo = useSolanaStore((s) => s.accountInfos[account])
const accountInfo = useMangoStore((s) => s.accountInfos[account])
return accountInfo && Buffer.from(accountInfo.data)
}
@ -74,23 +73,27 @@ export default function useOrderbook(
const setMangoStore = useMangoStore((s) => s.set)
const bids =
!bidOrderbook || !market
? []
: bidOrderbook.getL2(depth).map(([price, size]) => [price, size])
const asks =
!askOrderbook || !market
? []
: askOrderbook.getL2(depth).map(([price, size]) => [price, size])
const bids = useMemo(
() =>
!bidOrderbook || !market
? []
: bidOrderbook.getL2(depth).map(([price, size]) => [price, size]),
[bidOrderbook, depth, market]
)
const orderBook: [{ bids: number[][]; asks: number[][] }, boolean] = [
{ bids, asks },
!!bids || !!asks,
]
const asks = useMemo(
() =>
!askOrderbook || !market
? []
: askOrderbook.getL2(depth).map(([price, size]) => [price, size]),
[askOrderbook, depth, market]
)
setMangoStore((state) => {
state.market.orderBook = orderBook
})
useEffect(() => {
setMangoStore((state) => {
state.market.orderBook = [{ bids, asks }, !!bids || !!asks]
})
}, [bids, asks])
return orderBook
return [{ bids, asks }, !!bids || !!asks]
}

94
hooks/useTradeHistory.tsx Normal file
View File

@ -0,0 +1,94 @@
import { useCallback, useState, useEffect, useRef } from 'react'
import { isDefined } from '../utils'
import useMangoStore from '../stores/useMangoStore'
import useSerumStore from '../stores/useSerumStore'
const byTimestamp = (a, b) => {
return (
new Date(b.loadTimestamp).getTime() - new Date(a.loadTimestamp).getTime()
)
}
const formatTradeHistory = (newTradeHistory) => {
return newTradeHistory
.flat()
.map((trade) => {
return {
...trade,
marketName: trade.marketName
? trade.marketName
: `${trade.baseCurrency}/${trade.quoteCurrency}`,
key: `${trade.orderId}${trade.side}${trade.uuid}`,
liquidity: trade.maker || trade?.eventFlags?.maker ? 'Maker' : 'Taker',
}
})
.sort(byTimestamp)
}
export const usePrevious = (value) => {
const ref = useRef()
// Store current value in ref
useEffect(() => {
ref.current = value
}, [value]) // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current
}
export const useTradeHistory = () => {
const eventQueueFills = useSerumStore((s) => s.fills)
const [tradeHistory, setTradeHistory] = useState<any[]>([])
const [loadingHistory, setloadingHistory] = useState(false)
const [allTrades, setAllTrades] = useState<any[]>([])
const marginAccount = useMangoStore((s) => s.selectedMarginAccount.current)
const fetchTradeHistory = useCallback(async () => {
if (!marginAccount) return
if (marginAccount.openOrdersAccounts.length === 0) return
setloadingHistory(true)
const openOrdersAccounts = marginAccount.openOrdersAccounts.filter(
isDefined
)
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 : []
})
)
setTradeHistory(formatTradeHistory(results))
setAllTrades(formatTradeHistory(results))
setloadingHistory(false)
}, [marginAccount, eventQueueFills])
useEffect(() => {
if (marginAccount && tradeHistory.length === 0) {
fetchTradeHistory()
}
}, [marginAccount])
useEffect(() => {
if (eventQueueFills && eventQueueFills.length > 0) {
const newFills = eventQueueFills.filter(
(fill) =>
!tradeHistory.find((t) => t.orderId === fill.orderId.toString())
)
const newTradeHistory = [...newFills, ...tradeHistory]
if (newFills.length > 0 && newTradeHistory.length !== allTrades.length) {
const formattedTradeHistory = formatTradeHistory(newTradeHistory)
setAllTrades(formattedTradeHistory)
}
}
}, [tradeHistory, eventQueueFills])
return { tradeHistory: allTrades, loadingHistory, fetchTradeHistory }
}
export default useTradeHistory

View File

@ -2,7 +2,7 @@ import { useEffect } from 'react'
import Wallet from '@project-serum/sol-wallet-adapter'
// import { notify } from './notifications'
import useLocalStorageState from './useLocalStorageState'
import useSolanaStore from '../stores/useSolanaStore'
import useMangoStore from '../stores/useMangoStore'
export const WALLET_PROVIDERS = [
{ name: 'sollet.io', url: 'https://www.sollet.io' },
@ -11,9 +11,9 @@ export const WALLET_PROVIDERS = [
const ENDPOINT = process.env.CLUSTER ? process.env.CLUSTER : 'mainnet-beta'
export default function useWallet() {
const setSolanaStore = useSolanaStore((state) => state.set)
const { current: wallet, connected } = useSolanaStore((state) => state.wallet)
const endpoint = useSolanaStore((state) => state.connection.endpoint)
const setMangoStore = useMangoStore((state) => state.set)
const { current: wallet, connected } = useMangoStore((state) => state.wallet)
const endpoint = useMangoStore((state) => state.connection.endpoint)
const [savedProviderUrl] = useLocalStorageState(
'walletProvider',
'https://www.sollet.io'
@ -23,10 +23,11 @@ export default function useWallet() {
: 'https://www.sollet.io'
useEffect(() => {
if (wallet) return
console.log('creating wallet', endpoint)
const newWallet = new Wallet(providerUrl, ENDPOINT)
setSolanaStore((state) => {
setMangoStore((state) => {
state.wallet.current = newWallet
})
}, [endpoint])
@ -34,11 +35,10 @@ export default function useWallet() {
useEffect(() => {
if (!wallet) return
wallet.on('connect', () => {
setSolanaStore((state) => {
setMangoStore((state) => {
state.wallet.connected = true
})
console.log('connected!')
// const walletPublicKey = wallet.publicKey.toBase58()
// const keyToDisplay =
// walletPublicKey.length > 20
@ -53,8 +53,10 @@ export default function useWallet() {
// })
})
wallet.on('disconnect', () => {
setSolanaStore((state) => {
setMangoStore((state) => {
state.wallet.connected = false
state.marginAccounts = []
state.selectedMarginAccount.current = null
})
// notify({
// message: 'Wallet update',
@ -64,7 +66,7 @@ export default function useWallet() {
})
return () => {
wallet.disconnect()
setSolanaStore((state) => {
setMangoStore((state) => {
state.wallet.connected = false
})
}

View File

@ -7,40 +7,97 @@ import {
MangoGroup,
MarginAccount,
} from '@blockworks-foundation/mango-client'
import { AccountInfo, Connection } from '@solana/web3.js'
import { Wallet } from '@project-serum/sol-wallet-adapter'
import { EndpointInfo } from '../@types/types'
export const ENDPOINTS: EndpointInfo[] = [
{
name: 'mainnet-beta',
endpoint: 'https://solana-api.projectserum.com',
custom: false,
},
{
name: 'devnet',
endpoint: 'https://devnet.solana.com',
custom: false,
},
]
const CLUSTER = 'mainnet-beta'
const ENDPOINT_URL = ENDPOINTS.find((e) => e.name === CLUSTER).endpoint
const DEFAULT_CONNECTION = new Connection(ENDPOINT_URL, 'recent')
interface AccountInfoList {
[key: string]: AccountInfo<Buffer>
}
interface MangoStore extends State {
selectedMangoGroup: string
accountInfos: AccountInfoList
connection: {
cluster: string
current: Connection
endpoint: string
}
market: {
programId: number | null
current: Market | null
markPrice: number
orderBook: any[]
}
mangoClient: MangoClient
mangoGroup: MangoGroup | null
selectedMarginAcccount: MarginAccount | null
mangoGroups: Array<MangoGroup>
selectedMangoGroup: {
name: string
current: MangoGroup | null
}
selectedMarginAccount: {
current: MarginAccount | null
}
tradeForm: {
currency: string
size: number
}
wallet: {
connected: boolean
current: Wallet
}
set: (x: any) => void
}
const useMangoStore = create<MangoStore>(
devtools((set) => ({
selectedMangoGroup: 'BTC_ETH_USDT',
accountInfos: {},
connection: {
cluster: CLUSTER,
current: DEFAULT_CONNECTION,
endpoint: ENDPOINT_URL,
},
selectedMangoGroup: {
name: 'BTC_ETH_USDT',
current: null,
},
market: {
programId: null,
current: null,
markPrice: 0,
orderBook: [],
},
mangoClient: new MangoClient(),
mangoGroup: null,
mangoGroups: [],
marginAccounts: [],
selectedMarginAcccount: null,
selectedMarginAccount: {
current: null,
},
tradeForm: {
size: 0,
currency: 'BTC',
},
wallet: {
connected: false,
current: null,
},
set: (fn) => set(produce(fn)),
}))
)

View File

@ -1,5 +1,4 @@
import create, { State } from 'zustand'
import { devtools } from 'zustand/middleware'
import produce from 'immer'
// import { Connection } from '@solana/web3.js'
// import { Market } from '@project-serum/serum'
@ -13,15 +12,13 @@ interface SerumStore extends State {
set: (x: any) => void
}
const useSerumStore = create<SerumStore>(
devtools((set) => ({
orderbook: {
bids: [],
asks: [],
},
fills: [],
set: (fn) => set(produce(fn)),
}))
)
const useSerumStore = create<SerumStore>((set) => ({
orderbook: {
bids: [],
asks: [],
},
fills: [],
set: (fn) => set(produce(fn)),
}))
export default useSerumStore

View File

@ -1,59 +0,0 @@
import create, { State } from 'zustand'
import { devtools } from 'zustand/middleware'
import produce from 'immer'
import { AccountInfo, Connection } from '@solana/web3.js'
import { Wallet } from '@project-serum/sol-wallet-adapter'
import { EndpointInfo } from '../@types/types'
export const ENDPOINTS: EndpointInfo[] = [
{
name: 'mainnet-beta',
endpoint: 'https://solana-api.projectserum.com',
custom: false,
},
{
name: 'devnet',
endpoint: 'https://devnet.solana.com',
custom: false,
},
]
const CLUSTER = 'mainnet-beta'
const ENDPOINT_URL = ENDPOINTS.find((e) => e.name === CLUSTER).endpoint
const DEFAULT_CONNECTION = new Connection(ENDPOINT_URL, 'recent')
interface AccountInfoList {
[key: string]: AccountInfo<Buffer>
}
interface SolanaStore extends State {
accountInfos: AccountInfoList
connection: {
cluster: string
current: Connection
endpoint: string
}
wallet: {
connected: boolean
current: Wallet
}
set: (x: any) => void
}
const useSolanaStore = create<SolanaStore>(
devtools((set) => ({
accountInfos: {},
connection: {
cluster: CLUSTER,
current: DEFAULT_CONNECTION,
endpoint: ENDPOINT_URL,
},
wallet: {
connected: false,
current: null,
},
set: (fn) => set(produce(fn)),
}))
)
export default useSolanaStore