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

add unsettled balances ui
This commit is contained in:
saml33 2021-08-01 23:01:05 +10:00 committed by GitHub
commit 2c3e7910e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 300 additions and 218 deletions

View File

@ -1,4 +1,4 @@
import { FunctionComponent, useState } from 'react'
import { FunctionComponent, useEffect, useState } from 'react'
import useMangoStore, { mangoClient } from '../stores/useMangoStore'
import {
ExclamationCircleIcon,
@ -27,6 +27,20 @@ const AccountNameModal: FunctionComponent<AccountNameModalProps> = ({
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const actions = useMangoStore((s) => s.actions)
const setMangoStore = useMangoStore((s) => s.set)
useEffect(() => {
setMangoStore((state) => {
state.blurBackground = true
})
}, [])
const handleClose = () => {
setMangoStore((state) => {
state.blurBackground = false
})
onClose()
}
const submitName = async () => {
const wallet = useMangoStore.getState().wallet.current
@ -72,7 +86,7 @@ const AccountNameModal: FunctionComponent<AccountNameModalProps> = ({
}
return (
<Modal onClose={onClose} isOpen={isOpen}>
<Modal onClose={handleClose} isOpen={isOpen}>
<Modal.Header>
<div className="flex items-center">
<ElementTitle noMarignBottom>Name your Account</ElementTitle>

View File

@ -1,11 +1,7 @@
import React, { FunctionComponent, useEffect, useState } from 'react'
import { RadioGroup } from '@headlessui/react'
import { CheckCircleIcon } from '@heroicons/react/solid'
import {
ChevronLeftIcon,
CurrencyDollarIcon,
PlusCircleIcon,
} from '@heroicons/react/outline'
import { ChevronLeftIcon, PlusCircleIcon } from '@heroicons/react/outline'
import useMangoStore from '../stores/useMangoStore'
import {
MangoAccount,
@ -53,6 +49,19 @@ const AccountsModal: FunctionComponent<AccountsModalProps> = ({
onClose()
}
useEffect(() => {
setMangoStore((state) => {
state.blurBackground = true
})
}, [])
const handleClose = () => {
setMangoStore((state) => {
state.blurBackground = false
})
onClose()
}
useEffect(() => {
if (newAccPublicKey) {
setMangoStore((state) => {
@ -76,7 +85,7 @@ const AccountsModal: FunctionComponent<AccountsModalProps> = ({
}
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Modal isOpen={isOpen} onClose={handleClose}>
{mangoAccounts.length > 0 ? (
!showNewAccountForm ? (
<>
@ -126,7 +135,6 @@ const AccountsModal: FunctionComponent<AccountsModalProps> = ({
<div className="flex items-center">
<div className="text-sm">
<RadioGroup.Label className="cursor-pointer flex items-center text-th-fgd-1">
<CurrencyDollarIcon className="h-5 w-5 mr-2.5" />
<div>
<div className="pb-0.5">
{account?.name ||
@ -142,6 +150,15 @@ const AccountsModal: FunctionComponent<AccountsModalProps> = ({
</div>
) : null}
</div>
{mangoGroup ? (
<div className="text-th-fgd-3 text-xs">
<AccountInfo
mangoGroup={mangoGroup}
mangoAccount={account}
mangoCache={mangoCache}
/>
</div>
) : null}
</RadioGroup.Label>
</div>
</div>

View File

@ -1,7 +1,8 @@
import React from 'react'
import React, { useEffect } from 'react'
import Modal from './Modal'
import Button from './Button'
import useLocalStorageState from '../hooks/useLocalStorageState'
import useMangoStore from '../stores/useMangoStore'
const AlphaModal = ({
isOpen,
@ -14,6 +15,20 @@ const AlphaModal = ({
'mangoAlphaAccepted-2.0',
false
)
const setMangoStore = useMangoStore((s) => s.set)
useEffect(() => {
setMangoStore((state) => {
state.blurBackground = true
})
}, [])
const handleAccept = () => {
setAlphaAccepted(true)
setMangoStore((state) => {
state.blurBackground = false
})
}
return (
<Modal isOpen={isOpen} onClose={onClose} hideClose>
@ -55,7 +70,7 @@ const AlphaModal = ({
<div className={`text-th-fgd-2 text-center`}>
Mango Markets is unaudited software. Use at your own risk.
<div className={`mt-4 flex justify-center`}>
<Button onClick={() => setAlphaAccepted(true)}>
<Button onClick={handleAccept}>
<div className={`flex items-center`}>Accept</div>
</Button>
</div>

View File

@ -47,6 +47,7 @@ const DepositModal: FunctionComponent<DepositModalProps> = ({
const [maxButtonTransition, setMaxButtonTransition] = useState(false)
const walletTokens = useMangoStore((s) => s.wallet.tokens)
const actions = useMangoStore((s) => s.actions)
const setMangoStore = useMangoStore((s) => s.set)
const [selectedAccount, setSelectedAccount] = useState(walletTokens[0])
// const prices = [] //useMangoStore((s) => s.selectedMangoGroup.prices)
@ -65,6 +66,19 @@ const DepositModal: FunctionComponent<DepositModalProps> = ({
}
}, [tokenSymbol, walletTokens])
useEffect(() => {
setMangoStore((state) => {
state.blurBackground = true
})
}, [])
const handleClose = () => {
setMangoStore((state) => {
state.blurBackground = false
})
onClose()
}
/* TODO: simulation
useEffect(() => {
if (!selectedMangoGroup || !selectedMangoAccount || !selectedAccount)
@ -243,7 +257,7 @@ const DepositModal: FunctionComponent<DepositModalProps> = ({
}, [maxButtonTransition])
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Modal isOpen={isOpen} onClose={handleClose}>
{!showSimulation ? (
<>
<Modal.Header>

View File

@ -1,9 +1,10 @@
import React from 'react'
import React, { useEffect } from 'react'
import styled from '@emotion/styled'
import Link from 'next/link'
import { EyeIcon, EyeOffIcon } from '@heroicons/react/outline'
import Modal from './Modal'
import useLocalStorageState from '../hooks/useLocalStorageState'
import useMangoStore from '../stores/useMangoStore'
import { LinkButton } from './Button'
const StyledColumnHeader = styled.span`
@ -17,12 +18,26 @@ const MarketsModal = ({
}: {
isOpen: boolean
markets: Array<any>
onClose?: (x) => void
onClose?: (x?) => void
}) => {
const [hiddenMarkets, setHiddenMarkets] = useLocalStorageState(
'hiddenMarkets',
[]
)
const setMangoStore = useMangoStore((s) => s.set)
useEffect(() => {
setMangoStore((state) => {
state.blurBackground = true
})
}, [])
const handleClose = () => {
setMangoStore((state) => {
state.blurBackground = false
})
onClose()
}
const handleHideShowMarket = (asset) => {
if (hiddenMarkets.includes(asset)) {
@ -33,7 +48,7 @@ const MarketsModal = ({
}
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Modal isOpen={isOpen} onClose={handleClose}>
<div className="flex items-end justify-between pb-3 pt-2">
<div className="font-bold text-lg text-th-fgd-1">Markets</div>
<LinkButton

View File

@ -65,6 +65,7 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
const [maxButtonTransition, setMaxButtonTransition] = useState(false)
const actions = useMangoStore((s) => s.actions)
const setMangoStore = useMangoStore((s) => s.set)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const selectedMangoAccount = useMangoStore(
(s) => s.selectedMangoAccount.current
@ -351,6 +352,19 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
}
}, [maxButtonTransition])
useEffect(() => {
setMangoStore((state) => {
state.blurBackground = true
})
}, [])
const handleClose = () => {
setMangoStore((state) => {
state.blurBackground = false
})
onClose()
}
// turn on borrow toggle when asset balance is zero
// useEffect(() => {
// if (withdrawTokenSymbol && getDepositsForSelectedAsset() === 0) {
@ -361,7 +375,7 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
if (!withdrawTokenSymbol) return null
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Modal isOpen={isOpen} onClose={handleClose}>
<>
{!showSimulation ? (
<>

View File

@ -1,29 +1,30 @@
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useCallback, 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 Link from 'next/link'
import {
ArrowSmDownIcon,
ChartBarIcon,
CurrencyDollarIcon,
ExclamationIcon,
DotsHorizontalIcon,
HeartIcon,
XIcon,
} from '@heroicons/react/outline'
import {
getTokenBySymbol,
getMarketByPublicKey,
I80F48,
nativeI80F48ToUi,
PerpMarket,
} from '@blockworks-foundation/mango-client'
import useMangoStore from '../../stores/useMangoStore'
import { getTokenBySymbol, I80F48 } from '@blockworks-foundation/mango-client'
import useMangoStore, { mangoClient } from '../../stores/useMangoStore'
import { useBalances } from '../../hooks/useBalances'
import { useSortableData } from '../../hooks/useSortableData'
import { usdFormatter, tokenPrecision } from '../../utils'
import { sleep, usdFormatter, tokenPrecision } from '../../utils'
import { notify } from '../../utils/notifications'
import { Market } from '@project-serum/serum'
import SideBadge from '../SideBadge'
import Button, { LinkButton } from '../Button'
import Switch from '../Switch'
import PositionsTable from '../PositionsTable'
import DepositModal from '../DepositModal'
import WithdrawModal from '../WithdrawModal'
const StyledAccountValue = styled.div`
font-size: 1.8rem;
@ -32,11 +33,14 @@ const StyledAccountValue = styled.div`
export default function AccountOverview() {
const [spotPortfolio, setSpotPortfolio] = useState([])
const [perpPositions, setPerpPositions] = useState([])
const [unsettled, setUnsettled] = useState([])
const [filteredSpotPortfolio, setFilteredSpotPortfolio] = useState([])
const [showZeroBalances, setShowZeroBalances] = useState(false)
const allMarkets = useMangoStore((s) => s.selectedMangoGroup.markets)
const [showDepositModal, setShowDepositModal] = useState(false)
const [showWithdrawModal, setShowWithdrawModal] = useState(false)
const [actionSymbol, setActionSymbol] = useState('')
const balances = useBalances()
const actions = useMangoStore((s) => s.actions)
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
@ -45,62 +49,13 @@ export default function AccountOverview() {
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 = []
const unsettled = []
balances.forEach((b) => {
const token = getTokenBySymbol(groupConfig, b.symbol)
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
if (+b.marginDeposits > 0) {
if (+b.marginDeposits > 0 || b.orders > 0) {
spotPortfolio.push({
market: b.symbol,
balance: +b.marginDeposits + b.orders + b.unsettled,
@ -148,6 +103,16 @@ export default function AccountOverview() {
type: '',
})
}
if (b.unsettled > 0) {
unsettled.push({
market: b.symbol,
balance: b.unsettled,
symbol: b.symbol,
value:
b.unsettled *
mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
})
}
})
setSpotPortfolio(spotPortfolio.sort((a, b) => b.value - a.value))
setFilteredSpotPortfolio(
@ -155,6 +120,7 @@ export default function AccountOverview() {
.filter((pos) => pos.balance > 0)
.sort((a, b) => b.value - a.value)
)
setUnsettled(unsettled)
}, [])
const handleShowZeroBalances = (checked) => {
@ -166,20 +132,75 @@ export default function AccountOverview() {
setShowZeroBalances(checked)
}
async function handleSettleAll() {
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const markets = useMangoStore.getState().selectedMangoGroup.markets
const wallet = useMangoStore.getState().wallet.current
try {
const spotMarkets = Object.values(markets).filter(
(mkt) => mkt instanceof Market
) as Market[]
await mangoClient.settleAll(mangoGroup, mangoAccount, spotMarkets, wallet)
notify({ title: 'Successfully settled funds' })
await sleep(250)
actions.fetchMangoAccounts()
} catch (e) {
console.warn('Error settling all:', e)
if (e.message === 'No unsettled funds') {
notify({
title: 'There are no unsettled funds',
type: 'error',
})
} else {
notify({
title: 'Error settling funds',
description: e.message,
txid: e.txid,
type: 'error',
})
}
}
}
const handleOpenDepositModal = useCallback((symbol) => {
setActionSymbol(symbol)
setShowDepositModal(true)
}, [])
const handleOpenWithdrawModal = useCallback((symbol) => {
setActionSymbol(symbol)
setShowWithdrawModal(true)
}, [])
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 className="grid grid-flow-col grid-cols-1 grid-rows-2 md:grid-cols-2 md:grid-rows-1 gap-4 pb-4">
<div className="border border-th-bkg-4 p-4 rounded-lg">
<div className="pb-2 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="font-bold text-th-fgd-1">
{usdFormatter.format(
+mangoAccount.computeValue(mangoGroup, mangoCache).toFixed(2)
)}
</StyledAccountValue>
</div>
</div>
<div className="border border-th-bkg-4 p-4 rounded-lg">
<div className="pb-2 text-th-fgd-3">PNL</div>
<div className="flex items-center">
<ChartBarIcon className="flex-shrink-0 h-7 w-7 mr-1.5 text-th-primary" />
<StyledAccountValue className="font-bold text-th-fgd-1">
{usdFormatter.format(
+mangoAccount.computeValue(mangoGroup, mangoCache).toFixed(2)
)}
</StyledAccountValue>
</div>
</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="grid grid-flow-col grid-cols-1 grid-rows-4 md:grid-cols-2 md:grid-rows-2 lg:grid-cols-4 lg:grid-rows-1 gap-4 pb-8">
<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">
@ -225,6 +246,40 @@ export default function AccountOverview() {
</div>
</div>
</div>
{unsettled.length > 0 ? (
<div className="border border-th-primary rounded-lg mb-8 p-6">
<div className="flex items-center justify-between pb-4">
<div className="flex items-center text-lg">
<ExclamationIcon className="h-5 mr-1.5 mt-0.5 text-th-primary w-5" />
Unsettled Balances
</div>
<Button
className="text-xs pt-0 pb-0 h-8 pl-3 pr-3"
onClick={handleSettleAll}
>
Settle All
</Button>
</div>
{unsettled.map((a) => (
<div
className="border-b border-th-bkg-4 flex items-center justify-between py-4 last:border-b-0 last:pb-0"
key={a.symbol}
>
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${a.symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{a.symbol}</div>
</div>
{a.balance.toFixed(tokenPrecision[a.symbol])}
</div>
))}
</div>
) : null}
<div className="pb-8">
<div className="pb-4 text-th-fgd-1 text-lg">Perp Positions</div>
<PositionsTable />
@ -244,9 +299,9 @@ export default function AccountOverview() {
<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`}>
<Th scope="col" className={`px-6 py-2 text-left`}>
<LinkButton
className="flex items-center no-underline"
className="flex font-normal items-center no-underline"
onClick={() => requestSort('market')}
>
Asset
@ -261,9 +316,9 @@ export default function AccountOverview() {
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left font-normal`}>
<Th scope="col" className={`px-6 py-2 text-left`}>
<LinkButton
className="flex items-center no-underline"
className="flex font-normal items-center no-underline"
onClick={() => requestSort('type')}
>
Type
@ -278,9 +333,9 @@ export default function AccountOverview() {
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left font-normal`}>
<Th scope="col" className={`px-6 py-2 text-left`}>
<LinkButton
className="flex items-center no-underline"
className="flex font-normal items-center no-underline"
onClick={() => requestSort('balance')}
>
Balance
@ -295,9 +350,9 @@ export default function AccountOverview() {
/>
</LinkButton>
</Th>
<Th scope="col" className={`px-6 py-2 text-left font-normal`}>
<Th scope="col" className={`px-6 py-2 text-left`}>
<LinkButton
className="flex items-center no-underline"
className="flex font-normal items-center no-underline"
onClick={() => requestSort('price')}
>
Price
@ -312,9 +367,9 @@ export default function AccountOverview() {
/>
</LinkButton>
</Th>
<Th scope="col" className="px-6 py-2 text-left font-normal">
<Th scope="col" className="px-6 py-2 text-left">
<LinkButton
className="flex items-center no-underline"
className="flex font-normal items-center no-underline"
onClick={() => requestSort('value')}
>
Value
@ -329,9 +384,9 @@ export default function AccountOverview() {
/>
</LinkButton>
</Th>
<Th scope="col" className="px-6 py-2 text-left font-normal">
<Th scope="col" className="px-6 py-2 text-left">
<LinkButton
className="flex items-center no-underline"
className="flex font-normal items-center no-underline"
onClick={() => requestSort('depositRate')}
>
Deposit Rate
@ -346,9 +401,9 @@ export default function AccountOverview() {
/>
</LinkButton>
</Th>
<Th scope="col" className="px-6 py-2 text-left font-normal">
<Th scope="col" className="px-6 py-2 text-left">
<LinkButton
className="flex items-center no-underline"
className="flex font-normal items-center no-underline"
onClick={() => requestSort('borrowRate')}
>
Borrow Rate
@ -445,18 +500,24 @@ export default function AccountOverview() {
/>
{pos.symbol}
</div>
{pos.symbol !== 'USDC' ? (
<Menu.Item>
<Link
href={`/spot/${pos.symbol}`}
key={pos.symbol}
>
<a className="block font-normal p-2 rounded-none text-th-fgd-1 w-full hover:bg-th-bkg-2 hover:cursor-pointer hover:text-th-fgd-1 focus:outline-none">
<div className="text-left">Trade</div>
</a>
</Link>
</Menu.Item>
) : null}
<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')}
onClick={() =>
handleOpenDepositModal(pos.symbol)
}
>
<div className="text-left">Deposit</div>
</button>
@ -464,19 +525,13 @@ export default function AccountOverview() {
<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')}
onClick={() =>
handleOpenWithdrawModal(pos.symbol)
}
>
<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>
)}
@ -488,6 +543,20 @@ export default function AccountOverview() {
</Table>
</>
) : null}
{showDepositModal && (
<DepositModal
isOpen={showDepositModal}
onClose={() => setShowDepositModal(false)}
tokenSymbol={actionSymbol}
/>
)}
{showWithdrawModal && (
<WithdrawModal
isOpen={showWithdrawModal}
onClose={() => setShowWithdrawModal(false)}
tokenSymbol={actionSymbol}
/>
)}
</>
) : null
}

View File

@ -5,11 +5,13 @@ import '../node_modules/react-resizable/css/styles.css'
import '../styles/index.css'
import useWallet from '../hooks/useWallet'
import useHydrateStore from '../hooks/useHydrateStore'
import useMangoStore from '../stores/useMangoStore'
import Notifications from '../components/Notification'
function App({ Component, pageProps }) {
useHydrateStore()
useWallet()
const blurBackground = useMangoStore((s) => s.blurBackground)
return (
<>
@ -47,7 +49,15 @@ function App({ Component, pageProps }) {
<link rel="manifest" href="/manifest.json"></link>
</Head>
<ThemeProvider defaultTheme="Mango">
<Component {...pageProps} />
<div className="bg-th-bkg-1">
<div
className={`default-transition filter ${
blurBackground ? 'blur-sm' : 'blur-none'
}`}
>
<Component {...pageProps} />
</div>
</div>
<Notifications />
</ThemeProvider>
</>

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import {
CurrencyDollarIcon,
DuplicateIcon,
@ -6,14 +6,7 @@ import {
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'
@ -28,9 +21,9 @@ import Button from '../components/Button'
import EmptyState from '../components/EmptyState'
const TABS = [
'Overview',
'Assets',
'Borrows',
'Portfolio',
// 'Assets',
// 'Borrows',
// 'Stats',
// 'Positions',
'Orders',
@ -40,16 +33,10 @@ const TABS = [
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)
const wallet = useMangoStore((s) => s.wallet.current)
const handleTabChange = (tabName) => {
@ -59,78 +46,6 @@ 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)
@ -181,7 +96,7 @@ export default function Account() {
</div>
</Button>
<a
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"
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:text-th-fgd-1 hover:brightness-[1.15] focus:outline-none"
href={`https://explorer.solana.com/address/${mangoAccount?.publicKey}`}
target="_blank"
rel="noopener noreferrer"
@ -193,10 +108,7 @@ export default function Account() {
className="text-xs flex flex-grow items-center justify-center ml-2 pt-0 pb-0 h-8 pl-3 pr-3"
onClick={() => setShowAccountsModal(true)}
>
<div className="flex items-center">
<CurrencyDollarIcon className="h-4 w-4 mr-1.5" />
Accounts
</div>
Accounts
</Button>
</div>
</>
@ -264,7 +176,7 @@ export default function Account() {
const TabContent = ({ activeTab }) => {
switch (activeTab) {
case 'Overview':
case 'Portfolio':
return <AccountOverview />
case 'Assets':
return <AccountAssets />

View File

@ -158,6 +158,7 @@ interface MangoStore extends State {
uiLocked: boolean
}
tradeHistory: any[]
blurBackground: boolean
set: (x: any) => void
actions: {
[key: string]: (args?) => void
@ -213,6 +214,7 @@ const useMangoStore = create<MangoStore>((set, get) => ({
uiLocked: true,
},
tradeHistory: [],
blurBackground: false,
set: (fn) => set(produce(fn)),
actions: {
async fetchWalletTokens() {

View File

@ -78,7 +78,7 @@ button {
}
.default-transition {
@apply transition-all duration-500;
@apply transition-all duration-300;
}
.tiny-text {