add recent market trades box
This commit is contained in:
parent
45e278e331
commit
ac631f1fd5
|
@ -135,7 +135,7 @@ export interface SelectedTokenAccounts {
|
|||
[tokenMint: string]: string
|
||||
}
|
||||
|
||||
export interface ChartType {
|
||||
export interface ChartTradeType {
|
||||
market: string
|
||||
size: number
|
||||
price: number
|
||||
|
|
|
@ -8,9 +8,7 @@ const Wrapper = styled.div`
|
|||
|
||||
export default function FloatingElement({ css = undefined, children }) {
|
||||
return (
|
||||
<Wrapper
|
||||
css={xw`m-1 px-2 py-4 h-full bg-mango-dark rounded-lg overflow-auto`}
|
||||
>
|
||||
<Wrapper css={xw`m-1 p-4 h-full bg-mango-dark rounded-lg overflow-auto`}>
|
||||
{children}
|
||||
</Wrapper>
|
||||
)
|
||||
|
|
|
@ -154,30 +154,17 @@ export default function MarginInfo() {
|
|||
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
|
||||
{mAccountInfo.map((entry, i) => (
|
||||
<div css={xw`flex justify-between pt-2 pb-3`} key={i}>
|
||||
<Popover content={entry.desc} placement="topLeft" trigger="hover">
|
||||
<div css={xw`cursor-help text-gray-300`}>{entry.label}</div>
|
||||
</Popover>
|
||||
<div css={xw`text-gray-300 font-light`}>
|
||||
{entry.currency + entry.value}
|
||||
{entry.unit}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
</React.Fragment>
|
||||
</FloatingElement>
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@ import { ArrowUpIcon, ArrowDownIcon } from '@heroicons/react/solid'
|
|||
import useMarkPrice from '../hooks/useMarkPrice'
|
||||
import useOrderbook from '../hooks/useOrderbook'
|
||||
import useMarkets from '../hooks/useMarkets'
|
||||
import { ElementTitle } from './styles'
|
||||
|
||||
const Line = styled.div<any>`
|
||||
text-align: ${(props) => (props.invert ? 'left' : 'right')};
|
||||
|
@ -18,15 +19,7 @@ const Line = styled.div<any>`
|
|||
props['data-bgcolor'] && `background-color: ${props['data-bgcolor']};`}
|
||||
`
|
||||
|
||||
// const Price = styled.div<any>`
|
||||
// position: absolute;
|
||||
// ${(props) => (props.invert ? `left: 5px;` : `right: 15px;`)}
|
||||
// ${(props) => props['data-color'] && `color: ${props['data-color']};`}
|
||||
// `
|
||||
|
||||
export default function Orderbook({ depth = 7 }) {
|
||||
// TODO remove smallScreen
|
||||
const smallScreen = false
|
||||
const markPrice = useMarkPrice()
|
||||
const [orderbook] = useOrderbook()
|
||||
const { baseCurrency, quoteCurrency } = useMarkets()
|
||||
|
@ -89,36 +82,34 @@ export default function Orderbook({ depth = 7 }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div css={xw`flex justify-center pb-1 text-lg font-light`}>Orderbook</div>
|
||||
<>
|
||||
<div css={xw`text-gray-500 flex justify-between mb-4`}>
|
||||
<div css={xw`text-left`}>Size ({baseCurrency})</div>
|
||||
<div css={xw`text-right`}>Price ({quoteCurrency})</div>
|
||||
</div>
|
||||
{orderbookData?.asks.map(({ price, size, sizePercent }) => (
|
||||
<OrderbookRow
|
||||
key={price + ''}
|
||||
price={price}
|
||||
size={size}
|
||||
side={'sell'}
|
||||
sizePercent={sizePercent}
|
||||
onPriceClick={() => alert(`price ${price}`)}
|
||||
onSizeClick={() => alert(`size ${size}`)}
|
||||
/>
|
||||
))}
|
||||
<MarkPriceComponent markPrice={markPrice} />
|
||||
{orderbookData?.bids.map(({ price, size, sizePercent }) => (
|
||||
<OrderbookRow
|
||||
key={price + ''}
|
||||
price={price}
|
||||
size={size}
|
||||
side={'buy'}
|
||||
sizePercent={sizePercent}
|
||||
onPriceClick={() => alert(`price ${price}`)}
|
||||
onSizeClick={() => alert(`size ${size}`)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
<ElementTitle>Orderbook</ElementTitle>
|
||||
<div css={xw`text-gray-500 flex justify-between mb-2`}>
|
||||
<div css={xw`text-left`}>Size ({baseCurrency})</div>
|
||||
<div css={xw`text-right`}>Price ({quoteCurrency})</div>
|
||||
</div>
|
||||
{orderbookData?.asks.map(({ price, size, sizePercent }) => (
|
||||
<OrderbookRow
|
||||
key={price + ''}
|
||||
price={price}
|
||||
size={size}
|
||||
side={'sell'}
|
||||
sizePercent={sizePercent}
|
||||
onPriceClick={() => alert(`price ${price}`)}
|
||||
onSizeClick={() => alert(`size ${size}`)}
|
||||
/>
|
||||
))}
|
||||
<MarkPriceComponent markPrice={markPrice} />
|
||||
{orderbookData?.bids.map(({ price, size, sizePercent }) => (
|
||||
<OrderbookRow
|
||||
key={price + ''}
|
||||
price={price}
|
||||
size={size}
|
||||
side={'buy'}
|
||||
sizePercent={sizePercent}
|
||||
onPriceClick={() => alert(`price ${price}`)}
|
||||
onSizeClick={() => alert(`size ${size}`)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -152,7 +143,7 @@ const OrderbookRow = React.memo<any>(
|
|||
|
||||
return (
|
||||
<div
|
||||
css={xw`flex justify-between font-light`}
|
||||
css={xw`flex mb-0.5 justify-between font-light`}
|
||||
ref={element}
|
||||
onClick={onSizeClick}
|
||||
>
|
||||
|
@ -176,7 +167,7 @@ const OrderbookRow = React.memo<any>(
|
|||
</>
|
||||
) : (
|
||||
<>
|
||||
<div css={xw`text-left flex-1`}>{formattedSize}</div>
|
||||
<div css={xw`text-left flex-1 text-gray-200`}>{formattedSize}</div>
|
||||
<div css={xw`text-right relative flex-1`}>
|
||||
<Line
|
||||
css={xw`absolute inset-y-0 right-0`}
|
||||
|
@ -184,7 +175,7 @@ const OrderbookRow = React.memo<any>(
|
|||
data-bgcolor={side === 'buy' ? '#5b6b16' : '#E54033'}
|
||||
/>
|
||||
<div
|
||||
css={xw`z-30 relative`}
|
||||
css={xw`z-30 relative text-gray-200`}
|
||||
data-color={side === 'buy' ? '#ffffff' : 'white'}
|
||||
onClick={onPriceClick}
|
||||
>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import { useState } from 'react'
|
||||
// import styled from '@emotion/styled'
|
||||
import xw from 'xwind'
|
||||
import { getDecimalCount } from '../utils'
|
||||
import { ChartTradeType } from '../@types/types'
|
||||
import FloatingElement from './FloatingElement'
|
||||
import useMarkets from '../hooks/useMarkets'
|
||||
import useInterval from '../hooks/useInterval'
|
||||
import ChartApi from '../utils/chartDataConnector'
|
||||
import { ElementTitle } from './styles'
|
||||
|
||||
export default function PublicTrades() {
|
||||
const { baseCurrency, quoteCurrency, market, marketAddress } = useMarkets()
|
||||
const [trades, setTrades] = useState([])
|
||||
|
||||
useInterval(async () => {
|
||||
const trades = await ChartApi.getRecentTrades(marketAddress)
|
||||
setTrades(trades)
|
||||
}, 5000)
|
||||
|
||||
return (
|
||||
<FloatingElement>
|
||||
<ElementTitle>Recent Market Trades</ElementTitle>
|
||||
<div css={xw`grid grid-cols-3 text-gray-500 font-light mb-2`}>
|
||||
<div>Price ({quoteCurrency}) </div>
|
||||
<div css={xw`text-right`}>Size ({baseCurrency})</div>
|
||||
<div css={xw`text-right`}>Time</div>
|
||||
</div>
|
||||
{!!trades.length && (
|
||||
<div>
|
||||
{trades.map((trade: ChartTradeType, i: number) => (
|
||||
<div key={i} css={xw`mb-2 font-light grid grid-cols-3`}>
|
||||
<div
|
||||
style={{
|
||||
color: trade.side === 'buy' ? '#AFD803' : '#E54033',
|
||||
}}
|
||||
>
|
||||
{market?.tickSize && !isNaN(trade.price)
|
||||
? Number(trade.price).toFixed(
|
||||
getDecimalCount(market.tickSize)
|
||||
)
|
||||
: trade.price}
|
||||
</div>
|
||||
<div css={xw`text-right`}>
|
||||
{market?.minOrderSize && !isNaN(trade.size)
|
||||
? Number(trade.size).toFixed(
|
||||
getDecimalCount(market.minOrderSize)
|
||||
)
|
||||
: trade.size}
|
||||
</div>
|
||||
<div css={xw`text-right text-gray-500`}>
|
||||
{trade.time && new Date(trade.time).toLocaleTimeString()}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</FloatingElement>
|
||||
)
|
||||
}
|
|
@ -11,6 +11,7 @@ import Orderbook from '../components/Orderbook'
|
|||
import MarginInfo from './MarginInfo'
|
||||
import TradeForm from './TradeForm'
|
||||
import UserInfo from './UserInfo'
|
||||
import RecentMarketTrades from './RecentMarketTrades'
|
||||
|
||||
const ResponsiveGridLayout = WidthProvider(Responsive)
|
||||
|
||||
|
@ -20,18 +21,18 @@ const layouts = {
|
|||
{ i: 'orderbook', x: 3, y: 0, w: 1, h: 17 },
|
||||
{ i: 'tradeForm', x: 4, y: 0, w: 1, h: 17 },
|
||||
{ i: 'marginInfo', x: 4, y: 1, w: 1, h: 13 },
|
||||
{ i: 'useInfo', x: 0, y: 2, w: 3, h: 13 },
|
||||
{ i: 'balanceInfo', x: 3, y: 2, w: 1, h: 13 },
|
||||
{ i: 'extraInfo2', x: 4, y: 2, w: 1, h: 13 },
|
||||
{ i: 'userInfo', x: 0, y: 2, w: 3, h: 13 },
|
||||
{ i: 'marketTrades', x: 3, y: 2, w: 1, h: 13 },
|
||||
{ i: 'balanceInfo', x: 4, y: 2, w: 1, h: 13 },
|
||||
],
|
||||
lg: [
|
||||
{ i: 'tvChart', x: 0, y: 0, w: 2, h: 30 },
|
||||
{ i: 'orderbook', x: 2, y: 0, w: 1, h: 17 },
|
||||
{ i: 'tradeForm', x: 3, y: 0, w: 1, h: 17 },
|
||||
{ i: 'marginInfo', x: 3, y: 1, w: 1, h: 13 },
|
||||
{ i: 'useInfo', x: 0, y: 2, w: 2, h: 13 },
|
||||
{ i: 'balanceInfo', x: 2, y: 2, w: 1, h: 13 },
|
||||
{ i: 'extraInfo2', x: 3, y: 2, w: 1, h: 13 },
|
||||
{ i: 'userInfo', x: 0, y: 2, w: 2, h: 13 },
|
||||
{ i: 'marketTrades', x: 2, y: 2, w: 1, h: 13 },
|
||||
{ i: 'balanceInfo', x: 3, y: 2, w: 1, h: 13 },
|
||||
],
|
||||
}
|
||||
|
||||
|
@ -60,11 +61,13 @@ const TradePageGrid = () => {
|
|||
<div key="marginInfo">
|
||||
<MarginInfo />
|
||||
</div>
|
||||
<div key="useInfo">
|
||||
<div key="userInfo">
|
||||
<UserInfo />
|
||||
</div>
|
||||
<div key="balanceInfo">6</div>
|
||||
<div key="extraInfo2">7</div>
|
||||
<div key="marketTrades">
|
||||
<RecentMarketTrades />
|
||||
</div>
|
||||
</ResponsiveGridLayout>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
import styled from '@emotion/styled'
|
||||
import xw from 'xwind'
|
||||
|
||||
export const ElementTitle = styled.div(xw`
|
||||
flex justify-center mb-4 text-lg tracking-tight
|
||||
`)
|
|
@ -19,6 +19,10 @@ const useMarkets = () => {
|
|||
const mangoGroupName = useMangoStore((state) => state.selectedMangoGroup.name)
|
||||
const market = useMangoStore((state) => state.market.current)
|
||||
const { connection, cluster, programId, dexProgramId } = useConnection()
|
||||
const marketAddress = useMemo(
|
||||
() => (market ? market.publicKey.toString() : null),
|
||||
[market]
|
||||
)
|
||||
|
||||
const spotMarkets = useMemo(
|
||||
() => IDS[cluster]?.mango_groups[mangoGroupName]?.spot_market_symbols || {},
|
||||
|
@ -89,7 +93,14 @@ const useMarkets = () => {
|
|||
[market, TOKEN_MINTS]
|
||||
)
|
||||
|
||||
return { market, programId, marketList, baseCurrency, quoteCurrency }
|
||||
return {
|
||||
market,
|
||||
marketAddress,
|
||||
programId,
|
||||
marketList,
|
||||
baseCurrency,
|
||||
quoteCurrency,
|
||||
}
|
||||
}
|
||||
|
||||
export default useMarkets
|
||||
|
|
|
@ -5,6 +5,9 @@ module.exports = {
|
|||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
cursor: {
|
||||
help: 'help',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Lato', ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ChartType } from '../@types/types'
|
||||
import { ChartTradeType } from '../@types/types'
|
||||
|
||||
const baseUrl = 'https://serum-history.herokuapp.com'
|
||||
export default class ChartApi {
|
||||
|
@ -19,7 +19,7 @@ export default class ChartApi {
|
|||
|
||||
static async getRecentTrades(
|
||||
marketAddress: string
|
||||
): Promise<ChartType[] | null> {
|
||||
): Promise<ChartTradeType[] | null> {
|
||||
return ChartApi.get(`trades/address/${marketAddress}`)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue