import { useState, useEffect, useMemo } from 'react' import { useTranslation } from 'next-i18next' import { ArrowSmDownIcon, ExternalLinkIcon, InformationCircleIcon, SaveIcon, } from '@heroicons/react/outline' import { getMarketByBaseSymbolAndKind, PerpMarket, } from '@blockworks-foundation/mango-client' import TradeHistoryTable from '../TradeHistoryTable' import useMangoStore from '../../stores/useMangoStore' import { Table, TrHead, Th, TrBody, Td, TableDateDisplay, Row, } from '../TableElements' import { LinkButton } from '../Button' import { useSortableData } from '../../hooks/useSortableData' import { formatUsdValue, getDelistedMarketKey } from '../../utils' import Tooltip from '../Tooltip' import { exportDataToCSV } from '../../utils/export' import { notify } from '../../utils/notifications' import Button from '../Button' import { useViewport } from '../../hooks/useViewport' import { breakpoints } from '.././TradePageGrid' import MobileTableHeader from 'components/mobile/MobileTableHeader' const historyViews = [ { label: 'Trades', key: 'Trades' }, { label: 'Deposits', key: 'Deposit' }, { label: 'Withdrawals', key: 'Withdraw' }, { label: 'Liquidations', key: 'Liquidation' }, ] export default function AccountHistory() { const { t } = useTranslation('common') const [view, setView] = useState('Trades') const [history, setHistory] = useState(null) const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current) const mangoAccountPk = useMemo(() => { if (mangoAccount) { return mangoAccount.publicKey.toString() } }, [mangoAccount]) useEffect(() => { const fetchAccountActivity = async () => { const response = await fetch( `https://mango-transaction-log.herokuapp.com/v3/stats/activity-feed?mango-account=${mangoAccountPk}` ) const parsedResponse = await response.json() setHistory(parsedResponse) } if (mangoAccountPk) { fetchAccountActivity() } }, [mangoAccountPk]) return ( <>
{historyViews.map(({ label, key }, index) => (
0 ? 'ml-4 md:ml-2' : null } default-transition cursor-pointer rounded-md ${ view === key ? `text-th-primary` : `text-th-fgd-3 hover:text-th-fgd-1` } `} onClick={() => setView(key)} key={key as string} > {t(label.toLowerCase())}
))}
) } const ViewContent = ({ view, history }) => { switch (view) { case 'Trades': return case 'Deposit': return case 'Withdraw': return case 'Liquidation': return default: return } } const parseActivityDetails = (activity_details, activity_type, perpMarket) => { let assetGained, assetLost const assetSymbol = activity_type === 'liquidate_perp_market' ? 'USD (PERP)' : activity_details.asset_symbol const liabSymbol = activity_type === 'liquidate_perp_market' || activity_details.liab_type === 'Perp' ? activity_details.liab_symbol.includes('USDC') ? 'USD (PERP)' : `${activity_details.liab_symbol}-PERP` : activity_details.liab_symbol const liabAmount = perpMarket && liabSymbol !== 'USD (PERP)' ? perpMarket.baseLotsToNumber(activity_details.liab_amount) : activity_details.liab_amount const assetAmount = activity_details.asset_amount const asset_amount = { amount: parseFloat(assetAmount), symbol: assetSymbol, price: parseFloat(activity_details.asset_price), } const liab_amount = { amount: parseFloat(liabAmount), symbol: liabSymbol, price: parseFloat(activity_details.liab_price), } switch (activity_type) { case 'liquidate_token_and_token': return [liab_amount, asset_amount] case 'liquidate_token_and_perp': if (activity_details.asset_type === 'Token') { return [liab_amount, asset_amount] } else { return [asset_amount, liab_amount] } case 'liquidate_perp_market': if (parseFloat(activity_details.asset_amount) > 0) { assetGained = asset_amount assetLost = liab_amount } else { assetGained = liab_amount assetLost = asset_amount } return [assetGained, assetLost] default: return [] } } const LiquidationHistoryTable = ({ history, view }) => { const { t } = useTranslation('common') const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current) const markets = useMangoStore((s) => s.selectedMangoGroup.markets) const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config) const filteredHistory = useMemo(() => { return history?.length ? history.filter((h) => h.activity_type.includes('liquidate')) : [] }, [history, view]) const { items, requestSort, sortConfig } = useSortableData(filteredHistory) const exportHistoryToCSV = () => { const dataToExport = history .filter((val) => val.activity_type == view) .map((row) => { row = row.activity_details const timestamp = new Date(row.block_datetime) return { date: `${timestamp.toLocaleDateString()} ${timestamp.toLocaleTimeString()}`, asset: row.symbol, quantity: row.quantity, value: row.usd_equivalent, } }) const headers = ['Timestamp', 'Asset', 'Quantity', 'Value'] if (dataToExport.length == 0) { notify({ title: t('export-data-empty'), description: '', type: 'info', }) return } const tab = historyViews.filter((v) => v.key == view)[0].label const title = `${ mangoAccount?.name || mangoAccount?.publicKey }-${tab}-${new Date().toLocaleDateString()}` exportDataToCSV(dataToExport, title, headers, t) } return ( <>

{filteredHistory.length === 1 ? t('number-liquidation', { number: filteredHistory.length }) : t('number-liquidations', { number: filteredHistory.length })}

{t('delay-displaying-recent')} {t('use-explorer-one')} {t('use-explorer-two')} {t('use-explorer-three')}
} >
{items.length ? ( <> {items.map(({ activity_details, activity_type }) => { let perpMarket: PerpMarket | null = null if (activity_type.includes('perp')) { const symbol = activity_details.perp_market.split('-')[0] const marketConfig = getMarketByBaseSymbolAndKind( groupConfig, symbol, 'perp' ) if (marketConfig && marketConfig.publicKey) { perpMarket = markets[ marketConfig.publicKey.toString() ] as PerpMarket } else { perpMarket = markets[getDelistedMarketKey(symbol)] as PerpMarket } } const [assetGained, assetLost] = parseActivityDetails( activity_details, activity_type, perpMarket ) const lostDecimals = assetLost.symbol === 'SOL' ? 9 : 6 const gainedDecimals = assetGained.symbol === 'SOL' ? 9 : 6 return ( ) })}
requestSort('block_datetime')} > {t('date')} requestSort('asset_amount')} > Asset Lost requestSort('asset_price')} > Price requestSort('liab_amount')} > Asset Gained requestSort('liab_price')} > Price
{Math.abs(assetLost.amount).toLocaleString(undefined, { maximumFractionDigits: lostDecimals, })}{' '} {assetLost.symbol} {assetLost.price.toLocaleString(undefined, { maximumFractionDigits: lostDecimals, })} {Math.abs(assetGained.amount).toLocaleString( undefined, { maximumFractionDigits: gainedDecimals, } )}{' '} {assetGained.symbol} {assetGained.price.toLocaleString(undefined, { maximumFractionDigits: gainedDecimals, })} View Transaction
) : (
{t('history-empty')}
)} ) } const HistoryTable = ({ history, view }) => { const { t } = useTranslation('common') const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current) const { width } = useViewport() const isMobile = width ? width < breakpoints.md : false const filteredHistory = useMemo(() => { return history?.length ? history .filter((h) => h.activity_type === view) .map((h) => h.activity_details) : [] }, [history, view]) const { items, requestSort, sortConfig } = useSortableData(filteredHistory) const exportHistoryToCSV = () => { const dataToExport = history .filter((val) => val.activity_type == view) .map((row) => { row = row.activity_details const timestamp = new Date(row.block_datetime) return { date: `${timestamp.toLocaleDateString()} ${timestamp.toLocaleTimeString()}`, asset: row.symbol, quantity: row.quantity, value: row.usd_equivalent, } }) const headers = ['Timestamp', 'Asset', 'Quantity', 'Value'] if (dataToExport.length == 0) { notify({ title: t('export-data-empty'), description: '', type: 'info', }) return } const tab = historyViews.filter((v) => v.key == view)[0].label const title = `${ mangoAccount?.name || mangoAccount?.publicKey }-${tab}-${new Date().toLocaleDateString()}` exportDataToCSV(dataToExport, title, headers, t) } return ( <>

{filteredHistory.length === 1 ? view === 'Withdraw' ? t('number-withdrawal', { number: filteredHistory.length }) : t('number-deposit', { number: filteredHistory.length }) : view === 'Withdraw' ? t('number-withdrawals', { number: filteredHistory.length }) : t('number-deposits', { number: filteredHistory.length })}

{t('delay-displaying-recent')} {t('use-explorer-one')} {t('use-explorer-two')} {t('use-explorer-three')}
} >
{items.length ? ( !isMobile ? ( {items.map((activity_details: any) => { const { signature, block_datetime, symbol, quantity, usd_equivalent, } = activity_details return ( ) })}
requestSort('block_datetime')} > {t('date')} requestSort('symbol')} > {t('asset')} requestSort('quantity')} > {t('quantity')} requestSort('usd_equivalent')} > {t('value')}
{symbol}
{quantity.toLocaleString()} {formatUsdValue(usd_equivalent)} {t('view-transaction')}
) : (
{items.map((activity_details: any) => { const { signature, block_datetime, symbol, quantity, usd_equivalent, } = activity_details return (

{`${quantity.toLocaleString()} ${symbol}`}

{formatUsdValue(usd_equivalent)}

) })}
) ) : (
{t('history-empty')}
)} ) }