Merge pull request #115 from blockworks-foundation/expand-perp-activity

expand perp trade activity feed details
This commit is contained in:
saml33 2023-04-05 23:47:39 +10:00 committed by GitHub
commit 55adbe7602
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 496 additions and 310 deletions

View File

@ -1,39 +0,0 @@
import mangoStore from '@store/mangoStore'
import { useEffect, useState } from 'react'
import ActivityFeedTable from './ActivityFeedTable'
import { LiquidationActivity } from 'types'
import LiquidationDetails from './LiquidationDetails'
const ActivityFeed = () => {
const activityFeed = mangoStore((s) => s.activityFeed.feed)
const [showActivityDetail, setShowActivityDetail] =
useState<LiquidationActivity>()
const [scrollPosition, setScrollPosition] = useState(0)
const handleShowActivityDetails = (activity: LiquidationActivity) => {
setShowActivityDetail(activity)
setScrollPosition(window.scrollY)
}
useEffect(() => {
if (scrollPosition && !showActivityDetail) {
window.scroll(0, scrollPosition)
}
}, [scrollPosition, showActivityDetail])
return !showActivityDetail ? (
<ActivityFeedTable
activityFeed={activityFeed}
handleShowActivityDetails={handleShowActivityDetails}
/>
) : (
<div className="px-6">
<LiquidationDetails
activity={showActivityDetail}
setShowActivityDetail={setShowActivityDetail}
/>
</div>
)
}
export default ActivityFeed

View File

@ -9,11 +9,7 @@ import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
import Tooltip from '@components/shared/Tooltip'
import { Disclosure, Transition } from '@headlessui/react'
import PerpSideBadge from '@components/trade/PerpSideBadge'
import {
ChevronDownIcon,
ChevronRightIcon,
NoSymbolIcon,
} from '@heroicons/react/20/solid'
import { ChevronDownIcon, NoSymbolIcon } from '@heroicons/react/20/solid'
import { useWallet } from '@solana/wallet-adapter-react'
import mangoStore from '@store/mangoStore'
import dayjs from 'dayjs'
@ -23,16 +19,17 @@ import { useViewport } from 'hooks/useViewport'
import { useTranslation } from 'next-i18next'
import Image from 'next/legacy/image'
import { useCallback, useState } from 'react'
import { ActivityFeed, isLiquidationFeedItem, LiquidationActivity } from 'types'
import { isLiquidationFeedItem, isPerpTradeFeedItem } from 'types'
import { PAGINATION_PAGE_LENGTH, PREFERRED_EXPLORER_KEY } from 'utils/constants'
import { formatNumericValue } from 'utils/numbers'
import { breakpoints } from 'utils/theme'
import LiquidationDetails from './LiquidationDetails'
import PerpTradeDetails from './PerpTradeDetails'
const formatFee = (value: number) => {
export const formatFee = (value: number) => {
return value.toLocaleString(undefined, {
minimumSignificantDigits: 1,
maximumSignificantDigits: 2,
maximumSignificantDigits: 3,
})
}
@ -259,14 +256,9 @@ const getValue = (activity: any, mangoAccountAddress: string) => {
return value
}
const ActivityFeedTable = ({
activityFeed,
handleShowActivityDetails,
}: {
activityFeed: ActivityFeed[]
handleShowActivityDetails: (x: LiquidationActivity) => void
}) => {
const ActivityFeedTable = () => {
const { t } = useTranslation(['common', 'activity'])
const activityFeed = mangoStore((s) => s.activityFeed.feed)
const { mangoAccountAddress } = useMangoAccount()
const actions = mangoStore((s) => s.actions)
const loadActivityFeed = mangoStore((s) => s.activityFeed.loading)
@ -323,100 +315,118 @@ const ActivityFeedTable = ({
const amounts = getCreditAndDebit(activity, mangoAccountAddress)
const value = getValue(activity, mangoAccountAddress)
const fee = getFee(activity, mangoAccountAddress)
const isExpandable =
isLiquidationFeedItem(activity) || isPerpTradeFeedItem(activity)
return (
<TrBody
key={signature + index}
className={`default-transition text-sm hover:bg-th-bkg-2 ${
isLiquidationFeedItem(activity) ? 'cursor-pointer' : ''
}`}
onClick={
isLiquidationFeedItem(activity)
? () => handleShowActivityDetails(activity)
: undefined
}
>
<Td>
<p className="font-body">
{dayjs(block_datetime).format('ddd D MMM')}
</p>
<p className="text-xs text-th-fgd-3">
{dayjs(block_datetime).format('h:mma')}
</p>
</Td>
<Td className="text-right">
{t(`activity:${activity_type}`)}
</Td>
<Td className="text-right font-mono">
{amounts.credit.value}{' '}
<span className="font-body text-th-fgd-3">
{amounts.credit.symbol}
</span>
</Td>
<Td className="text-right font-mono">
{amounts.debit.value}{' '}
<span className="font-body text-th-fgd-3">
{amounts.debit.symbol}
</span>
</Td>
<Td className="text-right font-mono">
{fee.value}{' '}
<span className="font-body text-th-fgd-3">
{fee.symbol}
</span>
</Td>
<Td
className={`text-right font-mono ${
activity_type === 'swap' ||
activity_type === 'perp_trade' ||
isOpenbook ||
isLiquidationFeedItem(activity)
? 'text-th-fgd-2'
: value >= 0
? 'text-th-up'
: 'text-th-down'
}`}
>
{value > 0 &&
activity_type !== 'swap' &&
activity_type !== 'perp_trade' &&
!isOpenbook &&
!isLiquidationFeedItem(activity)
? '+'
: ''}
<FormatNumericValue value={value} isUsd />
</Td>
<Td>
{!isLiquidationFeedItem(activity) ? (
<div className="flex items-center justify-end">
<Tooltip
content={`View on ${t(
`settings:${preferredExplorer.name}`
)}`}
placement="top-end"
<Disclosure key={`${signature}${index}`}>
{({ open }) => (
<>
<Disclosure.Button
as={TrBody}
className={`default-transition text-sm ${
isExpandable
? 'cursor-pointer md:hover:bg-th-bkg-2'
: 'pointer-events-none'
}`}
>
<Td>
<p className="font-body">
{dayjs(block_datetime).format('ddd D MMM')}
</p>
<p className="text-xs text-th-fgd-3">
{dayjs(block_datetime).format('h:mma')}
</p>
</Td>
<Td className="text-right">
{t(`activity:${activity_type}`)}
</Td>
<Td className="text-right font-mono">
{amounts.credit.value}{' '}
<span className="font-body text-th-fgd-3">
{amounts.credit.symbol}
</span>
</Td>
<Td className="text-right font-mono">
{amounts.debit.value}{' '}
<span className="font-body text-th-fgd-3">
{amounts.debit.symbol}
</span>
</Td>
<Td className="text-right font-mono">
{fee.value}{' '}
<span className="font-body text-th-fgd-3">
{fee.symbol}
</span>
</Td>
<Td
className={`text-right font-mono ${
activity_type === 'swap' ||
isOpenbook ||
isExpandable
? 'text-th-fgd-2'
: value >= 0
? 'text-th-up'
: 'text-th-down'
}`}
>
<a
href={`${preferredExplorer.url}${signature}`}
target="_blank"
rel="noopener noreferrer"
>
<div className="h-6 w-6">
<Image
alt=""
width="24"
height="24"
src={`/explorer-logos/${preferredExplorer.name}.png`}
{value > 0 &&
activity_type !== 'swap' &&
!isOpenbook &&
!isExpandable
? '+'
: ''}
<FormatNumericValue value={value} isUsd />
</Td>
<Td>
{!isExpandable ? (
<div className="flex items-center justify-end">
<Tooltip
content={`View on ${t(
`settings:${preferredExplorer.name}`
)}`}
placement="top-end"
>
<a
href={`${preferredExplorer.url}${signature}`}
target="_blank"
rel="noopener noreferrer"
>
<div className="h-6 w-6">
<Image
alt=""
width="24"
height="24"
src={`/explorer-logos/${preferredExplorer.name}.png`}
/>
</div>
</a>
</Tooltip>
</div>
) : (
<div className="flex items-center justify-end">
<ChevronDownIcon
className={`h-6 w-6 text-th-fgd-3 ${
open ? 'rotate-180' : 'rotate-360'
}`}
/>
</div>
</a>
</Tooltip>
</div>
) : (
<div className="flex items-center justify-end">
<ChevronRightIcon className="h-6 w-6 text-th-fgd-3" />
</div>
)}
</Td>
</TrBody>
)}
</Td>
</Disclosure.Button>
<Disclosure.Panel as={TrBody}>
{isLiquidationFeedItem(activity) ? (
<td className="p-6" colSpan={7}>
<LiquidationDetails activity={activity} />
</td>
) : isPerpTradeFeedItem(activity) ? (
<td className="p-6" colSpan={7}>
<PerpTradeDetails activity={activity} />
</td>
) : null}
</Disclosure.Panel>
</>
)}
</Disclosure>
)
})}
</tbody>
@ -481,10 +491,22 @@ const MobileActivityFeedItem = ({
const isOpenbook = activity_type === 'openbook_trade'
const isPerp = activity_type === 'perp_trade'
const value = getValue(activity, mangoAccountAddress)
const isExpandable =
isLiquidationFeedItem(activity) || isPerpTradeFeedItem(activity)
const isPerpTaker =
isPerpTradeFeedItem(activity) &&
activity.activity_details.taker === mangoAccountAddress
const perpTradeSide = isPerpTaker
? activity.activity_details.taker_side
: activity.activity_details.taker_side === 'bid'
? 'ask'
: 'bid'
return (
<div key={signature} className="border-b border-th-bkg-3">
{isLiquidationFeedItem(activity) ? (
{isExpandable ? (
<Disclosure>
{({ open }) => (
<>
@ -503,9 +525,26 @@ const MobileActivityFeedItem = ({
<p className="text-right text-xs">
{t(`activity:${activity_type}`)}
</p>
<p className="text-right font-mono text-sm text-th-fgd-1">
<FormatNumericValue value={value} isUsd />
</p>
{isLiquidationFeedItem(activity) ? (
<p className="text-right font-mono text-sm text-th-fgd-1">
<FormatNumericValue value={value} isUsd />
</p>
) : (
<p className="font-mono text-th-fgd-1">
<span className="mr-1">
{activity.activity_details.quantity}
</span>
<span className="font-body text-th-fgd-3">
{activity.activity_details.perp_market_name}
</span>
<span className="font-body">
{' '}
<PerpSideBadge
basePosition={perpTradeSide === 'bid' ? 1 : -1}
/>
</span>
</p>
)}
</div>
<ChevronDownIcon
className={`${
@ -522,7 +561,11 @@ const MobileActivityFeedItem = ({
>
<Disclosure.Panel>
<div className="border-t border-th-bkg-3 px-4 py-4">
<LiquidationDetails activity={activity} />
{isLiquidationFeedItem(activity) ? (
<LiquidationDetails activity={activity} />
) : (
<PerpTradeDetails activity={activity} />
)}
</div>
</Disclosure.Panel>
</Transition>

View File

@ -1,11 +1,11 @@
import { useEffect, useState } from 'react'
import SwapHistoryTable from '../swap/SwapHistoryTable'
import ActivityFeed from './ActivityFeed'
import TradeHistory from '@components/trade/TradeHistory'
import { useTranslation } from 'next-i18next'
import ActivityFilters from './ActivityFilters'
import mangoStore from '@store/mangoStore'
import useMangoAccount from 'hooks/useMangoAccount'
import ActivityFeedTable from './ActivityFeedTable'
const TABS = ['activity:activity-feed', 'activity:swaps', 'activity:trades']
@ -49,13 +49,13 @@ const HistoryTabs = () => {
const TabContent = ({ activeTab }: { activeTab: string }) => {
switch (activeTab) {
case 'activity:activity-feed':
return <ActivityFeed />
return <ActivityFeedTable />
case 'activity:swaps':
return <SwapHistoryTable />
case 'activity:trades':
return <TradeHistory />
default:
return <ActivityFeed />
return <ActivityFeedTable />
}
}

View File

@ -1,8 +1,5 @@
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
import { IconButton } from '@components/shared/Button'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import { ArrowLeftIcon } from '@heroicons/react/20/solid'
import dayjs from 'dayjs'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { useTranslation } from 'next-i18next'
import Image from 'next/image'
@ -16,17 +13,14 @@ import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
const LiquidationDetails = ({
activity,
setShowActivityDetail,
}: {
activity: LiquidationActivity
setShowActivityDetail?: (x: LiquidationActivity | undefined) => void
}) => {
const { t } = useTranslation(['common', 'activity', 'settings'])
const [preferredExplorer] = useLocalStorageState(
PREFERRED_EXPLORER_KEY,
EXPLORERS[0]
)
const { block_datetime } = activity
const getAssetLiquidatedReturned = (details: SpotOrPerpLiquidationItem) => {
const assets = {
@ -91,152 +85,148 @@ const LiquidationDetails = ({
}, [activity])
return (
<div className="md:pb-10">
{setShowActivityDetail ? (
<div className="flex items-center py-6">
<IconButton
className="mr-4"
onClick={() => setShowActivityDetail(undefined)}
size="small"
>
<ArrowLeftIcon className="h-5 w-5" />
</IconButton>
<h2 className="text-lg">{t('activity:liquidation-details')}</h2>
</div>
) : null}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{isPerpLiquidation(activity.activity_details) ? (
<>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('date')}</p>
<p className="text-th-fgd-1">
{dayjs(block_datetime).format('ddd D MMM')}
</p>
<p className="text-xs text-th-fgd-3">
{dayjs(block_datetime).format('h:mma')}
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:liquidation-type')}</p>
<p className="text-th-fgd-1">{t('perp')}</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:asset-liquidated')}</p>
<p className="font-mono text-th-fgd-1">
<FormatNumericValue value={assetLiquidated} />{' '}
<span className="font-body">{assetLiquidatedSymbol}</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
<FormatNumericValue
value={activity.activity_details.price}
isUsd
/>
</p>
<p className="font-mono text-xs text-th-fgd-3">
<FormatNumericValue value={liquidatedValue} isUsd />
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:asset-returned')}</p>
<p className="font-mono text-th-fgd-1">
<FormatNumericValue value={assetReturned} />{' '}
<span className="font-body">{assetReturnedSymbol}</span>
</p>
<p className="font-mono text-xs text-th-fgd-3">
<FormatNumericValue value={returnedValue} isUsd />
</p>
</div>
</>
) : (
<>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('date')}</p>
<p className="text-th-fgd-1">
{dayjs(block_datetime).format('ddd D MMM')}
</p>
<p className="text-xs text-th-fgd-3">
{dayjs(block_datetime).format('h:mma')}
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:liquidation-type')}</p>
<p className="text-th-fgd-1">{t('spot')}</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:asset-liquidated')}</p>
<p className="font-mono text-th-fgd-1">
<FormatNumericValue value={assetLiquidated} />{' '}
<span className="font-body">{assetLiquidatedSymbol}</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
<FormatNumericValue
value={activity.activity_details.asset_price}
isUsd
/>
</p>
<p className="font-mono text-xs text-th-fgd-3">
<FormatNumericValue value={liquidatedValue} isUsd />
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:asset-returned')}</p>
<p className="font-mono text-th-fgd-1">
<FormatNumericValue value={assetReturned} />{' '}
<span className="font-body">{assetReturnedSymbol}</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
<FormatNumericValue
value={activity.activity_details.liab_price}
isUsd
/>
</p>
<p className="font-mono text-xs text-th-fgd-3">
<FormatNumericValue value={returnedValue} isUsd />
</p>
</div>
</>
)}
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:liquidation-fee')}</p>
<p className="font-mono text-th-fgd-1">
<FormatNumericValue value={fee} isUsd />
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:liquidation-side')}</p>
<p className="text-th-fgd-1">
{activity.activity_details.side === 'liqor'
? t('activity:liquidator')
: t('activity:liquidated')}
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('activity:counterparty')}</p>
<a
className="text-sm"
href={`/?address=${activity.activity_details.counterparty}`}
target="_blank"
rel="noopener noreferrer"
>
{t('activity:view-account')}
</a>
</div>
<div className="col-span-1">
<p className="mb-0.5 text-sm">{t('transaction')}</p>
<a
className="default-transition flex items-center text-th-fgd-2 hover:text-th-fgd-3"
href={`${preferredExplorer.url}${activity.activity_details.signature}`}
target="_blank"
rel="noopener noreferrer"
>
<Image
alt=""
width="20"
height="20"
src={`/explorer-logos/${preferredExplorer.name}.png`}
/>
<span className="ml-2 text-sm">
{t(`settings:${preferredExplorer.name}`)}
</span>
</a>
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{isPerpLiquidation(activity.activity_details) ? (
<>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:liquidation-type')}
</p>
<p className="font-body text-th-fgd-1">{t('perp')}</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:asset-liquidated')}
</p>
<p className="text-th-fgd-1">
<FormatNumericValue value={assetLiquidated} />{' '}
<span className="font-body text-th-fgd-3">
{assetLiquidatedSymbol}
</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
<FormatNumericValue
value={activity.activity_details.price}
isUsd
/>
</p>
<p className="text-xs text-th-fgd-3">
<FormatNumericValue value={liquidatedValue} isUsd />
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:asset-returned')}
</p>
<p className="text-th-fgd-1">
<FormatNumericValue value={assetReturned} />{' '}
<span className="font-body text-th-fgd-3">
{assetReturnedSymbol}
</span>
</p>
<p className="text-xs text-th-fgd-3">
<FormatNumericValue value={returnedValue} isUsd />
</p>
</div>
</>
) : (
<>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:liquidation-type')}
</p>
<p className="font-body text-th-fgd-1">{t('spot')}</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:asset-liquidated')}
</p>
<p className="text-th-fgd-1">
<FormatNumericValue value={assetLiquidated} />{' '}
<span className="font-body text-th-fgd-3">
{assetLiquidatedSymbol}
</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
<FormatNumericValue
value={activity.activity_details.asset_price}
isUsd
/>
</p>
<p className="text-xs text-th-fgd-3">
<FormatNumericValue value={liquidatedValue} isUsd />
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:asset-returned')}
</p>
<p className="text-th-fgd-1">
<FormatNumericValue value={assetReturned} />{' '}
<span className="font-body text-th-fgd-3">
{assetReturnedSymbol}
</span>
<span className="ml-2 font-body text-th-fgd-3">at</span>{' '}
<FormatNumericValue
value={activity.activity_details.liab_price}
isUsd
/>
</p>
<p className="text-xs text-th-fgd-3">
<FormatNumericValue value={returnedValue} isUsd />
</p>
</div>
</>
)}
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:liquidation-fee')}
</p>
<p className="text-th-fgd-1">
<FormatNumericValue value={fee} isUsd />
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:liquidation-side')}
</p>
<p className="font-body text-th-fgd-1">
{activity.activity_details.side === 'liqor'
? t('activity:liquidator')
: t('activity:liquidated')}
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:counterparty')}
</p>
<a
className="text-sm"
href={`/?address=${activity.activity_details.counterparty}`}
target="_blank"
rel="noopener noreferrer"
>
{t('activity:view-account')}
</a>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('transaction')}
</p>
<a
className="flex items-center"
href={`${preferredExplorer.url}${activity.activity_details.signature}`}
target="_blank"
rel="noopener noreferrer"
>
<Image
alt=""
width="20"
height="20"
src={`/explorer-logos/${preferredExplorer.name}.png`}
/>
<span className="ml-2 text-sm">
{t(`settings:${preferredExplorer.name}`)}
</span>
</a>
</div>
</div>
)

View File

@ -0,0 +1,136 @@
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'
import FormatNumericValue from '@components/shared/FormatNumericValue'
import Tooltip from '@components/shared/Tooltip'
import PerpSideBadge from '@components/trade/PerpSideBadge'
import useLocalStorageState from 'hooks/useLocalStorageState'
import useMangoAccount from 'hooks/useMangoAccount'
import { useTranslation } from 'next-i18next'
import Image from 'next/image'
import { PerpTradeActivity } from 'types'
import { PREFERRED_EXPLORER_KEY } from 'utils/constants'
import { getDecimalCount } from 'utils/numbers'
import { formatFee } from './ActivityFeedTable'
const PerpTradeDetails = ({ activity }: { activity: PerpTradeActivity }) => {
const { t } = useTranslation(['common', 'activity', 'settings', 'trade'])
const { mangoAccountAddress } = useMangoAccount()
const [preferredExplorer] = useLocalStorageState(
PREFERRED_EXPLORER_KEY,
EXPLORERS[0]
)
const {
maker,
maker_fee,
perp_market_name,
price,
quantity,
signature,
taker,
taker_fee,
taker_side,
} = activity.activity_details
const isTaker = taker === mangoAccountAddress
const side = isTaker ? taker_side : taker_side === 'bid' ? 'ask' : 'bid'
const notional = quantity * price
const fee = isTaker ? taker_fee * notional : maker_fee * notional
const totalPrice = (notional + fee) / quantity
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('trade:side')}
</p>
<PerpSideBadge basePosition={side === 'bid' ? 1 : -1} />
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('trade:size')}
</p>
<p className="font-mono text-th-fgd-1">
{quantity}{' '}
<span className="font-body text-th-fgd-3">{perp_market_name}</span>
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:execution-price')}
</p>
<p className="font-mono text-th-fgd-1">
<FormatNumericValue value={price} decimals={getDecimalCount(price)} />{' '}
<span className="font-body text-th-fgd-3">USDC</span>
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">{t('value')}</p>
<p className="font-mono text-th-fgd-1">
<FormatNumericValue value={notional} isUsd />
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">{t('fee')}</p>
<p className="font-mono text-th-fgd-1">
{formatFee(fee)} <span className="font-body text-th-fgd-3">USDC</span>
</p>
<p className="font-body text-xs text-th-fgd-3">
{isTaker ? t('trade:taker') : t('trade:maker')}
</p>
</div>
<div className="col-span-1">
<Tooltip content={t('activity:net-price-desc')} placement="top-start">
<p className="tooltip-underline mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:net-price')}
</p>
</Tooltip>
<p className="font-mono text-th-fgd-1">
<FormatNumericValue
value={totalPrice}
decimals={Math.max(getDecimalCount(price), 3)}
/>{' '}
<span className="font-body text-th-fgd-3">USDC</span>
</p>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('activity:counterparty')}
</p>
<a
className="text-sm"
href={`/?address=${isTaker ? maker : taker}`}
target="_blank"
rel="noopener noreferrer"
>
{t('activity:view-account')}
</a>
</div>
<div className="col-span-1">
<p className="mb-0.5 font-body text-sm text-th-fgd-3">
{t('transaction')}
</p>
<a
className="default-transition flex items-center"
href={`${preferredExplorer.url}${signature}`}
target="_blank"
rel="noopener noreferrer"
>
<Image
alt=""
width="20"
height="20"
src={`/explorer-logos/${preferredExplorer.name}.png`}
/>
<span className="ml-2 text-sm">
{t(`settings:${preferredExplorer.name}`)}
</span>
</a>
</div>
</div>
)
}
export default PerpTradeDetails

View File

@ -6,13 +6,13 @@ type SideBadgeProps = {
const SideBadge: FunctionComponent<SideBadgeProps> = ({ side }) => {
if (side !== 'buy' && side !== 'sell') {
return <div>Unknown</div>
return <span>Unknown</span>
}
const isBid = side === 'buy'
return (
<div
<span
className={`inline-block rounded uppercase ${
isBid
? 'text-th-up md:border md:border-th-up'
@ -21,7 +21,7 @@ const SideBadge: FunctionComponent<SideBadgeProps> = ({ side }) => {
uppercase md:-my-0.5 md:px-1.5 md:py-0.5 md:text-xs`}
>
{isBid ? 'Buy' : 'Sell'}
</div>
</span>
)
}

View File

@ -2,7 +2,7 @@ const PerpSideBadge = ({ basePosition }: { basePosition: number }) => {
return (
<>
{basePosition !== 0 ? (
<div
<span
className={`inline-block rounded uppercase ${
basePosition > 0
? 'text-th-up md:border md:border-th-up'
@ -11,7 +11,7 @@ const PerpSideBadge = ({ basePosition }: { basePosition: number }) => {
uppercase md:-my-0.5 md:px-1.5 md:py-0.5 md:text-xs`}
>
{basePosition > 0 ? 'Long' : 'Short'}
</div>
</span>
) : (
'--'
)}

View File

@ -11,6 +11,7 @@
"debit": "Debit",
"deposit": "Deposit",
"deposits": "Deposits",
"execution-price": "Execution Price",
"filter-results": "Filter",
"liquidate_perp_base_position_or_positive_pnl": "Perp Liquidation",
"liquidate_token_with_token": "Spot Liquidation",
@ -22,8 +23,11 @@
"liquidations": "Liquidations",
"liquidation-details": "Liquidation Details",
"liquidator": "Liquidator",
"net-price": "Net Price",
"net-price-desc": "The trade price inclusive of fees",
"no-activity": "No account activity",
"openbook_trade": "Spot Trade",
"perp-details": "Perp Trade Details",
"perps": "Perps",
"perp_trade": "Perp Trade",
"reset-filters": "Reset Filters",

View File

@ -11,6 +11,7 @@
"debit": "Debit",
"deposit": "Deposit",
"deposits": "Deposits",
"execution-price": "Execution Price",
"filter-results": "Filter",
"liquidate_perp_base_position_or_positive_pnl": "Perp Liquidation",
"liquidate_token_with_token": "Spot Liquidation",
@ -22,8 +23,11 @@
"liquidations": "Liquidations",
"liquidation-details": "Liquidation Details",
"liquidator": "Liquidator",
"net-price": "Net Price",
"net-price-desc": "The trade price inclusive of fees",
"no-activity": "No account activity",
"openbook_trade": "Spot Trade",
"perp-details": "Perp Trade Details",
"perps": "Perps",
"perp_trade": "Perp Trade",
"reset-filters": "Reset Filters",

View File

@ -11,6 +11,7 @@
"debit": "Debit",
"deposit": "Deposit",
"deposits": "Deposits",
"execution-price": "Execution Price",
"filter-results": "Filter",
"liquidate_perp_base_position_or_positive_pnl": "Perp Liquidation",
"liquidate_token_with_token": "Spot Liquidation",
@ -22,8 +23,11 @@
"liquidations": "Liquidations",
"liquidation-details": "Liquidation Details",
"liquidator": "Liquidator",
"net-price": "Net Price",
"net-price-desc": "The trade price inclusive of fees",
"no-activity": "No account activity",
"openbook_trade": "Spot Trade",
"perp-details": "Perp Trade Details",
"perps": "Perps",
"perp_trade": "Perp Trade",
"reset-filters": "Reset Filters",

View File

@ -11,6 +11,7 @@
"debit": "Debit",
"deposit": "Deposit",
"deposits": "Deposits",
"execution-price": "Execution Price",
"filter-results": "Filter",
"liquidate_perp_base_position_or_positive_pnl": "Perp Liquidation",
"liquidate_token_with_token": "Spot Liquidation",
@ -22,8 +23,11 @@
"liquidations": "Liquidations",
"liquidation-details": "Liquidation Details",
"liquidator": "Liquidator",
"net-price": "Net Price",
"net-price-desc": "The trade price inclusive of fees",
"no-activity": "No account activity",
"openbook_trade": "Spot Trade",
"perp-details": "Perp Trade Details",
"perps": "Perps",
"perp_trade": "Perp Trade",
"reset-filters": "Reset Filters",

View File

@ -11,6 +11,7 @@
"debit": "Debit",
"deposit": "Deposit",
"deposits": "Deposits",
"execution-price": "Execution Price",
"filter-results": "Filter",
"liquidate_perp_base_position_or_positive_pnl": "Perp Liquidation",
"liquidate_token_with_token": "Spot Liquidation",
@ -22,8 +23,11 @@
"liquidations": "Liquidations",
"liquidation-details": "Liquidation Details",
"liquidator": "Liquidator",
"net-price": "Net Price",
"net-price-desc": "The trade price inclusive of fees",
"no-activity": "No account activity",
"openbook_trade": "Spot Trade",
"perp-details": "Perp Trade Details",
"perps": "Perps",
"perp_trade": "Perp Trade",
"reset-filters": "Reset Filters",

View File

@ -132,6 +132,26 @@ export interface DepositWithdrawFeedItem {
wallet_pk: string
}
export interface PerpTradeFeedItem {
block_datetime: string
maker: string
maker_fee: number
maker_order_id: string | null
market_index: number
perp_market: string
perp_market_name: string
price: number
quantity: number
seq_num: number
signature: number
slot: number
taker: string
taker_client_order_id: string | null
taker_fee: number
taker_order_id: string | null
taker_side: string
}
export interface SpotLiquidationFeedItem {
asset_amount: number
asset_price: number
@ -173,6 +193,13 @@ export interface LiquidationActivity {
symbol: string
}
export interface PerpTradeActivity {
activity_details: PerpTradeFeedItem
block_datetime: string
activity_type: string
symbol: string
}
export function isLiquidationFeedItem(
item: ActivityFeed
): item is LiquidationActivity {
@ -182,6 +209,15 @@ export function isLiquidationFeedItem(
return false
}
export function isPerpTradeFeedItem(
item: ActivityFeed
): item is PerpTradeActivity {
if (item.activity_type === 'perp_trade') {
return true
}
return false
}
export function isPerpLiquidation(
activityDetails: SpotOrPerpLiquidationItem
): activityDetails is PerpLiquidationFeedItem {
@ -234,7 +270,7 @@ export type ActivityFeed = {
| SpotLiquidationFeedItem
| PerpLiquidationFeedItem
| SwapHistoryItem
| PerpTradeHistory
| PerpTradeFeedItem
| SpotTradeHistory
}