WIP adds RedeemDropdown

This commit is contained in:
Kieran Gillen 2022-03-22 09:57:52 +01:00
parent c79e0869b5
commit 8f40cce851
9 changed files with 252 additions and 112 deletions

1
.gitignore vendored
View File

@ -25,6 +25,7 @@ yarn-debug.log*
yarn-error.log*
# local env files
.env
.env.local
.env.development.local
.env.test.local

View File

@ -360,101 +360,107 @@ const BalancesTable = ({
</TrHead>
</thead>
<tbody>
{items.map((balance, index) => (
<TrBody key={`${balance.symbol}${index}`}>
<Td>
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${balance.symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
{balance.symbol === 'USDC' ||
decodeURIComponent(asPath).includes(
`${balance.symbol}/USDC`
) ? (
<span>{balance.symbol}</span>
) : (
<Link
href={{
pathname: '/',
query: { name: `${balance.symbol}/USDC` },
}}
shallow={true}
>
<a className="text-th-fgd-1 underline hover:text-th-fgd-1 hover:no-underline">
{balance.symbol}
</a>
</Link>
)}
</div>
</Td>
<Td>{balance.deposits.toFormat(balance.decimals)}</Td>
<Td>{balance.borrows.toFormat(balance.decimals)}</Td>
<Td>{balance.orders}</Td>
<Td>{balance.unsettled}</Td>
<Td>
{marketConfig.kind === 'spot' &&
marketConfig.name.includes(balance.symbol) &&
selectedMarket &&
clickToPopulateTradeForm ? (
<span
className={
balance.net.toNumber() != 0
? 'cursor-pointer underline hover:no-underline'
: ''
}
onClick={() =>
handleSizeClick(balance.net, balance.symbol)
}
>
{balance.net.toFormat(balance.decimals)}
</span>
) : (
balance.net.toFormat(balance.decimals)
)}
</Td>
<Td>{formatUsdValue(balance.value.toNumber())}</Td>
<Td>
<span className="text-th-green">
{balance.depositRate.toFixed(2)}%
</span>
</Td>
<Td>
<span className="text-th-red">
{balance.borrowRate.toFixed(2)}%
</span>
</Td>
{showDepositWithdraw ? (
{items.map((balance, index) => {
console.log('balance', balance)
if (!balance) {
return null
}
return (
<TrBody key={`${balance.symbol}${index}`}>
<Td>
<div className="flex justify-end">
<Button
className="h-7 pt-0 pb-0 pl-3 pr-3 text-xs"
onClick={() =>
handleOpenDepositModal(balance.symbol)
}
>
{balance.borrows.toNumber() > 0
? t('repay')
: t('deposit')}
</Button>
<Button
className="ml-4 h-7 pt-0 pb-0 pl-3 pr-3 text-xs"
onClick={() =>
handleOpenWithdrawModal(balance.symbol)
}
disabled={!canWithdraw}
>
{t('withdraw')}
</Button>
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${balance.symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
{balance.symbol === 'USDC' ||
decodeURIComponent(asPath).includes(
`${balance.symbol}/USDC`
) ? (
<span>{balance.symbol}</span>
) : (
<Link
href={{
pathname: '/',
query: { name: `${balance.symbol}/USDC` },
}}
shallow={true}
>
<a className="text-th-fgd-1 underline hover:text-th-fgd-1 hover:no-underline">
{balance.symbol}
</a>
</Link>
)}
</div>
</Td>
) : null}
</TrBody>
))}
<Td>{balance.deposits.toFormat(balance.decimals)}</Td>
<Td>{balance.borrows.toFormat(balance.decimals)}</Td>
<Td>{balance.orders}</Td>
<Td>{balance.unsettled}</Td>
<Td>
{marketConfig.kind === 'spot' &&
marketConfig.name.includes(balance.symbol) &&
selectedMarket &&
clickToPopulateTradeForm ? (
<span
className={
balance.net.toNumber() != 0
? 'cursor-pointer underline hover:no-underline'
: ''
}
onClick={() =>
handleSizeClick(balance.net, balance.symbol)
}
>
{balance.net.toFormat(balance.decimals)}
</span>
) : (
balance.net.toFormat(balance.decimals)
)}
</Td>
<Td>{formatUsdValue(balance.value.toNumber())}</Td>
<Td>
<span className="text-th-green">
{balance.depositRate.toFixed(2)}%
</span>
</Td>
<Td>
<span className="text-th-red">
{balance.borrowRate.toFixed(2)}%
</span>
</Td>
{showDepositWithdraw ? (
<Td>
<div className="flex justify-end">
<Button
className="h-7 pt-0 pb-0 pl-3 pr-3 text-xs"
onClick={() =>
handleOpenDepositModal(balance.symbol)
}
>
{balance.borrows.toNumber() > 0
? t('repay')
: t('deposit')}
</Button>
<Button
className="ml-4 h-7 pt-0 pb-0 pl-3 pr-3 text-xs"
onClick={() =>
handleOpenWithdrawModal(balance.symbol)
}
disabled={!canWithdraw}
>
{t('withdraw')}
</Button>
</div>
</Td>
) : null}
</TrBody>
)
})}
</tbody>
{showDepositModal && (
<DepositModal

View File

@ -38,6 +38,12 @@ const MarketNavItem: FunctionComponent<MarketNavItemProps> = ({
mangoGroupConfig,
market.baseSymbol
)
// The following if statement is for markets not on devnet
if (!mangoGroup.spotMarkets[marketIndex]) {
return 1
}
const ws = getWeights(mangoGroup, marketIndex, 'Init')
const w = market.name.includes('PERP')
? ws.perpAssetWeight

View File

@ -21,6 +21,45 @@ import { breakpoints } from './TradePageGrid'
import { useTranslation } from 'next-i18next'
import useMangoAccount from '../hooks/useMangoAccount'
export const settlePosPnl = async (
perpMarkets: PerpMarket[],
perpAccount: PerpAccount,
t,
mangoAccounts: MangoAccount[] | undefined
) => {
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const mangoCache = useMangoStore.getState().selectedMangoGroup.cache
const wallet = useMangoStore.getState().wallet.current
const actions = useMangoStore.getState().actions
const mangoClient = useMangoStore.getState().connection.client
try {
await mangoClient.settlePosPnl(
mangoGroup,
mangoCache,
mangoAccount,
perpMarkets,
mangoGroup.rootBankAccounts[QUOTE_INDEX],
wallet,
mangoAccounts
)
actions.reloadMangoAccount()
notify({
title: t('pnl-success'),
description: '',
})
} catch (e) {
console.log('Error settling PNL: ', `${e}`, `${perpAccount}`)
notify({
title: t('pnl-error'),
description: e.message,
txid: e.txid,
type: 'error',
})
}
}
export const settlePnl = async (
perpMarket: PerpMarket,
perpAccount: PerpAccount,

View File

@ -5,7 +5,7 @@ import { useTranslation } from 'next-i18next'
import { ExclamationIcon } from '@heroicons/react/outline'
import useMangoStore from '../stores/useMangoStore'
import Button, { LinkButton } from '../components/Button'
import { LinkButton } from '../components/Button'
import { useViewport } from '../hooks/useViewport'
import { breakpoints } from './TradePageGrid'
import { ExpandableRow, Table, Td, Th, TrBody, TrHead } from './TableElements'
@ -16,11 +16,10 @@ import PerpSideBadge from './PerpSideBadge'
import PnlText from './PnlText'
import { settlePnl } from './MarketPosition'
import MobileTableHeader from './mobile/MobileTableHeader'
import { RedeemDropdown } from 'components/perp_positions'
const PositionsTable = () => {
const { t } = useTranslation('common')
const { reloadMangoAccount } = useMangoStore((s) => s.actions)
const [settling, setSettling] = useState(false)
const [settleSinglePos, setSettleSinglePos] = useState(null)
const selectedMarket = useMangoStore((s) => s.selectedMarket.current)
@ -53,16 +52,6 @@ const PositionsTable = () => {
})
}
const handleSettleAll = async () => {
setSettling(true)
for (const p of unsettledPositions) {
await settlePnl(p.perpMarket, p.perpAccount, t, undefined)
}
reloadMangoAccount()
setSettling(false)
}
const handleSettlePnl = async (perpMarket, perpAccount, index) => {
setSettleSinglePos(index)
await settlePnl(perpMarket, perpAccount, t, undefined)
@ -79,12 +68,7 @@ const PositionsTable = () => {
<h3>{t('unsettled-positions')}</h3>
</div>
<Button
className="h-8 whitespace-nowrap pt-0 pb-0 pl-3 pr-3 text-xs"
onClick={handleSettleAll}
>
{settling ? <Loading /> : t('redeem-all')}
</Button>
<RedeemDropdown />
</div>
<div className="grid grid-flow-row grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
{unsettledPositions.map((p, index) => {

View File

@ -0,0 +1,101 @@
import { ChevronDownIcon } from '@heroicons/react/solid'
import React, { Fragment, useState } from 'react'
import { settlePnl, settlePosPnl } from 'components/MarketPosition'
import Button from 'components/Button'
import { Transition } from '@headlessui/react'
import { useTranslation } from 'next-i18next'
import Loading from 'components/Loading'
import useMangoStore from 'stores/useMangoStore'
const MenuButton = ({ onClick, text }) => {
return (
<div
className="default-transition flex items-center justify-end whitespace-nowrap pb-2.5 text-xs tracking-wider text-th-fgd-1 hover:cursor-pointer hover:text-th-primary"
onClick={onClick}
>
{text}
</div>
)
}
export const RedeemDropdown: React.FC = () => {
const { t } = useTranslation('common')
const { reloadMangoAccount } = useMangoStore((s) => s.actions)
const [settling, setSettling] = useState(false)
const [settlingPosPnl, setSettlingPosPnl] = useState(false)
const [open, setOpen] = useState(false)
const unsettledPositions =
useMangoStore.getState().selectedMangoAccount.unsettledPerpPositions
const openPositions = useMangoStore(
(s) => s.selectedMangoAccount.openPerpPositions
)
const loading = settling || settlingPosPnl
const handleSettleAll = async () => {
setOpen(false)
setSettling(true)
for (const p of unsettledPositions) {
await settlePnl(p.perpMarket, p.perpAccount, t, undefined)
}
reloadMangoAccount()
setSettling(false)
}
const handleSettlePosPnl = async () => {
setOpen(false)
setSettlingPosPnl(true)
for (const p of openPositions) {
if (p.unsettledPnl > 0) {
console.log('settlePosPnl', settlePosPnl)
// await settlePosPnl([p.perpMarket], p.perpAccount, t, undefined)
}
}
setTimeout(() => {
setSettlingPosPnl(false)
}, 2000)
}
return (
<div
className="relative"
onMouseOver={() => setOpen(true)}
onMouseOut={() => setOpen(false)}
>
<Button className="flex h-8 w-full items-center justify-center rounded-full bg-th-bkg-button pt-0 pb-0 pl-3 pr-2 text-xs font-bold hover:brightness-[1.1] hover:filter sm:w-auto">
{loading ? (
<Loading />
) : (
<>
{t('redeem-pnl')}
<ChevronDownIcon
className={`default-transition h-5 w-5 ${
open ? 'rotate-180 transform' : 'rotate-360 transform'
}`}
/>
</>
)}
</Button>
<Transition
appear={true}
show={open}
as={Fragment}
enter="transition-all ease-in duration-200"
enterFrom="opacity-0 transform scale-75"
enterTo="opacity-100 transform scale-100"
leave="transition ease-out duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="absolute right-0 rounded-md bg-th-bkg-3 px-4 pt-2.5">
<MenuButton onClick={handleSettleAll} text={t('redeem-all')} />
<MenuButton
onClick={handleSettlePosPnl}
text={t('redeem-positive')}
/>
</div>
</Transition>
</div>
)
}

View File

@ -0,0 +1 @@
export * from './RedeemDropdown'

View File

@ -299,6 +299,7 @@
"redeem-all": "Redeem All",
"redeem-failure": "Error redeeming MNGO",
"redeem-pnl": "Redeem",
"redeem-positive": "Redeem positive",
"redeem-success": "Successfully redeemed MNGO",
"referrals": "Referrals",
"refresh": "Refresh",

View File

@ -13,7 +13,8 @@
"resolveJsonModule": true,
"jsx": "preserve",
"isolatedModules": true,
"incremental": true
"incremental": true,
"baseUrl": "."
},
"exclude": ["node_modules", ".next", "out", "public/datafeeds"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"]