2022-07-28 03:51:36 -07:00
|
|
|
import { ModalProps } from '../../types/modal'
|
|
|
|
import Modal from '../shared/Modal'
|
2022-09-12 08:53:57 -07:00
|
|
|
import mangoStore from '@store/mangoStore'
|
2022-07-28 03:51:36 -07:00
|
|
|
import { notify } from '../../utils/notifications'
|
|
|
|
import Button from '../shared/Button'
|
|
|
|
import { useTranslation } from 'next-i18next'
|
2023-01-12 05:23:16 -08:00
|
|
|
import { useCallback, useEffect, useState } from 'react'
|
2022-07-28 03:51:36 -07:00
|
|
|
import BounceLoader from '../shared/BounceLoader'
|
2023-01-12 05:23:16 -08:00
|
|
|
import {
|
|
|
|
MangoAccount,
|
|
|
|
TokenPosition,
|
|
|
|
toUiDecimalsForQuote,
|
|
|
|
} from '@blockworks-foundation/mango-v4'
|
|
|
|
import {
|
|
|
|
CheckCircleIcon,
|
|
|
|
ExclamationCircleIcon,
|
|
|
|
TrashIcon,
|
|
|
|
} from '@heroicons/react/20/solid'
|
2023-01-11 09:38:51 -08:00
|
|
|
import useUnsettledPerpPositions from 'hooks/useUnsettledPerpPositions'
|
2023-01-12 05:23:16 -08:00
|
|
|
import { getMultipleAccounts } from '@project-serum/anchor/dist/cjs/utils/rpc'
|
|
|
|
import { formatFixedDecimals } from 'utils/numbers'
|
2022-07-28 03:51:36 -07:00
|
|
|
|
|
|
|
const CloseAccountModal = ({ isOpen, onClose }: ModalProps) => {
|
2023-01-12 05:23:16 -08:00
|
|
|
const { t } = useTranslation(['close-account'])
|
2022-07-28 03:51:36 -07:00
|
|
|
const [loading, setLoading] = useState(false)
|
2022-08-11 21:20:17 -07:00
|
|
|
const set = mangoStore((s) => s.set)
|
2023-01-11 09:38:51 -08:00
|
|
|
const openOrders = Object.values(mangoStore((s) => s.mangoAccount.openOrders))
|
2023-01-12 05:23:16 -08:00
|
|
|
const connection = mangoStore.getState().connection
|
2023-01-11 14:37:58 -08:00
|
|
|
const hasOpenOrders =
|
|
|
|
openOrders.length && openOrders.filter((x) => x.length).length > 0
|
2023-01-11 09:38:51 -08:00
|
|
|
const mangoAccount = mangoStore((s) => s.mangoAccount)
|
|
|
|
const perpPositions = mangoStore((s) => s.mangoAccount.perpPositions)
|
|
|
|
const openPerpPositions = Object.values(perpPositions).filter((p) =>
|
|
|
|
p.basePositionLots.toNumber()
|
|
|
|
)
|
2023-01-11 14:37:58 -08:00
|
|
|
const group = mangoStore.getState().group
|
2023-01-11 09:38:51 -08:00
|
|
|
const unsettledBalances = Object.values(mangoAccount.spotBalances).filter(
|
|
|
|
(x) => x.unsettled && x.unsettled > 0
|
|
|
|
)
|
|
|
|
const unsettledPerpPositions = useUnsettledPerpPositions()
|
2023-01-11 14:37:58 -08:00
|
|
|
const [hasBorrows, setHasBorrows] = useState(false)
|
2023-01-11 09:38:51 -08:00
|
|
|
const [hasOpenPositions, setHasOpenPositions] = useState(false)
|
2023-01-12 05:23:16 -08:00
|
|
|
const [totalAccountSOL, setTotalAccountSOL] = useState(0)
|
2022-07-28 03:51:36 -07:00
|
|
|
|
|
|
|
const handleCloseMangoAccount = async () => {
|
|
|
|
const client = mangoStore.getState().client
|
|
|
|
const mangoAccount = mangoStore.getState().mangoAccount.current
|
2022-10-07 04:47:15 -07:00
|
|
|
const mangoAccounts = mangoStore.getState().mangoAccounts
|
2023-01-11 09:38:51 -08:00
|
|
|
|
2022-07-28 03:51:36 -07:00
|
|
|
if (!mangoAccount || !group) return
|
|
|
|
setLoading(true)
|
|
|
|
try {
|
2023-01-13 09:07:18 -08:00
|
|
|
const tx = await client.emptyAndCloseMangoAccount(group, mangoAccount)
|
2022-07-28 03:51:36 -07:00
|
|
|
if (tx) {
|
2022-08-24 20:28:01 -07:00
|
|
|
const newMangoAccounts = mangoAccounts.filter(
|
|
|
|
(ma) => !ma.publicKey.equals(mangoAccount.publicKey)
|
|
|
|
)
|
|
|
|
let newCurrentAccount: MangoAccount
|
|
|
|
if (newMangoAccounts[0]) {
|
2022-10-07 05:22:18 -07:00
|
|
|
newCurrentAccount = await newMangoAccounts[0].reload(client)
|
2022-08-24 20:28:01 -07:00
|
|
|
}
|
|
|
|
|
2022-07-28 03:51:36 -07:00
|
|
|
setLoading(false)
|
|
|
|
onClose()
|
|
|
|
notify({
|
|
|
|
title: t('account-closed'),
|
|
|
|
type: 'success',
|
|
|
|
txid: tx,
|
|
|
|
})
|
2022-08-11 21:20:17 -07:00
|
|
|
set((state) => {
|
2022-10-07 04:47:15 -07:00
|
|
|
state.mangoAccounts = newMangoAccounts
|
2022-08-24 20:28:01 -07:00
|
|
|
state.mangoAccount.current = newCurrentAccount
|
2022-08-11 21:20:17 -07:00
|
|
|
})
|
2022-07-28 03:51:36 -07:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
setLoading(false)
|
2022-08-02 11:04:00 -07:00
|
|
|
console.error(e)
|
2022-07-28 03:51:36 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-12 05:23:16 -08:00
|
|
|
const fetchTotalAccountSOL = useCallback(async () => {
|
|
|
|
if (!mangoAccount) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const accountKeys = [
|
|
|
|
mangoAccount.current!.publicKey,
|
|
|
|
...mangoAccount.openOrderAccounts.map((x) => x.address),
|
|
|
|
]
|
|
|
|
const accounts = await getMultipleAccounts(connection, accountKeys)
|
|
|
|
const lamports =
|
|
|
|
accounts.reduce((total, account) => {
|
|
|
|
return total + account!.account.lamports
|
|
|
|
}, 0) * 0.000000001
|
|
|
|
|
|
|
|
setTotalAccountSOL(lamports)
|
|
|
|
}, [mangoAccount])
|
|
|
|
|
2023-01-11 09:38:51 -08:00
|
|
|
useEffect(() => {
|
2023-01-11 14:37:58 -08:00
|
|
|
if (mangoAccount && group) {
|
|
|
|
if (
|
|
|
|
mangoAccount.current
|
|
|
|
?.tokensActive()
|
|
|
|
.filter(
|
2023-01-11 14:42:42 -08:00
|
|
|
(token: TokenPosition) =>
|
2023-01-11 14:37:58 -08:00
|
|
|
token.balanceUi(
|
|
|
|
group.getFirstBankByTokenIndex(token.tokenIndex)
|
|
|
|
) < 0
|
|
|
|
).length
|
|
|
|
) {
|
|
|
|
setHasBorrows(true)
|
|
|
|
}
|
2023-01-11 09:38:51 -08:00
|
|
|
if (openPerpPositions.length || unsettledPerpPositions.length) {
|
|
|
|
setHasOpenPositions(true)
|
|
|
|
}
|
2023-01-12 05:23:16 -08:00
|
|
|
fetchTotalAccountSOL()
|
2023-01-11 09:38:51 -08:00
|
|
|
}
|
2023-01-11 14:37:58 -08:00
|
|
|
}, [mangoAccount, group])
|
2023-01-11 09:38:51 -08:00
|
|
|
|
|
|
|
const isDisabled =
|
2023-01-11 14:37:58 -08:00
|
|
|
hasOpenOrders ||
|
2023-01-11 09:38:51 -08:00
|
|
|
hasBorrows ||
|
|
|
|
hasOpenPositions ||
|
|
|
|
!!unsettledBalances.length
|
2022-07-28 03:51:36 -07:00
|
|
|
return (
|
|
|
|
<Modal isOpen={isOpen} onClose={onClose}>
|
2023-01-12 05:23:16 -08:00
|
|
|
<div className="h-[550px]">
|
2022-07-28 03:51:36 -07:00
|
|
|
{loading ? (
|
|
|
|
<BounceLoader loadingMessage={t('closing-account')} />
|
|
|
|
) : (
|
|
|
|
<div className="flex h-full flex-col justify-between">
|
2023-01-12 05:23:16 -08:00
|
|
|
<div className="space-y-4">
|
2022-07-28 03:51:36 -07:00
|
|
|
<h2 className="mb-1">{t('close-account')}</h2>
|
2023-01-12 05:23:16 -08:00
|
|
|
<p>{t('description')}</p>
|
2023-01-12 05:34:12 -08:00
|
|
|
<p>{t('you-must')}:</p>
|
2023-01-12 05:23:16 -08:00
|
|
|
<div className="overflow-none space-y-2 rounded-md bg-th-bkg-4 p-2 sm:p-4">
|
|
|
|
<div className="flex items-center text-th-fgd-2">
|
|
|
|
{hasBorrows ? (
|
|
|
|
<ExclamationCircleIcon className="mr-1.5 h-4 w-4 text-th-down" />
|
|
|
|
) : (
|
|
|
|
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
|
|
|
)}
|
|
|
|
{t('close-all-borrows')}
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center text-th-fgd-2">
|
|
|
|
{hasOpenPositions ? (
|
|
|
|
<ExclamationCircleIcon className="mr-1.5 h-4 w-4 text-th-down" />
|
|
|
|
) : (
|
|
|
|
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
|
|
|
)}
|
|
|
|
{t('close-perp-positions')}
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center text-th-fgd-2">
|
|
|
|
{hasOpenOrders ? (
|
|
|
|
<ExclamationCircleIcon className="mr-1.5 h-4 w-4 text-th-down" />
|
|
|
|
) : (
|
|
|
|
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
|
|
|
)}
|
|
|
|
{t('close-open-orders')}
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center text-th-fgd-2">
|
|
|
|
{unsettledBalances.length ? (
|
|
|
|
<ExclamationCircleIcon className="mr-1.5 h-4 w-4 text-th-down" />
|
|
|
|
) : (
|
|
|
|
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
|
|
|
)}
|
|
|
|
{t('settle-balances')}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<p>By closing your account you will:</p>
|
|
|
|
<div className="overflow-none space-y-2 rounded-md bg-th-bkg-4 p-2 sm:p-4">
|
|
|
|
<div className="flex items-center text-th-fgd-2">
|
|
|
|
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
2023-01-12 05:34:12 -08:00
|
|
|
{t('delete-your-account')}
|
2023-01-12 05:23:16 -08:00
|
|
|
</div>
|
|
|
|
<div className="flex items-center text-th-fgd-2">
|
|
|
|
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
|
|
|
{t('withdraw-assets-worth', {
|
|
|
|
value:
|
|
|
|
mangoAccount && group
|
|
|
|
? formatFixedDecimals(
|
|
|
|
toUiDecimalsForQuote(
|
|
|
|
mangoAccount!.current!.getEquity(group).toNumber()
|
|
|
|
),
|
|
|
|
false,
|
|
|
|
true
|
|
|
|
)
|
|
|
|
: 0,
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center text-th-fgd-2">
|
|
|
|
<CheckCircleIcon className="mr-1.5 h-4 w-4 text-th-success"></CheckCircleIcon>
|
|
|
|
{t('recover-x-sol', {
|
|
|
|
amount: totalAccountSOL.toFixed(3),
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-28 03:51:36 -07:00
|
|
|
</div>
|
2023-01-12 05:23:16 -08:00
|
|
|
|
2022-07-28 03:51:36 -07:00
|
|
|
<Button
|
|
|
|
className="w-full"
|
2023-01-11 14:37:58 -08:00
|
|
|
disabled={isDisabled}
|
2022-07-28 03:51:36 -07:00
|
|
|
onClick={handleCloseMangoAccount}
|
|
|
|
size="large"
|
|
|
|
>
|
2022-09-11 05:05:21 -07:00
|
|
|
<div className="flex items-center justify-center">
|
|
|
|
<TrashIcon className="mr-2 h-5 w-5" />
|
|
|
|
{t('close-account')}
|
|
|
|
</div>
|
2022-07-28 03:51:36 -07:00
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</Modal>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default CloseAccountModal
|