2021-08-05 07:07:29 -07:00
|
|
|
|
import { useEffect, useCallback, useMemo, useState } from 'react'
|
2021-07-25 06:54:25 -07:00
|
|
|
|
import { Table, Thead, Tbody, Tr, Th, Td } from 'react-super-responsive-table'
|
|
|
|
|
import styled from '@emotion/styled'
|
2021-07-29 06:19:32 -07:00
|
|
|
|
import { Menu } from '@headlessui/react'
|
2021-08-01 05:48:15 -07:00
|
|
|
|
import Link from 'next/link'
|
2021-07-29 06:19:32 -07:00
|
|
|
|
import {
|
2021-08-01 05:48:15 -07:00
|
|
|
|
ChartBarIcon,
|
2021-07-29 06:19:32 -07:00
|
|
|
|
CurrencyDollarIcon,
|
2021-08-01 05:48:15 -07:00
|
|
|
|
ExclamationIcon,
|
2021-07-29 06:19:32 -07:00
|
|
|
|
DotsHorizontalIcon,
|
2021-08-05 07:07:29 -07:00
|
|
|
|
GiftIcon,
|
2021-07-29 06:19:32 -07:00
|
|
|
|
HeartIcon,
|
|
|
|
|
XIcon,
|
|
|
|
|
} from '@heroicons/react/outline'
|
2021-08-14 14:14:43 -07:00
|
|
|
|
import { ArrowSmDownIcon } from '@heroicons/react/solid'
|
2021-08-05 07:07:29 -07:00
|
|
|
|
import {
|
|
|
|
|
getTokenBySymbol,
|
|
|
|
|
nativeToUi,
|
|
|
|
|
ZERO_BN,
|
|
|
|
|
I80F48,
|
|
|
|
|
} from '@blockworks-foundation/mango-client'
|
|
|
|
|
import useMangoStore, {
|
|
|
|
|
mangoClient,
|
|
|
|
|
MNGO_INDEX,
|
|
|
|
|
} from '../../stores/useMangoStore'
|
2021-07-25 06:54:25 -07:00
|
|
|
|
import { useBalances } from '../../hooks/useBalances'
|
2021-07-29 06:19:32 -07:00
|
|
|
|
import { useSortableData } from '../../hooks/useSortableData'
|
2021-08-02 05:44:58 -07:00
|
|
|
|
import useLocalStorageState from '../../hooks/useLocalStorageState'
|
2021-08-15 06:31:59 -07:00
|
|
|
|
import { sleep, tokenPrecision, formatUsdValue } from '../../utils'
|
2021-08-01 05:48:15 -07:00
|
|
|
|
import { notify } from '../../utils/notifications'
|
|
|
|
|
import { Market } from '@project-serum/serum'
|
2021-07-25 06:54:25 -07:00
|
|
|
|
import SideBadge from '../SideBadge'
|
2021-07-29 06:19:32 -07:00
|
|
|
|
import Button, { LinkButton } from '../Button'
|
|
|
|
|
import Switch from '../Switch'
|
2021-08-15 10:06:52 -07:00
|
|
|
|
import PositionsTable from '../PerpPositionsTable'
|
2021-08-01 05:48:15 -07:00
|
|
|
|
import DepositModal from '../DepositModal'
|
|
|
|
|
import WithdrawModal from '../WithdrawModal'
|
2021-07-25 06:54:25 -07:00
|
|
|
|
|
|
|
|
|
const StyledAccountValue = styled.div`
|
|
|
|
|
font-size: 1.8rem;
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
export default function AccountOverview() {
|
2021-07-29 06:19:32 -07:00
|
|
|
|
const [spotPortfolio, setSpotPortfolio] = useState([])
|
2021-08-01 05:48:15 -07:00
|
|
|
|
const [unsettled, setUnsettled] = useState([])
|
2021-07-29 06:19:32 -07:00
|
|
|
|
const [filteredSpotPortfolio, setFilteredSpotPortfolio] = useState([])
|
2021-08-02 05:44:58 -07:00
|
|
|
|
const [showZeroBalances, setShowZeroBalances] = useLocalStorageState(
|
|
|
|
|
'showZeroAccountBalances',
|
|
|
|
|
false
|
|
|
|
|
)
|
2021-08-01 05:48:15 -07:00
|
|
|
|
const [showDepositModal, setShowDepositModal] = useState(false)
|
|
|
|
|
const [showWithdrawModal, setShowWithdrawModal] = useState(false)
|
|
|
|
|
const [actionSymbol, setActionSymbol] = useState('')
|
2021-07-25 06:54:25 -07:00
|
|
|
|
const balances = useBalances()
|
2021-08-01 05:48:15 -07:00
|
|
|
|
const actions = useMangoStore((s) => s.actions)
|
2021-07-25 06:54:25 -07:00
|
|
|
|
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)
|
2021-07-29 06:19:32 -07:00
|
|
|
|
const { items, requestSort, sortConfig } = useSortableData(
|
|
|
|
|
filteredSpotPortfolio
|
|
|
|
|
)
|
2021-07-25 06:54:25 -07:00
|
|
|
|
|
2021-07-29 06:19:32 -07:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
const spotPortfolio = []
|
2021-08-01 05:48:15 -07:00
|
|
|
|
const unsettled = []
|
2021-07-25 06:54:25 -07:00
|
|
|
|
balances.forEach((b) => {
|
|
|
|
|
const token = getTokenBySymbol(groupConfig, b.symbol)
|
|
|
|
|
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
|
2021-08-01 05:48:15 -07:00
|
|
|
|
if (+b.marginDeposits > 0 || b.orders > 0) {
|
2021-07-29 06:19:32 -07:00
|
|
|
|
spotPortfolio.push({
|
2021-07-25 06:54:25 -07:00
|
|
|
|
market: b.symbol,
|
|
|
|
|
balance: +b.marginDeposits + b.orders + b.unsettled,
|
2021-07-29 06:19:32 -07:00
|
|
|
|
borrowRate: mangoGroup
|
|
|
|
|
.getBorrowRate(tokenIndex)
|
|
|
|
|
.mul(I80F48.fromNumber(100)),
|
|
|
|
|
depositRate: mangoGroup
|
|
|
|
|
.getDepositRate(tokenIndex)
|
|
|
|
|
.mul(I80F48.fromNumber(100)),
|
|
|
|
|
price: mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
|
2021-07-25 06:54:25 -07:00
|
|
|
|
symbol: b.symbol,
|
|
|
|
|
value:
|
|
|
|
|
(+b.marginDeposits + b.orders + b.unsettled) *
|
|
|
|
|
mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
|
2021-07-29 06:19:32 -07:00
|
|
|
|
type: 'Deposit',
|
2021-07-25 06:54:25 -07:00
|
|
|
|
})
|
2021-07-29 06:19:32 -07:00
|
|
|
|
} else if (+b.borrows > 0) {
|
|
|
|
|
spotPortfolio.push({
|
2021-07-25 06:54:25 -07:00
|
|
|
|
market: b.symbol,
|
|
|
|
|
balance: +b.borrows,
|
2021-07-29 06:19:32 -07:00
|
|
|
|
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,
|
2021-07-25 06:54:25 -07:00
|
|
|
|
value: b.borrows.mul(mangoGroup.getPrice(tokenIndex, mangoCache)),
|
2021-07-29 06:19:32 -07:00
|
|
|
|
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: '–',
|
2021-07-25 06:54:25 -07:00
|
|
|
|
})
|
|
|
|
|
}
|
2021-08-01 05:48:15 -07:00
|
|
|
|
if (b.unsettled > 0) {
|
|
|
|
|
unsettled.push({
|
|
|
|
|
market: b.symbol,
|
|
|
|
|
balance: b.unsettled,
|
|
|
|
|
symbol: b.symbol,
|
|
|
|
|
value:
|
|
|
|
|
b.unsettled *
|
|
|
|
|
mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-07-25 06:54:25 -07:00
|
|
|
|
})
|
2021-07-29 06:19:32 -07:00
|
|
|
|
setSpotPortfolio(spotPortfolio.sort((a, b) => b.value - a.value))
|
|
|
|
|
setFilteredSpotPortfolio(
|
2021-08-02 05:44:58 -07:00
|
|
|
|
!showZeroBalances
|
|
|
|
|
? spotPortfolio
|
|
|
|
|
.filter((pos) => pos.balance > 0)
|
|
|
|
|
.sort((a, b) => b.value - a.value)
|
|
|
|
|
: spotPortfolio.sort((a, b) => b.value - a.value)
|
2021-07-29 06:19:32 -07:00
|
|
|
|
)
|
2021-08-02 05:44:58 -07:00
|
|
|
|
|
2021-08-01 05:48:15 -07:00
|
|
|
|
setUnsettled(unsettled)
|
2021-08-02 05:44:58 -07:00
|
|
|
|
}, [mangoAccount])
|
2021-07-29 06:19:32 -07:00
|
|
|
|
|
2021-08-05 07:07:29 -07:00
|
|
|
|
const maintHealthRatio = useMemo(() => {
|
|
|
|
|
return mangoAccount
|
|
|
|
|
? mangoAccount.getHealthRatio(mangoGroup, mangoCache, 'Maint')
|
|
|
|
|
: 100
|
|
|
|
|
}, [mangoAccount, mangoGroup, mangoCache])
|
|
|
|
|
|
|
|
|
|
const initHealthRatio = useMemo(() => {
|
|
|
|
|
return mangoAccount
|
|
|
|
|
? mangoAccount.getHealthRatio(mangoGroup, mangoCache, 'Init')
|
|
|
|
|
: 100
|
|
|
|
|
}, [mangoAccount, mangoGroup, mangoCache])
|
|
|
|
|
|
|
|
|
|
const mngoAccrued = useMemo(() => {
|
|
|
|
|
return mangoAccount
|
|
|
|
|
? mangoAccount.perpAccounts.reduce((acc, perpAcct) => {
|
|
|
|
|
return perpAcct.mngoAccrued.add(acc)
|
|
|
|
|
}, ZERO_BN)
|
|
|
|
|
: ZERO_BN
|
|
|
|
|
}, [mangoAccount])
|
|
|
|
|
|
2021-07-29 06:19:32 -07:00
|
|
|
|
const handleShowZeroBalances = (checked) => {
|
|
|
|
|
if (checked) {
|
|
|
|
|
setFilteredSpotPortfolio(spotPortfolio)
|
|
|
|
|
} else {
|
|
|
|
|
setFilteredSpotPortfolio(spotPortfolio.filter((pos) => pos.balance > 0))
|
|
|
|
|
}
|
|
|
|
|
setShowZeroBalances(checked)
|
|
|
|
|
}
|
2021-07-25 06:54:25 -07:00
|
|
|
|
|
2021-08-01 05:48:15 -07:00
|
|
|
|
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[]
|
2021-08-14 14:14:43 -07:00
|
|
|
|
// @ts-ignore
|
2021-08-01 05:48:15 -07:00
|
|
|
|
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',
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-05 07:07:29 -07:00
|
|
|
|
const handleRedeemMngo = async () => {
|
|
|
|
|
const wallet = useMangoStore.getState().wallet.current
|
|
|
|
|
const mngoNodeBank =
|
|
|
|
|
mangoGroup.rootBankAccounts[MNGO_INDEX].nodeBankAccounts[0]
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const txid = await mangoClient.redeemAllMngo(
|
|
|
|
|
mangoGroup,
|
|
|
|
|
mangoAccount,
|
|
|
|
|
wallet,
|
|
|
|
|
mangoGroup.tokens[MNGO_INDEX].rootBank,
|
|
|
|
|
mngoNodeBank.publicKey,
|
|
|
|
|
mngoNodeBank.vault
|
|
|
|
|
)
|
|
|
|
|
actions.fetchMangoAccounts()
|
|
|
|
|
notify({
|
|
|
|
|
title: 'Successfully redeemed MNGO',
|
|
|
|
|
description: '',
|
|
|
|
|
txid,
|
|
|
|
|
})
|
|
|
|
|
} catch (e) {
|
|
|
|
|
notify({
|
|
|
|
|
title: 'Error redeeming MNGO',
|
|
|
|
|
description: e.message,
|
|
|
|
|
txid: e.txid,
|
|
|
|
|
type: 'error',
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-01 05:48:15 -07:00
|
|
|
|
const handleOpenDepositModal = useCallback((symbol) => {
|
|
|
|
|
setActionSymbol(symbol)
|
|
|
|
|
setShowDepositModal(true)
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
const handleOpenWithdrawModal = useCallback((symbol) => {
|
|
|
|
|
setActionSymbol(symbol)
|
|
|
|
|
setShowWithdrawModal(true)
|
|
|
|
|
}, [])
|
|
|
|
|
|
2021-07-25 06:54:25 -07:00
|
|
|
|
return mangoAccount ? (
|
|
|
|
|
<>
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<div className="grid grid-flow-col grid-cols-1 grid-rows-4 md:grid-cols-4 md:grid-rows-1 gap-4 pb-8">
|
2021-08-01 05:48:15 -07:00
|
|
|
|
<div className="border border-th-bkg-4 p-4 rounded-lg">
|
|
|
|
|
<div className="pb-2 text-th-fgd-3">Account Value</div>
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<div className="flex items-center pb-3">
|
2021-08-01 05:48:15 -07:00
|
|
|
|
<CurrencyDollarIcon className="flex-shrink-0 h-7 w-7 mr-1.5 text-th-primary" />
|
|
|
|
|
<StyledAccountValue className="font-bold text-th-fgd-1">
|
2021-08-15 06:31:59 -07:00
|
|
|
|
{formatUsdValue(
|
|
|
|
|
+mangoAccount.computeValue(mangoGroup, mangoCache)
|
2021-08-01 05:48:15 -07:00
|
|
|
|
)}
|
|
|
|
|
</StyledAccountValue>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="border border-th-bkg-4 p-4 rounded-lg">
|
|
|
|
|
<div className="pb-2 text-th-fgd-3">PNL</div>
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<div className="flex items-center pb-3">
|
2021-08-01 05:48:15 -07:00
|
|
|
|
<ChartBarIcon className="flex-shrink-0 h-7 w-7 mr-1.5 text-th-primary" />
|
|
|
|
|
<StyledAccountValue className="font-bold text-th-fgd-1">
|
2021-08-15 06:31:59 -07:00
|
|
|
|
{formatUsdValue(
|
|
|
|
|
+mangoAccount.computeValue(mangoGroup, mangoCache)
|
2021-08-01 05:48:15 -07:00
|
|
|
|
)}
|
|
|
|
|
</StyledAccountValue>
|
|
|
|
|
</div>
|
2021-07-25 06:54:25 -07:00
|
|
|
|
</div>
|
2021-07-29 06:19:32 -07:00
|
|
|
|
<div className="border border-th-bkg-4 p-4 rounded-lg">
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<div className="pb-2 text-th-fgd-3">Health Ratio</div>
|
|
|
|
|
<div className="flex items-center pb-4">
|
|
|
|
|
<HeartIcon className="flex-shrink-0 h-7 w-7 mr-1.5 text-th-primary" />
|
|
|
|
|
<StyledAccountValue className="font-bold text-th-fgd-1">
|
|
|
|
|
{maintHealthRatio.toFixed(2)}%
|
|
|
|
|
</StyledAccountValue>
|
2021-07-25 06:54:25 -07:00
|
|
|
|
</div>
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<div className="h-1.5 flex rounded bg-th-bkg-3">
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
width: `${maintHealthRatio}%`,
|
|
|
|
|
}}
|
|
|
|
|
className={`flex rounded ${
|
|
|
|
|
maintHealthRatio > 30
|
|
|
|
|
? 'bg-th-green'
|
|
|
|
|
: initHealthRatio > 0
|
|
|
|
|
? 'bg-th-orange'
|
|
|
|
|
: 'bg-th-red'
|
|
|
|
|
}`}
|
|
|
|
|
></div>
|
2021-07-25 06:54:25 -07:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2021-07-29 06:19:32 -07:00
|
|
|
|
<div className="border border-th-bkg-4 p-4 rounded-lg">
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<div className="pb-2 text-th-fgd-3">MNGO Rewards</div>
|
|
|
|
|
<div className="flex items-center pb-2">
|
|
|
|
|
<GiftIcon className="flex-shrink-0 h-7 w-7 mr-1.5 text-th-primary" />
|
|
|
|
|
<StyledAccountValue className="font-bold text-th-fgd-1">
|
|
|
|
|
{mangoGroup
|
|
|
|
|
? nativeToUi(
|
|
|
|
|
mngoAccrued.toNumber(),
|
|
|
|
|
mangoGroup.tokens[MNGO_INDEX].decimals
|
|
|
|
|
)
|
|
|
|
|
: 0}
|
|
|
|
|
</StyledAccountValue>
|
2021-07-25 06:54:25 -07:00
|
|
|
|
</div>
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<LinkButton
|
|
|
|
|
onClick={handleRedeemMngo}
|
|
|
|
|
disabled={mngoAccrued.eq(ZERO_BN)}
|
|
|
|
|
className="text-th-primary text-xs"
|
|
|
|
|
>
|
|
|
|
|
Claim Reward
|
|
|
|
|
</LinkButton>
|
2021-07-25 06:54:25 -07:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2021-08-01 05:48:15 -07:00
|
|
|
|
{unsettled.length > 0 ? (
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<div className="border border-th-bkg-3 rounded-lg mb-8 p-6">
|
2021-08-01 05:48:15 -07:00
|
|
|
|
<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}
|
2021-07-29 06:19:32 -07:00
|
|
|
|
<div className="pb-8">
|
|
|
|
|
<div className="pb-4 text-th-fgd-1 text-lg">Perp Positions</div>
|
|
|
|
|
<PositionsTable />
|
|
|
|
|
</div>
|
2021-08-05 07:07:29 -07:00
|
|
|
|
<div className="pb-4 text-th-fgd-1 text-lg">Assets & Liabilities</div>
|
|
|
|
|
|
|
|
|
|
<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-0.5 text-xs text-th-fgd-3">Total Assets Value</div>
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<div className="text-lg text-th-fgd-1">
|
2021-08-15 06:31:59 -07:00
|
|
|
|
{formatUsdValue(
|
2021-08-05 07:07:29 -07:00
|
|
|
|
+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">
|
|
|
|
|
Total Liabilities Value
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center">
|
|
|
|
|
<div className="text-lg text-th-fgd-1">
|
2021-08-15 06:31:59 -07:00
|
|
|
|
{formatUsdValue(
|
2021-08-05 07:07:29 -07:00
|
|
|
|
+mangoAccount.getLiabsVal(mangoGroup, mangoCache)
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex justify-end pb-4">
|
2021-08-02 05:44:58 -07:00
|
|
|
|
<Switch
|
|
|
|
|
checked={showZeroBalances}
|
|
|
|
|
className="text-xs"
|
|
|
|
|
onChange={handleShowZeroBalances}
|
|
|
|
|
>
|
|
|
|
|
Show zero balances
|
|
|
|
|
</Switch>
|
|
|
|
|
</div>
|
|
|
|
|
{filteredSpotPortfolio.length > 0 ? (
|
|
|
|
|
<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`}>
|
|
|
|
|
<LinkButton
|
|
|
|
|
className="flex font-normal 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`}>
|
|
|
|
|
<LinkButton
|
|
|
|
|
className="flex font-normal 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`}>
|
|
|
|
|
<LinkButton
|
|
|
|
|
className="flex font-normal 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`}>
|
|
|
|
|
<LinkButton
|
|
|
|
|
className="flex font-normal 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">
|
|
|
|
|
<LinkButton
|
|
|
|
|
className="flex font-normal 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">
|
|
|
|
|
<LinkButton
|
|
|
|
|
className="flex font-normal 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">
|
|
|
|
|
<LinkButton
|
|
|
|
|
className="flex font-normal 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
|
2021-07-25 06:54:25 -07:00
|
|
|
|
${i % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
|
|
|
|
|
`}
|
2021-08-02 05:44:58 -07:00
|
|
|
|
>
|
|
|
|
|
<Td
|
|
|
|
|
className={`px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
|
2021-07-25 06:54:25 -07:00
|
|
|
|
>
|
2021-08-02 05:44:58 -07:00
|
|
|
|
<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`}
|
|
|
|
|
>
|
2021-08-15 06:31:59 -07:00
|
|
|
|
{formatUsdValue(pos.price)}
|
2021-08-02 05:44:58 -07:00
|
|
|
|
</Td>
|
|
|
|
|
<Td
|
|
|
|
|
className={`px-6 py-2 whitespace-nowrap text-sm text-th-fgd-1`}
|
|
|
|
|
>
|
2021-08-15 06:31:59 -07:00
|
|
|
|
{formatUsdValue(pos.value)}
|
2021-08-02 05:44:58 -07:00
|
|
|
|
</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>
|
|
|
|
|
{pos.symbol !== 'USDC' ? (
|
2021-07-29 06:19:32 -07:00
|
|
|
|
<Menu.Item>
|
2021-08-02 05:44:58 -07:00
|
|
|
|
<Link
|
|
|
|
|
href={`/spot/${pos.symbol}`}
|
|
|
|
|
key={pos.symbol}
|
2021-07-29 06:19:32 -07:00
|
|
|
|
>
|
2021-08-02 05:44:58 -07:00
|
|
|
|
<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>
|
2021-07-29 06:19:32 -07:00
|
|
|
|
</Menu.Item>
|
2021-08-02 05:44:58 -07:00
|
|
|
|
) : 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={() => handleOpenDepositModal(pos.symbol)}
|
|
|
|
|
>
|
|
|
|
|
<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={() =>
|
|
|
|
|
handleOpenWithdrawModal(pos.symbol)
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<div className="text-left">Withdraw</div>
|
|
|
|
|
</button>
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
</Menu.Items>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</Menu>
|
|
|
|
|
</Td>
|
|
|
|
|
</Tr>
|
|
|
|
|
))}
|
|
|
|
|
</Tbody>
|
|
|
|
|
</Table>
|
|
|
|
|
) : (
|
|
|
|
|
<div
|
|
|
|
|
className={`w-full text-center py-6 bg-th-bkg-1 text-th-fgd-3 rounded-md`}
|
|
|
|
|
>
|
|
|
|
|
No assets
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2021-08-01 05:48:15 -07:00
|
|
|
|
{showDepositModal && (
|
|
|
|
|
<DepositModal
|
|
|
|
|
isOpen={showDepositModal}
|
|
|
|
|
onClose={() => setShowDepositModal(false)}
|
|
|
|
|
tokenSymbol={actionSymbol}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
{showWithdrawModal && (
|
|
|
|
|
<WithdrawModal
|
|
|
|
|
isOpen={showWithdrawModal}
|
|
|
|
|
onClose={() => setShowWithdrawModal(false)}
|
|
|
|
|
tokenSymbol={actionSymbol}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2021-07-25 06:54:25 -07:00
|
|
|
|
</>
|
|
|
|
|
) : null
|
|
|
|
|
}
|