Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add page for raw l2 transaction data #434

Merged
merged 9 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions packages/backend/src/api/controllers/L2TransactionController.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { renderPerpetualL2TransactionDetailsPage } from '@explorer/frontend'
import {
renderPerpetualL2TransactionDetailsPage,
renderRawL2TransactionPage,
} from '@explorer/frontend'
import { UserDetails } from '@explorer/shared'

import { PageContextService } from '../../core/PageContextService'
Expand Down Expand Up @@ -64,7 +67,59 @@ export class L2TransactionController {
}
}

extractTransactionByMultiAndAltIndex(
async getRawL2TransactionPage(
givenUser: Partial<UserDetails>,
transactionId: number,
multiIndex?: number,
altIndex?: number
): Promise<ControllerResult> {
const context = await this.pageContextService.getPageContext(givenUser)

if (context.tradingMode != 'perpetual') {
return { type: 'not found' }
}
const aggregatedL2Transaction =
await this.l2TransactionRepository.findAggregatedByTransactionId(
transactionId
)

if (!aggregatedL2Transaction) {
return {
type: 'not found',
message: `L2 transaction #${transactionId} was not found`,
}
}
let transaction: AggregatedL2TransactionRecord | undefined
transaction = this.extractTransactionByMultiAndAltIndex(
aggregatedL2Transaction,
multiIndex,
altIndex
)

if (!transaction) {
return {
type: 'not found',
message: `L2 transaction #${transactionId} with given parameters was not found`,
}
}

transaction = this.convertPricesToUSDCents(transaction)

return {
type: 'success',
content: renderRawL2TransactionPage({
context,
transaction: {
transactionId: transaction.transactionId,
stateUpdateId: transaction.stateUpdateId,
originalTransaction: transaction.originalTransaction,
alternativeTransactions: transaction.alternativeTransactions,
},
}),
}
}

private extractTransactionByMultiAndAltIndex(
aggregatedL2Transaction: AggregatedL2TransactionRecord,
multiIndex: number | undefined,
altIndex: number | undefined
Expand Down
44 changes: 44 additions & 0 deletions packages/backend/src/api/routers/PerpetualFrontendRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,27 @@ export function addPerpetualTradingRoutes(
)
)

router.get(
'/raw-l2-transactions/:transactionId{/:multiIndex}?',
withTypedContext(
z.object({
params: z.object({
transactionId: stringAsInt(),
multiIndex: z.optional(stringAsInt()),
}),
}),
async (ctx) => {
const givenUser = getGivenUser(ctx)
const result = await l2TransactionController.getRawL2TransactionPage(
givenUser,
ctx.params.transactionId,
ctx.params.multiIndex
)
applyControllerResult(ctx, result)
}
)
)

router.get(
'/l2-transactions/:transactionId/alternatives/:altIndex{/:multiIndex}?',
withTypedContext(
Expand All @@ -113,5 +134,28 @@ export function addPerpetualTradingRoutes(
}
)
)

router.get(
'/raw-l2-transactions/:transactionId/alternatives/:altIndex{/:multiIndex}?',
withTypedContext(
z.object({
params: z.object({
transactionId: stringAsInt(),
altIndex: z.optional(stringAsInt()),
multiIndex: z.optional(stringAsInt()),
}),
}),
async (ctx) => {
const givenUser = getGivenUser(ctx)
const result = await l2TransactionController.getRawL2TransactionPage(
givenUser,
ctx.params.transactionId,
ctx.params.multiIndex,
ctx.params.altIndex
)
applyControllerResult(ctx, result)
}
)
)
}
}
12 changes: 12 additions & 0 deletions packages/frontend/src/preview/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
renderNewSpotForcedWithdrawPage,
renderOfferAndForcedTradePage,
renderPerpetualForcedWithdrawalPage,
renderRawL2TransactionPage,
renderRegularWithdrawalPage,
renderSpotForcedWithdrawalPage,
renderStateUpdateBalanceChangesPage,
Expand Down Expand Up @@ -935,6 +936,17 @@ const routes: Route[] = [
altIndex: randomInt(0, 10),
})
},
},
{
path: '/raw-l2-transactions/:transactionId',
link: '/raw-l2-transactions/random',
description: 'Raw L2 transaction details page.',
render: (ctx) => {
ctx.body = renderRawL2TransactionPage({
context: getPerpetualPageContext(ctx),
transaction: randomAggregatedPerpetualL2TransactionEntry(),
})
},
breakAfter: true,
},
// #endregion
Expand Down
18 changes: 18 additions & 0 deletions packages/frontend/src/view/assets/icons/RawIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import classNames from 'classnames'
import React, { SVGAttributes } from 'react'

export function RawIcon(props: SVGAttributes<SVGElement>) {
const { className, ...rest } = props
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
className={classNames('fill-current', className)}
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<path d="M6.49999 3C5.51516 3 4.69999 3.81517 4.69999 4.8V9.3C3.70549 9.3 2.89999 10.1055 2.89999 11.1V15.6C2.89999 16.5945 3.70549 17.4 4.69999 17.4V19.2C4.69999 20.1848 5.51516 21 6.49999 21H17.3C18.2848 21 19.1 20.1848 19.1 19.2V15.6V11.1V7.87266C19.0999 7.63398 19.0051 7.40509 18.8363 7.23633L14.8637 3.26367C14.6949 3.09489 14.466 3.00005 14.2273 3H6.49999ZM6.49999 4.8H13.7V7.5C13.7 7.9968 14.1032 8.4 14.6 8.4H17.3V9.3H6.49999V4.8ZM4.69999 11.1H5.59999H6.04999C6.79519 11.1 7.39999 11.7048 7.39999 12.45C7.39999 13.0332 7.02894 13.5249 6.51054 13.7139L7.45273 15.6H6.44726L5.59999 13.9055V15.6H4.69999V13.8V11.1ZM9.51816 11.1H10.6889L11.9 15.6H10.9525L10.7223 14.7H9.48828L9.25624 15.6H8.29999L9.51816 11.1ZM11.9 11.1H12.8053L13.2342 13.9143L13.7633 11.1H14.5314L15.0623 13.9107L15.4894 11.1H16.4L15.5914 15.5789H14.6721L14.1482 12.9404L13.6209 15.5789H12.7051L11.9 11.1ZM5.59999 12V12.9H6.04999C6.29839 12.9 6.49999 12.6984 6.49999 12.45C6.49999 12.2016 6.29839 12 6.04999 12H5.59999ZM10.093 12.3182L9.71152 13.8H10.4779L10.093 12.3182ZM6.49999 17.4H17.3V19.2H6.49999V17.4Z" />
</svg>
)
}
58 changes: 14 additions & 44 deletions packages/frontend/src/view/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import cx from 'classnames'
import React from 'react'
import React, { ComponentPropsWithoutRef, ElementType } from 'react'

import { Link } from './Link'

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
readonly variant?: ButtonVariant
}
type ButtonProps<T extends ElementType> = {
variant?: ButtonVariant
className?: string
as?: T
} & ComponentPropsWithoutRef<T> &
// eslint-disable-next-line @typescript-eslint/ban-types
(T extends 'a' ? { disabled?: boolean } : {})

type ButtonVariant = 'contained' | 'outlined'
const mainClassNames =
Expand All @@ -17,52 +19,20 @@ const classNameMap: Record<ButtonVariant, string> = {
'bg-transparent border border-brand hover:bg-brand hover:bg-opacity-20',
}

export function Button({
export function Button<T extends ElementType = 'button'>({
variant = 'contained',
className,
children,
disabled,
as,
...rest
}: ButtonProps) {
}: ButtonProps<T>) {
const Comp = as ?? 'button'
return (
<button
<Comp
className={cx(mainClassNames, classNameMap[variant], className)}
disabled={disabled}
{...rest}
>
{children}
</button>
)
}

interface LinkButtonProps extends React.HTMLProps<HTMLAnchorElement> {
readonly variant?: ButtonVariant
readonly href: string
}

export function LinkButton({
variant = 'contained',
className,
children,
href,
disabled,
...rest
}: LinkButtonProps) {
return (
<Link
href={disabled ? undefined : href}
disabled={disabled}
className={cx(
'flex items-center justify-center !text-white !no-underline',
mainClassNames,
disabled
? 'cursor-not-allowed bg-white bg-opacity-20'
: classNameMap[variant],
className
)}
{...rest}
>
{children}
</Link>
</Comp>
)
}
15 changes: 12 additions & 3 deletions packages/frontend/src/view/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@ import React from 'react'

import { OutLinkIcon } from '../assets/icons/OutLinkIcon'

type LinkProps = React.HTMLProps<HTMLAnchorElement>
type LinkProps = React.HTMLProps<HTMLAnchorElement> & {
accessoryLeft?: React.ReactNode
}

export function Link({ className, href, children, ...rest }: LinkProps) {
export function Link({
className,
href,
children,
accessoryLeft,
...rest
}: LinkProps) {
const isOutLink = /^https?:\/\//.test(href ?? '')
const target = isOutLink ? '_blank' : undefined
const rel = isOutLink ? 'noreferrer noopener' : undefined
const hasHref = href != null
const classNames = cx(
'group inline-flex gap-2 text-blue-500 hover:text-blue-600 underline underline-offset-[3.5px]',
'group inline-flex gap-2 text-blue-500 fill-blue-500 hover:fill-blue-600 hover:text-blue-600 underline underline-offset-[3.5px] transition-colors',
className
)
return hasHref ? (
<a href={href} className={classNames} target={target} rel={rel} {...rest}>
{accessoryLeft}
{children}
{isOutLink && <OutLinkIcon className="group-hover:stroke-blue-600" />}
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function ContentWrapper(props: ContentWrapperProps) {
return (
<main
className={cx(
'mx-auto w-full max-w-[1024px] flex-1 py-16 px-4 sm:px-8',
'mx-auto w-full max-w-5xl flex-1 py-16 px-4 sm:px-8',
props.className
)}
>
Expand Down
6 changes: 3 additions & 3 deletions packages/frontend/src/view/components/table/TablePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import isNumber from 'lodash/isNumber'
import React, { ReactNode } from 'react'

import { formatInt } from '../../../utils/formatting/formatAmount'
import { LinkButton } from '../Button'
import { Button } from '../Button'
import { Link } from '../Link'
import { SectionHeading } from '../SectionHeading'

Expand Down Expand Up @@ -42,9 +42,9 @@ export function TablePreview(props: TablePreviewProps) {
)}
{isNumber(props.total) && props.total > props.visible && (
<div className="mt-6 flex items-center justify-center">
<LinkButton variant="outlined" href={props.path}>
<Button as="a" variant="outlined" href={props.path}>
View all {props.entryLongNamePlural}
</LinkButton>
</Button>
</div>
)}
</section>
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/src/view/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './pages/home/HomeStateUpdatesPage'
export * from './pages/home/HomeTransactionsPage'
export * from './pages/l2-transaction/PerpetualL2TransactionDetailsPage'
export * from './pages/MerkleProofPage'
export * from './pages/RawL2TransactionPage'
export type { StateUpdateBalanceChangeEntry } from './pages/state-update/components/StateUpdateBalanceChangesTable'
export * from './pages/state-update/StateUpdateBalanceChangesPage'
export * from './pages/state-update/StateUpdateL2TransactionsPage'
Expand Down
47 changes: 47 additions & 0 deletions packages/frontend/src/view/pages/RawL2TransactionPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { PageContext, toJsonWithoutBigInts } from '@explorer/shared'
import React from 'react'

import { Card } from '../components/Card'
import { ContentWrapper } from '../components/page/ContentWrapper'
import { Page } from '../components/page/Page'
import { PageTitle } from '../components/PageTitle'
import { reactToHtml } from '../reactToHtml'
import {
AggregatedPerpetualL2TransactionEntry,
l2TransactionTypeToText,
} from './l2-transaction/common'

interface RawL2TransactionPageProps {
context: PageContext<'perpetual'>
transaction: AggregatedPerpetualL2TransactionEntry
}

export function renderRawL2TransactionPage(props: RawL2TransactionPageProps) {
return reactToHtml(<RawL2TransactionPage {...props} />)
}

export function RawL2TransactionPage(props: RawL2TransactionPageProps) {
return (
<Page
context={props.context}
description={`Raw details of ${l2TransactionTypeToText(
props.transaction.originalTransaction.type
)} l2 transaction`}
path="/raw-l2-transactions/:transactionId"
>
<ContentWrapper className="flex !max-w-6xl flex-col">
<div className="flex gap-3">
<PageTitle>{`Raw data of transaction #${props.transaction.transactionId}`}</PageTitle>
<span className="h-min rounded-full bg-fuchsia-400 py-2 px-2.5 text-sm font-bold text-black">
L2 TRANSACTION
</span>
</div>
<Card>
<pre className="whitespace-pre-wrap">
{toJsonWithoutBigInts(props.transaction, 2)}
</pre>
</Card>
</ContentWrapper>
</Page>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from 'react'
import { assetToInfo } from '../../../utils/assets'
import { formatAmount } from '../../../utils/formatting/formatAmount'
import { AssetWithLogo } from '../../components/AssetWithLogo'
import { Button, LinkButton } from '../../components/Button'
import { Button } from '../../components/Button'
import { Card } from '../../components/Card'
import { Link } from '../../components/Link'
import { OrderedList } from '../../components/OrderedList'
Expand Down Expand Up @@ -106,13 +106,14 @@ function NewSpotForcedWithdrawalPage(props: Props) {
</div>
<div className="flex flex-col gap-2">
<Button className="w-full">Prepare for withdrawal</Button>
<LinkButton
<Button
as="a"
className="w-full"
variant="outlined"
href={`/users/${props.starkKey.toString()}`}
>
Back to assets
</LinkButton>
</Button>
</div>
</form>
</Card>
Expand Down
Loading