Merge pull request #7 from blockworks-foundation/account-page

Account page
This commit is contained in:
saml33 2021-07-29 23:36:52 +10:00 committed by GitHub
commit 15db74feb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 767 additions and 189 deletions

View File

@ -81,7 +81,7 @@ const BalancesTable = () => {
>
<Table className={`min-w-full divide-y divide-th-bkg-2`}>
<Thead>
<Tr className="text-th-fgd-3">
<Tr className="text-th-fgd-3 text-xs">
<Th
scope="col"
className={`px-6 py-2 text-left font-normal`}
@ -134,7 +134,7 @@ const BalancesTable = () => {
`}
>
<Td
className={`flex items-center px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
className={`flex items-center px-4 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
<img
alt=""
@ -147,27 +147,27 @@ const BalancesTable = () => {
{balance.symbol}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-4 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.marginDeposits.toFixed(tokenConfig.decimals)}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-4 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.borrows.toFixed(tokenConfig.decimals)}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-4 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.orders}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-4 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.unsettled}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-4 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.net.toFixed(tokenConfig.decimals)}
</Td>

View File

@ -17,8 +17,8 @@ const Button: FunctionComponent<ButtonProps> = ({
<button
onClick={onClick}
disabled={disabled}
className={`${className} px-6 py-2 border border-th-fgd-4 bg-th-bkg-2 rounded-md text-th-fgd-1
active:border-mango-yellow hover:bg-th-bkg-3 focus:outline-none disabled:bg-th-bkg-2
className={`${className} px-6 py-2 bg-th-bkg-4 rounded-full text-th-fgd-1
hover:brightness-[1.15] focus:outline-none disabled:bg-th-bkg-4
disabled:text-th-fgd-4 disabled:cursor-not-allowed`}
{...props}
>
@ -47,3 +47,22 @@ export const LinkButton: FunctionComponent<ButtonProps> = ({
</button>
)
}
export const IconButton: FunctionComponent<ButtonProps> = ({
children,
onClick,
disabled = false,
className,
...props
}) => {
return (
<button
onClick={onClick}
disabled={disabled}
className={`${className} bg-th-bkg-4 flex items-center justify-center rounded-full w-8 h-8 text-th-fgd-1 focus:outline-none hover:text-th-primary`}
{...props}
>
{children}
</button>
)
}

View File

@ -44,8 +44,8 @@ const ConnectWalletButton = () => {
{connected && wallet?.publicKey ? (
<Menu>
<div className="relative h-full">
<Menu.Button className="bg-th-fgd-4 flex items-center justify-center rounded-full w-10 h-10 text-th-fgd-2 focus:outline-none hover:bg-th-bkg-3 hover:text-th-fgd-3">
<ProfileIcon className="fill-current h-6 w-6" />
<Menu.Button className="bg-th-bkg-4 flex items-center justify-center rounded-full w-10 h-10 text-white focus:outline-none hover:bg-th-bkg-3 hover:text-th-fgd-3">
<ProfileIcon className="h-6 w-6" />
</Menu.Button>
<Menu.Items className="bg-th-bkg-1 mt-2 p-1 absolute right-0 shadow-lg outline-none rounded-md w-48 z-20">
<Menu.Item>

View File

@ -8,7 +8,7 @@ import useMangoStore from '../stores/useMangoStore'
import DepositModal from './DepositModal'
import WithdrawModal from './WithdrawModal'
// import BorrowModal from './BorrowModal'
import Button from './Button'
import Button, { IconButton } from './Button'
import AccountsModal from './AccountsModal'
export default function MarginBalances() {
@ -48,11 +48,10 @@ export default function MarginBalances() {
<ElementTitle noMarignBottom>Mango Account</ElementTitle>
<div className="absolute right-0 pr-4">
<Menu>
<Menu.Button
className="flex items-center justify-center rounded-full bg-th-bkg-3 w-8 h-8 hover:text-th-primary focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
disabled={!connected}
>
<Menu.Button disabled={!connected}>
<IconButton>
<DotsHorizontalIcon className="w-5 h-5" />
</IconButton>
</Menu.Button>
<Menu.Items className="bg-th-bkg-1 mt-2 p-1 absolute right-0 shadow-lg outline-none rounded-md w-48 z-20">
<Menu.Item>

View File

@ -2,6 +2,7 @@ import { useState } from 'react'
import { RefreshClockwiseIcon } from './icons'
import useMangoStore from '../stores/useMangoStore'
import Tooltip from './Tooltip'
import { IconButton } from './Button'
const ManualRefresh = ({ className = '' }) => {
const [spin, setSpin] = useState(false)
@ -18,14 +19,11 @@ const ManualRefresh = ({ className = '' }) => {
return (
<div className={`inline-flex relative ${className}`}>
<Tooltip content="Refresh Data" className="text-xs py-1">
<button
onClick={() => handleRefreshData()}
className="flex items-center justify-center rounded-full bg-th-bkg-3 w-8 h-8 hover:text-th-primary focus:outline-none"
>
<IconButton onClick={handleRefreshData}>
<RefreshClockwiseIcon
className={`w-4 h-4 ${spin ? 'animate-spin' : null}`}
/>
</button>
</IconButton>
</Tooltip>
</div>
)

View File

@ -106,11 +106,14 @@ export default function MarginInfo() {
</div>
</div> */}
</div>
<div className="border border-th-bkg-3 mt-4 p-4 rounded">
<div className="border border-th-bkg-4 mt-4 p-4 rounded">
<div className="flex flex-col">
<div className="flex justify-between">
<div className="flex items-center">
<HeartIcon className="h-5 w-5" aria-hidden="true" />
<HeartIcon
className="h-5 w-5 text-th-primary"
aria-hidden="true"
/>
<span className="ml-2">Health Ratio</span>
</div>
<div className="text-right">{maintHealth.toFixed(2)}%</div>
@ -121,7 +124,13 @@ export default function MarginInfo() {
style={{
width: `${maintHealth}%`,
}}
className="flex rounded bg-th-primary"
className={`flex rounded ${
maintHealth > 50
? 'bg-th-green'
: maintHealth <= 50 && maintHealth > 24
? 'bg-th-orange'
: 'bg-th-red'
}`}
></div>
</div>
</div>

View File

@ -80,7 +80,7 @@ export default function MarketMenuItem({ menuTitle = '', linksArray = [] }) {
className="flex flex-col h-10"
>
<Popover.Button
className="flex items-center px-2.5 h-10 text-th-fgd-3 hover:text-th-primary focus:outline-none"
className="flex items-center px-3 h-10 text-th-fgd-3 hover:text-th-primary focus:outline-none"
ref={buttonRef}
onClick={() => handleClick(open)}
>

View File

@ -15,16 +15,16 @@ const StyledMarketSelectWrapper = styled.div`
}
`
const StyledMarketTypeToggleWrapper = styled.div`
background: rgba(255, 255, 255, 0.12);
`
// const StyledMarketTypeToggleWrapper = styled.div`
// background: rgba(255, 255, 255, 0.12);
// `
const StyledArrow = styled.div`
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-left: 20px solid rgba(255, 255, 255, 0.12);
// border-left: 20px solid rgba(255, 255, 255, 0.12);
padding-right: 0.5rem;
`
@ -54,15 +54,15 @@ const MarketSelect = () => {
return (
<>
<StyledMarketSelectWrapper className="bg-th-bkg-3 flex h-10">
<StyledMarketTypeToggleWrapper className="flex items-center pl-6 md:pl-9 pr-1">
<div className="bg-th-bkg-4 flex items-center pl-6 md:pl-9 pr-1">
<LinkButton
className="font-normal text-th-fgd-2 text-xs"
onClick={() => setShowMarketsModal(true)}
>
MARKETS
</LinkButton>
</StyledMarketTypeToggleWrapper>
<StyledArrow />
</div>
<StyledArrow className="border-l-[20px] border-th-bkg-4" />
<div className="flex items-center justify-between w-full">
<div className="flex items-center">
{sortedMarkets

View File

@ -12,6 +12,7 @@ import SideBadge from './SideBadge'
import { useSortableData } from '../hooks/useSortableData'
import { Order, Market } from '@project-serum/serum/lib/market'
import { PerpOrder, PerpMarket } from '@blockworks-foundation/mango-client'
import { usdFormatter } from '../utils'
const OpenOrdersTable = () => {
const { asPath } = useRouter()
@ -76,9 +77,9 @@ const OpenOrdersTable = () => {
<Table className={`min-w-full divide-y divide-th-bkg-2`}>
<Thead>
<Tr className="text-th-fgd-3 text-xs">
<Th scope="col" className={`px-6 py-2 text-left`}>
<Th scope="col" className={`px-6 py-2`}>
<LinkButton
className="flex items-center no-underline font-normal text-sm"
className="flex items-center no-underline font-normal"
onClick={() => requestSort('marketName')}
>
Market
@ -93,9 +94,9 @@ const OpenOrdersTable = () => {
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left`}>
<Th scope="col" className={`px-6 py-2`}>
<LinkButton
className="flex items-center no-underline font-normal text-sm"
className="flex items-center no-underline font-normal"
onClick={() => requestSort('side')}
>
Side
@ -110,9 +111,9 @@ const OpenOrdersTable = () => {
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left`}>
<Th scope="col" className={`px-6 py-2`}>
<LinkButton
className="flex items-center no-underline font-normal text-sm"
className="flex items-center no-underline font-normal"
onClick={() => requestSort('size')}
>
Size
@ -127,9 +128,9 @@ const OpenOrdersTable = () => {
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left`}>
<Th scope="col" className={`px-6 py-2`}>
<LinkButton
className="flex items-center no-underline font-normal text-sm"
className="flex items-center no-underline font-normal"
onClick={() => requestSort('price')}
>
Price
@ -158,7 +159,7 @@ const OpenOrdersTable = () => {
`}
>
<Td
className={`px-4 py-1 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-6 py-2 whitespace-nowrap text-th-fgd-1`}
>
<div className="flex items-center">
<img
@ -172,21 +173,21 @@ const OpenOrdersTable = () => {
</div>
</Td>
<Td
className={`px-4 py-1 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-6 py-2 whitespace-nowrap text-th-fgd-1`}
>
<SideBadge side={order.side} />
</Td>
<Td
className={`px-4 py-1 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-6 py-2 whitespace-nowrap text-th-fgd-1`}
>
{order.size}
</Td>
<Td
className={`px-4 py-1 whitespace-nowrap text-sm text-th-fgd-1`}
className={`px-6 py-2 whitespace-nowrap text-th-fgd-1`}
>
{order.price}
{usdFormatter.format(order.price)}
</Td>
<Td className={`px-4 py-1 whitespace-nowrap text-left`}>
<Td className={`px-6 py-2 whitespace-nowrap`}>
<div className={`flex justify-end`}>
<Button
onClick={() =>

View File

@ -15,6 +15,7 @@ import BN from 'bn.js'
import SideBadge from './SideBadge'
import { useState } from 'react'
import Loading from './Loading'
import { usdFormatter } from '../utils'
const PositionsTable = () => {
const actions = useMangoStore((s) => s.actions)
@ -95,9 +96,9 @@ const PositionsTable = () => {
<div className="overflow-hidden border-b border-th-bkg-2 sm:rounded-m">
<Table className="min-w-full divide-y divide-th-bkg-2">
<Thead>
<Tr className="text-th-fgd-3">
<Tr className="text-th-fgd-3 text-xs">
<Th scope="col" className="px-6 py-2 text-left font-normal">
Perp Market
Market
</Th>
<Th scope="col" className="px-2 py-2 text-left font-normal">
Side
@ -144,7 +145,7 @@ const PositionsTable = () => {
${index % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
`}
>
<Td className="px-6 py-1 whitespace-nowrap text-sm text-th-fgd-1">
<Td className="px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1">
<div className="flex items-center">
<img
alt=""
@ -156,7 +157,7 @@ const PositionsTable = () => {
<div>{marketConfig.name}</div>
</div>
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
<SideBadge
side={
perpAccount.basePosition.gt(new BN(0))
@ -165,25 +166,28 @@ const PositionsTable = () => {
}
/>
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
{perpMarket.baseLotsToNumber(
perpAccount.basePosition
)}
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
{nativeI80F48ToUi(
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
{usdFormatter.format(
+nativeI80F48ToUi(
perpAccount.quotePosition,
marketConfig.quoteDecimals
).toFixed()}
)
)}
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
$
{nativeI80F48ToUi(
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
{usdFormatter.format(
+nativeI80F48ToUi(
perpAccount.getPnl(perpMarketInfo, price),
marketConfig.quoteDecimals
).toFixed()}
)
)}
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
<Td className="px-2 py-2 whitespace-nowrap text-sm text-th-fgd-1">
{perpAccount
.getHealth(
perpMarketInfo,
@ -195,7 +199,7 @@ const PositionsTable = () => {
)
.toFixed(3)}
</Td>
<Td className="px-6 py-1 whitespace-nowrap text-sm text-th-fgd-1">
<Td className="px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1">
<div className="flex justify-end">
<Button
onClick={() =>

View File

@ -2,6 +2,7 @@ import { TemplateIcon } from '@heroicons/react/outline'
import { defaultLayouts, GRID_LAYOUT_KEY } from './TradePageGrid'
import useLocalStorageState from '../hooks/useLocalStorageState'
import Tooltip from './Tooltip'
import { IconButton } from './Button'
const ResetLayout = ({ className = '' }) => {
const [, setSavedLayouts] = useLocalStorageState(
@ -16,12 +17,9 @@ const ResetLayout = ({ className = '' }) => {
return (
<div className={`inline-flex relative ${className}`}>
<Tooltip content="Reset Layout" className="text-xs py-1">
<button
onClick={() => handleResetLayout()}
className="flex items-center justify-center rounded-full bg-th-bkg-3 w-8 h-8 hover:text-th-primary focus:outline-none"
>
<IconButton onClick={handleResetLayout}>
<TemplateIcon className="w-4 h-4" />
</button>
</IconButton>
</Tooltip>
</div>
)

View File

@ -24,7 +24,7 @@ const Switch: FunctionComponent<SwitchProps> = ({
<button
type="button"
className={`${
checked ? 'bg-th-primary' : 'bg-th-fgd-4'
checked ? 'bg-th-primary' : 'bg-th-bkg-4'
} relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent
rounded-full cursor-pointer transition-colors ease-in-out duration-200
focus:outline-none`}

View File

@ -3,6 +3,7 @@ import { useTheme } from 'next-themes'
import { MoonIcon, SunIcon } from '@heroicons/react/outline'
import DropMenu from './DropMenu'
import { MangoIcon } from './icons'
import { IconButton } from './Button'
const THEMES = [
{ name: 'Light', icon: <SunIcon className="h-4 w-4" /> },
@ -20,9 +21,7 @@ const ThemeSwitch = () => {
return mounted ? (
<DropMenu
button={
<div className="flex items-center justify-center rounded-full bg-th-bkg-3 w-8 h-8">
{THEMES.find((t) => t.name === theme).icon}
</div>
<IconButton>{THEMES.find((t) => t.name === theme).icon}</IconButton>
}
buttonClassName="flex items-center justify-center hover:text-th-primary rounded-md focus:outline-none"
value={theme}

View File

@ -58,15 +58,15 @@ const TopBar = () => {
</div>
{mangoAccount ? (
<div className="pl-3">
<Button
className="pb-1 pt-1 pl-2 pr-2 text-xs"
<button
className="border border-th-bkg-4 py-1 px-2 rounded text-xs focus:outline-none hover:border-th-fgd-4"
onClick={() => setShowAccountsModal(true)}
>
<div className="font-normal text-th-primary tiny-text">
Account
</div>
{abbreviateAddress(mangoAccount.publicKey)}
</Button>
</button>
</div>
) : null}
<div className="flex">

View File

@ -374,15 +374,15 @@ export default function TradeForm() {
onClick={onSubmit}
className={`${
!disabledTradeButton
? 'border-th-green hover:border-th-green-dark'
: undefined
? 'bg-th-bkg-2 border border-th-green hover:border-th-green-dark'
: 'border border-th-bkg-4'
} text-th-green hover:text-th-fgd-1 hover:bg-th-green-dark flex-grow`}
>
{`${
baseSize > 0
? 'Buy ' + baseSize
: 'Set BUY bid >= ' + minOrderSize
} ${marketConfig.baseSymbol}`}
{`${baseSize > 0 ? 'Buy ' + baseSize : 'Buy '} ${
marketConfig.name.includes('PERP')
? marketConfig.name
: marketConfig.baseSymbol
}`}
</Button>
) : (
<Button
@ -390,15 +390,15 @@ export default function TradeForm() {
onClick={onSubmit}
className={`${
!disabledTradeButton
? 'border-th-red hover:border-th-red-dark'
: undefined
? 'bg-th-bkg-2 border border-th-red hover:border-th-red-dark'
: 'border border-th-bkg-4'
} text-th-red hover:text-th-fgd-1 hover:bg-th-red-dark flex-grow`}
>
{`${
baseSize > 0
? 'Sell ' + baseSize
: 'Set SELL bid >= ' + minOrderSize
} ${marketConfig.baseSymbol}`}
{`${baseSize > 0 ? 'Sell ' + baseSize : 'Sell '} ${
marketConfig.name.includes('PERP')
? marketConfig.name
: marketConfig.baseSymbol
}`}
</Button>
)
) : (

View File

@ -58,7 +58,10 @@ const TVChartContainer = () => {
useEffect(() => {
const widgetOptions: ChartingLibraryWidgetOptions = {
// TODO: stop trading view from not crash looping on perp
symbol: selectedMarketConfig.kind == "perp" ? "BTC/USDC" : selectedMarketConfig.name,
symbol:
selectedMarketConfig.kind == 'perp'
? 'BTC/USDC'
: selectedMarketConfig.name,
// BEWARE: no trailing slash is expected in feed URL
// tslint:disable-next-line:no-any
datafeed: new (window as any).Datafeeds.UDFCompatibleDatafeed(
@ -102,7 +105,7 @@ const TVChartContainer = () => {
loading_screen: { backgroundColor: 'rgba(0,0,0,0.1)' },
overrides: {
'paneProperties.background':
theme === 'Dark' ? '#2B2B2B' : theme === 'Light' ? '#fff' : '#1D1832',
theme === 'Dark' ? '#1B1B1F' : theme === 'Light' ? '#fff' : '#1D1832',
'mainSeriesProperties.candleStyle.upColor':
theme === 'Mango' ? '#AFD803' : '#5EBF4D',
'mainSeriesProperties.candleStyle.downColor':

View File

@ -3,6 +3,7 @@ import { Transition } from '@headlessui/react'
import useMangoStore from '../stores/useMangoStore'
import ResetLayout from './ResetLayout'
import Tooltip from './Tooltip'
import { IconButton } from './Button'
const UiLock = ({ className = '' }) => {
const set = useMangoStore((s) => s.set)
@ -36,16 +37,13 @@ const UiLock = ({ className = '' }) => {
content={uiLocked ? 'Unlock Layout' : 'Lock Layout'}
className="text-xs py-1"
>
<div
onClick={handleClick}
className="flex items-center justify-center rounded-full bg-th-bkg-3 w-8 h-8 default-transition hover:text-th-primary focus:outline-none"
>
<IconButton onClick={handleClick}>
{uiLocked ? (
<LockClosedIcon className="w-4 h-4" />
) : (
<LockOpenIcon className="w-4 h-4 animate-bounce" />
)}
</div>
</IconButton>
</Tooltip>
</div>
</div>

View File

@ -162,10 +162,10 @@ export default function AccountAssets() {
{balances.map((bal, i) => {
const token = getTokenBySymbol(groupConfig, bal.symbol)
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
console.log(
'price cache',
mangoCache.priceCache[tokenIndex]
)
// console.log(
// 'price cache',
// mangoCache.priceCache[tokenIndex]
// )
return (
<Tr

View File

@ -0,0 +1,493 @@
import { useEffect, useMemo, useState } from 'react'
import { Table, Thead, Tbody, Tr, Th, Td } from 'react-super-responsive-table'
import styled from '@emotion/styled'
import { Menu } from '@headlessui/react'
import {
ArrowSmDownIcon,
CurrencyDollarIcon,
DotsHorizontalIcon,
HeartIcon,
XIcon,
} from '@heroicons/react/outline'
import {
getTokenBySymbol,
getMarketByPublicKey,
I80F48,
nativeI80F48ToUi,
PerpMarket,
} from '@blockworks-foundation/mango-client'
import useMangoStore from '../../stores/useMangoStore'
import { useBalances } from '../../hooks/useBalances'
import { useSortableData } from '../../hooks/useSortableData'
import { usdFormatter, tokenPrecision } from '../../utils'
import SideBadge from '../SideBadge'
import Button, { LinkButton } from '../Button'
import Switch from '../Switch'
import PositionsTable from '../PositionsTable'
const StyledAccountValue = styled.div`
font-size: 1.8rem;
line-height: 1.2;
`
export default function AccountOverview() {
const [spotPortfolio, setSpotPortfolio] = useState([])
const [perpPositions, setPerpPositions] = useState([])
const [filteredSpotPortfolio, setFilteredSpotPortfolio] = useState([])
const [showZeroBalances, setShowZeroBalances] = useState(false)
const allMarkets = useMangoStore((s) => s.selectedMangoGroup.markets)
const balances = useBalances()
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
const { items, requestSort, sortConfig } = useSortableData(
filteredSpotPortfolio
)
const perpMarkets = useMemo(
() =>
mangoGroup
? groupConfig.perpMarkets.map(
(m) => mangoGroup.perpMarkets[m.marketIndex]
)
: [],
[mangoGroup]
)
const perpAccounts = useMemo(
() =>
mangoAccount
? groupConfig.perpMarkets.map(
(m) => mangoAccount.perpAccounts[m.marketIndex]
)
: [],
[mangoAccount]
)
useEffect(() => {
let positions = []
perpAccounts.forEach((acc, index) => {
const market = perpMarkets[index]
const marketConfig = getMarketByPublicKey(groupConfig, market.perpMarket)
const perpMarket = allMarkets[
marketConfig.publicKey.toString()
] as PerpMarket
if (
+nativeI80F48ToUi(acc.quotePosition, marketConfig.quoteDecimals) !== 0
) {
positions.push({
market: marketConfig.name,
balance: perpMarket.baseLotsToNumber(acc.basePosition),
price: mangoGroup.getPrice(marketConfig.marketIndex, mangoCache),
symbol: marketConfig.baseSymbol,
value: +nativeI80F48ToUi(
acc.quotePosition,
marketConfig.quoteDecimals
),
type:
perpMarket.baseLotsToNumber(acc.basePosition) > 0
? 'Long'
: 'Short',
})
}
})
setPerpPositions(positions.sort((a, b) => b.value - a.value))
}, [])
useEffect(() => {
const spotPortfolio = []
balances.forEach((b) => {
const token = getTokenBySymbol(groupConfig, b.symbol)
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
if (+b.marginDeposits > 0) {
spotPortfolio.push({
market: b.symbol,
balance: +b.marginDeposits + b.orders + b.unsettled,
borrowRate: mangoGroup
.getBorrowRate(tokenIndex)
.mul(I80F48.fromNumber(100)),
depositRate: mangoGroup
.getDepositRate(tokenIndex)
.mul(I80F48.fromNumber(100)),
price: mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
symbol: b.symbol,
value:
(+b.marginDeposits + b.orders + b.unsettled) *
mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
type: 'Deposit',
})
} else if (+b.borrows > 0) {
spotPortfolio.push({
market: b.symbol,
balance: +b.borrows,
borrowRate: mangoGroup
.getBorrowRate(tokenIndex)
.mul(I80F48.fromNumber(100)),
depositRate: mangoGroup
.getDepositRate(tokenIndex)
.mul(I80F48.fromNumber(100)),
price: mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
symbol: b.symbol,
value: b.borrows.mul(mangoGroup.getPrice(tokenIndex, mangoCache)),
type: 'Borrow',
})
} else {
spotPortfolio.push({
market: b.symbol,
balance: 0,
borrowRate: mangoGroup
.getBorrowRate(tokenIndex)
.mul(I80F48.fromNumber(100)),
depositRate: mangoGroup
.getDepositRate(tokenIndex)
.mul(I80F48.fromNumber(100)),
price: mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
symbol: b.symbol,
value: 0,
type: '',
})
}
})
setSpotPortfolio(spotPortfolio.sort((a, b) => b.value - a.value))
setFilteredSpotPortfolio(
spotPortfolio
.filter((pos) => pos.balance > 0)
.sort((a, b) => b.value - a.value)
)
}, [])
const handleShowZeroBalances = (checked) => {
if (checked) {
setFilteredSpotPortfolio(spotPortfolio)
} else {
setFilteredSpotPortfolio(spotPortfolio.filter((pos) => pos.balance > 0))
}
setShowZeroBalances(checked)
}
return mangoAccount ? (
<>
<div className="pb-6">
<div className="pb-1 text-th-fgd-3">Account Value</div>
<div className="flex items-center">
<CurrencyDollarIcon className="flex-shrink-0 h-7 w-7 mr-1.5 text-th-primary" />
<StyledAccountValue className="text-th-fgd-1">
{usdFormatter.format(
+mangoAccount.computeValue(mangoGroup, mangoCache).toFixed(2)
)}
</StyledAccountValue>
</div>
</div>
<div className="grid grid-flow-col grid-cols-1 grid-rows-4 sm:grid-cols-2 sm:grid-rows-2 md:grid-cols-4 md:grid-rows-1 gap-4 pb-10">
<div className="border border-th-bkg-4 p-4 rounded-lg">
<div className="pb-0.5 text-xs text-th-fgd-3">Positions</div>
<div className="flex items-center">
<div className="text-lg text-th-fgd-1">
{spotPortfolio.length > 0
? usdFormatter.format(
spotPortfolio.reduce(
(acc, d) => d.market.includes('PERP') && acc + d.value,
0
)
)
: 0}
</div>
</div>
</div>
<div className="border border-th-bkg-4 p-4 rounded-lg">
<div className="pb-0.5 text-xs text-th-fgd-3">Deposits</div>
<div className="flex items-center">
<div className="text-lg text-th-fgd-1">
{usdFormatter.format(
+mangoAccount.getAssetsVal(mangoGroup, mangoCache)
)}
</div>
</div>
</div>
<div className="border border-th-bkg-4 p-4 rounded-lg">
<div className="pb-0.5 text-xs text-th-fgd-3">Borrows</div>
<div className="flex items-center">
<div className="text-lg text-th-fgd-1">
{usdFormatter.format(
+mangoAccount.getLiabsVal(mangoGroup, mangoCache)
)}
</div>
</div>
</div>
<div className="border border-th-bkg-4 p-4 rounded-lg">
<div className="pb-0.5 text-xs text-th-fgd-3">Health Ratio</div>
<div className="flex items-center">
<HeartIcon className="flex-shrink-0 h-5 w-5 mr-2 text-th-primary" />
<div className="text-lg text-th-fgd-1">
{mangoAccount.getHealthRatio(mangoGroup, mangoCache, 'Maint')}%
</div>
</div>
</div>
</div>
<div className="pb-8">
<div className="pb-4 text-th-fgd-1 text-lg">Perp Positions</div>
<PositionsTable />
</div>
{spotPortfolio.length > 0 ? (
<>
<div className="flex items-center justify-between pb-4">
<div className="text-th-fgd-1 text-lg">Assets</div>
<Switch
checked={showZeroBalances}
className="text-xs"
onChange={handleShowZeroBalances}
>
Show zero balances
</Switch>
</div>
<Table className="min-w-full divide-y divide-th-bkg-2">
<Thead>
<Tr className="text-th-fgd-3 text-xs">
<Th scope="col" className={`px-6 py-2 text-left font-normal`}>
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('market')}
>
Asset
<ArrowSmDownIcon
className={`default-transition flex-shrink-0 h-4 w-4 ml-1 ${
sortConfig?.key === 'market'
? sortConfig.direction === 'ascending'
? 'transform rotate-180'
: 'transform rotate-360'
: null
}`}
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left font-normal`}>
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('type')}
>
Type
<ArrowSmDownIcon
className={`default-transition flex-shrink-0 h-4 w-4 ml-1 ${
sortConfig?.key === 'type'
? sortConfig.direction === 'ascending'
? 'transform rotate-180'
: 'transform rotate-360'
: null
}`}
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left font-normal`}>
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('balance')}
>
Balance
<ArrowSmDownIcon
className={`default-transition flex-shrink-0 h-4 w-4 ml-1 ${
sortConfig?.key === 'balance'
? sortConfig.direction === 'ascending'
? 'transform rotate-180'
: 'transform rotate-360'
: null
}`}
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left font-normal`}>
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('price')}
>
Price
<ArrowSmDownIcon
className={`default-transition flex-shrink-0 h-4 w-4 ml-1 ${
sortConfig?.key === 'price'
? sortConfig.direction === 'ascending'
? 'transform rotate-180'
: 'transform rotate-360'
: null
}`}
/>
</LinkButton>
</Th>
<Th scope="col" className="px-6 py-2 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('value')}
>
Value
<ArrowSmDownIcon
className={`default-transition flex-shrink-0 h-4 w-4 ml-1 ${
sortConfig?.key === 'value'
? sortConfig.direction === 'ascending'
? 'transform rotate-180'
: 'transform rotate-360'
: null
}`}
/>
</LinkButton>
</Th>
<Th scope="col" className="px-6 py-2 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('depositRate')}
>
Deposit Rate
<ArrowSmDownIcon
className={`default-transition flex-shrink-0 h-4 w-4 ml-1 ${
sortConfig?.key === 'depositRate'
? sortConfig.direction === 'ascending'
? 'transform rotate-180'
: 'transform rotate-360'
: null
}`}
/>
</LinkButton>
</Th>
<Th scope="col" className="px-6 py-2 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('borrowRate')}
>
Borrow Rate
<ArrowSmDownIcon
className={`default-transition flex-shrink-0 h-4 w-4 ml-1 ${
sortConfig?.key === 'borrowRate'
? sortConfig.direction === 'ascending'
? 'transform rotate-180'
: 'transform rotate-360'
: null
}`}
/>
</LinkButton>
</Th>
</Tr>
</Thead>
<Tbody>
{items.map((pos, i) => (
<Tr
key={i}
className={`border-b border-th-bkg-3
${i % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
`}
>
<Td
className={`px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${pos.symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{pos.market}</div>
</div>
</Td>
<Td
className={`px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{pos.type === 'Long' || pos.type === 'Short' ? (
<SideBadge side={pos.type} />
) : (
pos.type
)}
</Td>
<Td
className={`px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{pos.balance > 0
? pos.balance.toFixed(tokenPrecision[pos.symbol])
: 0}
</Td>
<Td
className={`px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{usdFormatter.format(pos.price)}
</Td>
<Td
className={`px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
>
{usdFormatter.format(pos.value)}
</Td>
<Td
className={`px-6 py-2 whitespace-nowrap text-sm text-th-green`}
>
{pos.depositRate.toFixed(2)}%
</Td>
<Td
className={`px-6 py-2 whitespace-nowrap text-sm text-th-red`}
>
{pos.borrowRate.toFixed(2)}%
</Td>
<Td className={`px-6 py-2 flex justify-end`}>
<Menu>
{({ open }) => (
<div className="relative h-full">
<Menu.Button className="bg-th-bkg-4 flex items-center justify-center rounded-full w-8 h-8 text-th-fgd-1 focus:outline-none hover:text-th-primary">
{open ? (
<XIcon className="h-5 w-5" />
) : (
<DotsHorizontalIcon className="h-5 w-5" />
)}
</Menu.Button>
<Menu.Items className="bg-th-bkg-1 mt-2 p-1 absolute right-0 bottom-10 shadow-lg outline-none rounded-md w-32 z-20">
<div className="border-b border-th-bkg-3 flex items-center p-2 text-th-fgd-3 text-xs">
<img
alt=""
width="12"
height="12"
src={`/assets/icons/${pos.symbol.toLowerCase()}.svg`}
className={`mr-1.5`}
/>
{pos.symbol}
</div>
<Menu.Item>
<button
className="font-normal rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
onClick={() => console.log('true')}
>
<div className="text-left">Trade</div>
</button>
</Menu.Item>
<Menu.Item>
<button
className="font-normal rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
onClick={() => console.log('true')}
>
<div className="text-left">Deposit</div>
</button>
</Menu.Item>
<Menu.Item>
<button
className="font-normal rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
onClick={() => console.log('true')}
>
<div className="text-left">Withdraw</div>
</button>
</Menu.Item>
<Menu.Item>
<button
className="font-normal rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
onClick={() => console.log('true')}
>
<div className="text-left">Borrow</div>
</button>
</Menu.Item>
</Menu.Items>
</div>
)}
</Menu>
</Td>
</Tr>
))}
</Tbody>
</Table>
</>
) : null}
</>
) : null
}

View File

@ -1,14 +1,19 @@
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import {
CurrencyDollarIcon,
ChartBarIcon,
DuplicateIcon,
ExternalLinkIcon,
ChartPieIcon,
LinkIcon,
PencilIcon,
} from '@heroicons/react/outline'
import {
getTokenBySymbol,
getMarketByPublicKey,
nativeI80F48ToUi,
PerpMarket,
} from '@blockworks-foundation/mango-client'
import useMangoStore from '../stores/useMangoStore'
import { useBalances } from '../hooks/useBalances'
import { abbreviateAddress, copyToClipboard } from '../utils'
import PageBodyContainer from '../components/PageBodyContainer'
import TopBar from '../components/TopBar'
@ -17,18 +22,20 @@ import AccountBorrows from '../components/account-page/AccountBorrows'
import AccountOrders from '../components/account-page/AccountOrders'
import AccountHistory from '../components/account-page/AccountHistory'
import AccountsModal from '../components/AccountsModal'
import AccountOverview from '../components/account-page/AccountOverview'
import AccountNameModal from '../components/AccountNameModal'
import Button from '../components/Button'
import EmptyState from '../components/EmptyState'
import { MangoAccount } from '@blockworks-foundation/mango-client'
const TABS = [
'Overview',
'Assets',
'Borrows',
// 'Stats',
// 'Positions',
'Orders',
'History',
'Activity',
]
export function getMarginInfoString(marginAccount: MangoAccount) {
@ -43,9 +50,13 @@ export function getMarginInfoString(marginAccount: MangoAccount) {
export default function Account() {
const [activeTab, setActiveTab] = useState(TABS[0])
const [showAccountsModal, setShowAccountsModal] = useState(false)
const [portfolio, setPortfolio] = useState([])
const allMarkets = useMangoStore((s) => s.selectedMangoGroup.markets)
const balances = useBalances()
const [showNameModal, setShowNameModal] = useState(false)
const [isCopied, setIsCopied] = useState(false)
const connected = useMangoStore((s) => s.wallet.connected)
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
@ -61,6 +72,77 @@ export default function Account() {
setShowAccountsModal(false)
}, [])
const perpMarkets = useMemo(
() =>
mangoGroup
? groupConfig.perpMarkets.map(
(m) => mangoGroup.perpMarkets[m.marketIndex]
)
: [],
[mangoGroup]
)
const perpAccounts = useMemo(
() =>
mangoAccount
? groupConfig.perpMarkets.map(
(m) => mangoAccount.perpAccounts[m.marketIndex]
)
: [],
[mangoAccount]
)
useEffect(() => {
const portfolio = []
perpAccounts.forEach((acc, index) => {
const market = perpMarkets[index]
const marketConfig = getMarketByPublicKey(groupConfig, market.perpMarket)
const perpMarket = allMarkets[
marketConfig.publicKey.toString()
] as PerpMarket
if (
+nativeI80F48ToUi(acc.quotePosition, marketConfig.quoteDecimals) > 0
) {
portfolio.push({
market: marketConfig.name,
balance: perpMarket.baseLotsToNumber(acc.basePosition),
symbol: marketConfig.baseSymbol,
value: +nativeI80F48ToUi(
acc.quotePosition,
marketConfig.quoteDecimals
),
type:
perpMarket.baseLotsToNumber(acc.basePosition) > 0
? 'Long'
: 'Short',
})
}
})
balances.forEach((b) => {
const token = getTokenBySymbol(groupConfig, b.symbol)
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
if (+b.marginDeposits > 0) {
portfolio.push({
market: b.symbol,
balance: +b.marginDeposits + b.orders + b.unsettled,
symbol: b.symbol,
value:
(+b.marginDeposits + b.orders + b.unsettled) *
mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
type: 'Deposits',
})
}
if (+b.borrows > 0) {
portfolio.push({
market: b.symbol,
balance: +b.borrows,
value: b.borrows.mul(mangoGroup.getPrice(tokenIndex, mangoCache)),
type: 'Borrows',
})
}
})
setPortfolio(portfolio.sort((a, b) => b.value - a.value))
}, [perpAccounts])
const handleCopyPublicKey = (code) => {
setIsCopied(true)
copyToClipboard(code)
@ -113,7 +195,7 @@ export default function Account() {
</div>
</Button> */}
<a
className="border border-th-fgd-4 bg-th-bkg-2 default-transition flex flex-grow font-bold h-8 items-center justify-center pl-3 pr-3 rounded-md text-th-fgd-1 text-xs hover:bg-th-bkg-3 hover:text-th-fgd-1 focus:outline-none"
className="bg-th-bkg-4 default-transition flex flex-grow font-bold h-8 items-center justify-center pl-3 pr-3 rounded-full text-th-fgd-1 text-xs hover:bg-th-bkg-3 hover:text-th-fgd-1 focus:outline-none"
href={`https://explorer.solana.com/address/${mangoAccount?.publicKey}`}
target="_blank"
rel="noopener noreferrer"
@ -137,47 +219,7 @@ export default function Account() {
<div className="bg-th-bkg-2 overflow-none p-6 rounded-lg">
{mangoAccount ? (
<>
<div className="pb-4 text-th-fgd-1 text-lg">Overview</div>
<div className="grid grid-flow-col grid-cols-1 grid-rows-4 sm:grid-cols-2 sm:grid-rows-2 md:grid-cols-4 md:grid-rows-1 gap-4 pb-10">
<div className="bg-th-bkg-3 p-3 rounded-md">
<div className="pb-0.5 text-xs text-th-fgd-3">
Account Value
</div>
<div className="flex items-center">
<CurrencyDollarIcon className="flex-shrink-0 h-5 w-5 mr-2 text-th-primary" />
<div className="text-lg text-th-fgd-1">
$
{mangoAccount
.computeValue(mangoGroup, mangoCache)
.toFixed(2)}
</div>
</div>
</div>
<div className="bg-th-bkg-3 p-3 rounded-md">
<div className="pb-0.5 text-xs text-th-fgd-3">Total PnL</div>
<div className="flex items-center">
<ChartBarIcon className="flex-shrink-0 h-5 w-5 mr-2 text-th-primary" />
<div className="text-lg text-th-fgd-1">$0.00</div>
</div>
</div>
<div className="bg-th-bkg-3 p-3 rounded-md">
<div className="pb-0.5 text-xs text-th-fgd-3">
Health Ratio
</div>
<div className="flex items-center">
<ChartPieIcon className="flex-shrink-0 h-5 w-5 mr-2 text-th-primary" />
<div className="text-lg text-th-fgd-1">
{mangoAccount.getHealthRatio(
mangoGroup,
mangoCache,
'Maint'
)}
%
</div>
</div>
</div>
</div>
<div className="border-b border-th-fgd-4 mb-4">
<div className="border-b border-th-fgd-4 mb-8">
<nav className={`-mb-px flex space-x-6`} aria-label="Tabs">
{TABS.map((tabName) => (
<a
@ -196,6 +238,7 @@ export default function Account() {
))}
</nav>
</div>
<TabContent activeTab={activeTab} />
</>
) : connected ? (
@ -235,6 +278,8 @@ export default function Account() {
const TabContent = ({ activeTab }) => {
switch (activeTab) {
case 'Overview':
return <AccountOverview />
case 'Assets':
return <AccountAssets />
case 'Borrows':
@ -248,6 +293,6 @@ const TabContent = ({ activeTab }) => {
case 'History':
return <AccountHistory />
default:
return <AccountAssets />
return <AccountOverview />
}
}

View File

@ -15,6 +15,7 @@
--bkg-1: theme('colors.light-theme["bkg-1"]');
--bkg-2: theme('colors.light-theme["bkg-2"]');
--bkg-3: theme('colors.light-theme["bkg-3"]');
--bkg-4: theme('colors.light-theme["bkg-4"]');
--fgd-1: theme('colors.light-theme["fgd-1"]');
--fgd-2: theme('colors.light-theme["fgd-2"]');
--fgd-3: theme('colors.light-theme["fgd-3"]');
@ -32,6 +33,7 @@
--bkg-1: theme('colors.dark-theme["bkg-1"]');
--bkg-2: theme('colors.dark-theme["bkg-2"]');
--bkg-3: theme('colors.dark-theme["bkg-3"]');
--bkg-4: theme('colors.dark-theme["bkg-4"]');
--fgd-1: theme('colors.dark-theme["fgd-1"]');
--fgd-2: theme('colors.dark-theme["fgd-2"]');
--fgd-3: theme('colors.dark-theme["fgd-3"]');
@ -49,6 +51,7 @@
--bkg-1: theme('colors.mango-theme["bkg-1"]');
--bkg-2: theme('colors.mango-theme["bkg-2"]');
--bkg-3: theme('colors.mango-theme["bkg-3"]');
--bkg-4: theme('colors.mango-theme["bkg-4"]');
--fgd-1: theme('colors.mango-theme["fgd-1"]');
--fgd-2: theme('colors.mango-theme["fgd-2"]');
--fgd-3: theme('colors.mango-theme["fgd-3"]');

View File

@ -19,35 +19,35 @@ module.exports = {
help: 'help',
},
colors: {
'mango-orange': {
DEFAULT: '#DFAB01',
dark: '#CB9C01',
},
'mango-yellow': '#F2C94C',
'mango-red': '#E54033',
'mango-green': '#AFD803',
'mango-dark': {
lighter: '#332F46',
light: '#262337',
DEFAULT: '#141026',
},
'mango-med': {
light: '#C2BDD9',
DEFAULT: '#9490A6',
dark: '#706C81',
},
'mango-light': {
light: '#FCFCFF',
DEFAULT: '#F0EDFF',
dark: '#B9B5CE',
},
'mango-grey': {
lighter: '#f7f7f7',
light: '#e6e6e6',
dark: '#092e34',
darker: '#072428',
darkest: '#061f23',
},
// 'mango-orange': {
// DEFAULT: '#DFAB01',
// dark: '#CB9C01',
// },
// 'mango-yellow': '#F2C94C',
// 'mango-red': '#E54033',
// 'mango-green': '#AFD803',
// 'mango-dark': {
// lighter: '#332F46',
// light: '#262337',
// DEFAULT: '#141026',
// },
// 'mango-med': {
// light: '#C2BDD9',
// DEFAULT: '#9490A6',
// dark: '#706C81',
// },
// 'mango-light': {
// light: '#FCFCFF',
// DEFAULT: '#F0EDFF',
// dark: '#B9B5CE',
// },
// 'mango-grey': {
// lighter: '#f7f7f7',
// light: '#e6e6e6',
// dark: '#092e34',
// darker: '#072428',
// darkest: '#061f23',
// },
'light-theme': {
orange: {
DEFAULT: '#FF9C24',
@ -57,7 +57,8 @@ module.exports = {
green: { DEFAULT: '#5EBF4D', dark: '#4BA53B' },
'bkg-1': '#f7f7f7',
'bkg-2': '#FFFFFF',
'bkg-3': '#EDEDED',
'bkg-3': '#F0F0F0',
'bkg-4': '#E6E6E6',
'fgd-1': '#061f23',
'fgd-2': '#0C3F45',
'fgd-3': '#446065',
@ -71,9 +72,10 @@ module.exports = {
red: { DEFAULT: '#CC2929', dark: '#AA2222' },
green: { DEFAULT: '#5EBF4D', dark: '#4BA53B' },
orange: { DEFAULT: '#FF9C24' },
'bkg-1': '#1C1C1C',
'bkg-2': '#2B2B2B',
'bkg-3': '#424242',
'bkg-1': '#101012',
'bkg-2': '#1B1B1F',
'bkg-3': '#27272B',
'bkg-4': '#38383D',
'fgd-1': '#FFFFFF',
'fgd-2': '#F7F7F7',
'fgd-3': '#E7E7E7',
@ -95,7 +97,8 @@ module.exports = {
orange: { DEFAULT: '#FF9C24' },
'bkg-1': '#141026',
'bkg-2': '#1D1832',
'bkg-3': '#322E47',
'bkg-3': '#2A2440',
'bkg-4': '#37324D',
'fgd-1': '#F0EDFF',
'fgd-2': '#FCFCFF',
'fgd-3': '#B9B5CE',
@ -104,6 +107,7 @@ module.exports = {
'th-bkg-1': 'var(--bkg-1)',
'th-bkg-2': 'var(--bkg-2)',
'th-bkg-3': 'var(--bkg-3)',
'th-bkg-4': 'var(--bkg-4)',
'th-fgd-1': 'var(--fgd-1)',
'th-fgd-2': 'var(--fgd-2)',
'th-fgd-3': 'var(--fgd-3)',

View File

@ -298,3 +298,8 @@ export async function getOrderBookAccountInfos(
return await getMultipleAccounts(DEFAULT_CONNECTION, orderBookPks)
}
export const usdFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
})