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

feat: implement OptimismCard component for displaying decoded transaction fields and removes autocompleteBlockHash #658

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
165 changes: 92 additions & 73 deletions apps/web/src/pages/tx/[hash].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { useMemo } from "react";
import type { NextPage } from "next";
import { useRouter } from "next/router";

import type { OptimismDecodedData } from "@blobscan/api/src/blob-parse/optimism";
import type dayjs from "@blobscan/dayjs";

import { RollupBadge } from "~/components/Badges/RollupBadge";
import { Card } from "~/components/Cards/Card";
import { BlobCard } from "~/components/Cards/SurfaceCards/BlobCard";
Expand All @@ -16,6 +19,7 @@ import { Link } from "~/components/Link";
import { NavArrows } from "~/components/NavArrows";
import { BlockStatus } from "~/components/Status";
import { api } from "~/api-client";
import Loading from "~/icons/loading.svg";
import NextError from "~/pages/_error";
import type { TransactionWithExpandedBlockAndBlob } from "~/types";
import {
Expand Down Expand Up @@ -256,62 +260,10 @@ const Tx: NextPage = () => {
/>

{decodedData && (
<Card header="Decoded Fields">
<div>
<InfoGrid
fields={[
{
name: "Timestamp since L2 genesis",
value: (
<div className="whitespace-break-spaces">
{tx
? formatTimestamp(
tx.blockTimestamp.subtract(
decodedData.timestampSinceL2Genesis,
"ms"
)
)
: ""}
</div>
),
},
{
name: "Last L1 origin number",
value: decodedData.lastL1OriginNumber,
},
{
name: "Parent L2 block hash",
value: "0x" + decodedData.parentL2BlockHash + "...",
},
{
name: "L1 origin block hash",
value: (
<BlockHash
fullHash={decodedData.fullL1OriginBlockHash}
partialHash={decodedData.l1OriginBlockHash}
/>
),
},
{
name: "Number of L2 blocks",
value: decodedData.numberOfL2Blocks,
},
{
name: "Changed by L1 origin",
value: decodedData.changedByL1Origin,
},
{
name: "Total transactions",
value: decodedData.totalTxs,
},
{
name: "Contract creation transactions",
value: decodedData.contractCreationTxsNumber,
},
]}
/>
</div>
</Card>
<OptimismCard
decodedData={decodedData}
txTimestamp={tx ? tx.blockTimestamp : undefined}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we assume that tx is always defined?

/>
)}

<Card header={`Blobs ${tx ? `(${tx.blobs.length})` : ""}`}>
Expand All @@ -325,28 +277,95 @@ const Tx: NextPage = () => {
);
};

type BlockHashProps = {
partialHash: string;
fullHash: string | undefined;
type OptimismCardProps = {
decodedData: OptimismDecodedData;
txTimestamp: dayjs.Dayjs | undefined;
};

const BlockHash: FC<BlockHashProps> = ({ fullHash, partialHash }) => {
if (fullHash === undefined) {
return "0x" + partialHash + "...";
}
const OptimismCard: FC<OptimismCardProps> = ({ decodedData, txTimestamp }) => {
luis-herasme marked this conversation as resolved.
Show resolved Hide resolved
const { data: blockExists, isLoading } = api.block.checkBlockExists.useQuery({
blockNumber: decodedData.lastL1OriginNumber,
});

const blockLink = blockExists
? `https://blobscan.com/block/${decodedData.lastL1OriginNumber}`
: `https://etherscan.io/block/${decodedData.lastL1OriginNumber}`;

const hash = `0x${decodedData.l1OriginBlockHash}...`;

const timestamp = txTimestamp
? formatTimestamp(
txTimestamp.subtract(decodedData.timestampSinceL2Genesis, "ms")
)
: undefined;

const prefixedFullHash = "0x" + fullHash;
if (isLoading) {
return (
<Card header="Loading Decoded Fields...">
<div className="flex h-32 items-center justify-center">
<Loading className="h-8 w-8 animate-spin" />
</div>
</Card>
);
}

return (
<div className="flex items-center gap-2">
<Link href={`https://blobscan.com/block/${prefixedFullHash}`}>
{prefixedFullHash}
</Link>
<CopyToClipboard
value={prefixedFullHash}
tooltipText="Copy L1 origin block hash"
/>
</div>
<Card header="Decoded Fields">
<div>
<InfoGrid
fields={[
{
name: "Timestamp since L2 genesis",
value: <div className="whitespace-break-spaces">{timestamp}</div>,
},
{
name: "Last L1 origin number",
value: (
<div className="flex items-center gap-2">
<Link href={blockLink}>{decodedData.lastL1OriginNumber}</Link>
<CopyToClipboard
value={decodedData.lastL1OriginNumber.toString()}
tooltipText="Copy Last L1 origin number"
/>
</div>
),
},
{
name: "Parent L2 block hash",
value: "0x" + decodedData.parentL2BlockHash + "...",
},
{
name: "L1 origin block hash",
value: (
<div className="flex items-center gap-2">
<Link href={blockLink}>{hash}</Link>
<CopyToClipboard
value={hash}
tooltipText="Copy L1 origin block hash"
/>
</div>
),
},
{
name: "Number of L2 blocks",
value: decodedData.numberOfL2Blocks,
},
{
name: "Changed by L1 origin",
value: decodedData.changedByL1Origin,
},
{
name: "Total transactions",
value: decodedData.totalTxs,
},
{
name: "Contract creation transactions",
value: decodedData.contractCreationTxsNumber,
},
]}
/>
</div>
</Card>
);
};

Expand Down
49 changes: 1 addition & 48 deletions packages/api/src/blob-parse/optimism.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { z } from "zod";

import { prisma } from "@blobscan/db";
import { logger } from "@blobscan/logger";

export const OptimismDecodedDataSchema = z.object({
timestampSinceL2Genesis: z.number(),
lastL1OriginNumber: z.number(),
Expand All @@ -12,10 +9,9 @@ export const OptimismDecodedDataSchema = z.object({
changedByL1Origin: z.number(),
totalTxs: z.number(),
contractCreationTxsNumber: z.number(),
fullL1OriginBlockHash: z.string().optional(),
});

type OptimismDecodedData = z.infer<typeof OptimismDecodedDataSchema>;
export type OptimismDecodedData = z.infer<typeof OptimismDecodedDataSchema>;

export async function parseOptimismDecodedData(
data: string
Expand All @@ -34,48 +30,5 @@ export async function parseOptimismDecodedData(
return null;
}

const hash = await autocompleteBlockHash(decoded.data.l1OriginBlockHash);

if (hash) {
decoded.data.fullL1OriginBlockHash = hash;
} else {
logger.error(
`Failed to get full block hash for L1 origin block hash: ${decoded.data.l1OriginBlockHash}`
);
}

return decoded.data;
}

/* Autocomplete a block hash from a truncated version of it.
@param partialHash - The first bytes of a block hash.
@returns The block hash, if there is a single ocurrence, or null.
*/
async function autocompleteBlockHash(partialHash: string) {
if (!partialHash) {
return null;
}

const blocks = await prisma.block.findMany({
where: {
hash: {
startsWith: "0x" + partialHash,
},
},
select: {
hash: true,
},
});

if (blocks[0] === undefined) {
return null;
}

if (blocks.length > 1) {
logger.error(
`Found ${blocks.length} blocks while autocompleting block hash ${partialHash}`
);
}

return blocks[0].hash;
}
22 changes: 22 additions & 0 deletions packages/api/src/routers/block/checkBlobExists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { z } from "@blobscan/zod";
luis-herasme marked this conversation as resolved.
Show resolved Hide resolved

import { publicProcedure } from "../../procedures";

export const checkBlockExists = publicProcedure
luis-herasme marked this conversation as resolved.
Show resolved Hide resolved
.input(
z.object({
blockNumber: z.number(),
})
)
.query(async ({ ctx: { prisma }, input }) => {
const block = await prisma.block.findFirst({
where: {
number: input.blockNumber,
},
select: {
number: true,
},
});

return Boolean(block);
});

Check warning on line 22 in packages/api/src/routers/block/checkBlobExists.ts

View check run for this annotation

Codecov / codecov/patch

packages/api/src/routers/block/checkBlobExists.ts#L12-L22

Added lines #L12 - L22 were not covered by tests
2 changes: 2 additions & 0 deletions packages/api/src/routers/block/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { t } from "../../trpc-client";
import { checkBlockExists } from "./checkBlobExists";
import { getAll } from "./getAll";
import { getByBlockId } from "./getByBlockId";
import { getCount } from "./getCount";
Expand All @@ -9,4 +10,5 @@ export const blockRouter = t.router({
getByBlockId,
getCount,
getLatestBlock,
checkBlockExists,
});
Loading