Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

Commit

Permalink
Feature: beta transaction details view
Browse files Browse the repository at this point in the history
  • Loading branch information
shelegdmitriy committed Jun 15, 2022
1 parent e54b668 commit c8b3d68
Show file tree
Hide file tree
Showing 18 changed files with 1,038 additions and 6 deletions.
4 changes: 4 additions & 0 deletions common/src/types/procedures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export type TransactionBaseInfo = TRPCQueryOutput<
>[number];

export type TransactionDetails = NonNullable<TRPCQueryOutput<"transaction">>;
export type TransactionReceipt = TransactionDetails["receipt"];
export type RefundReceipt = TransactionDetails["refundReceipts"][number];
export type TransactionBlockInfo = TransactionReceipt["includedInBlock"];
export type TransactionReceiptExecutionStatus = TransactionReceipt["status"];

export type Action =
TRPCQueryOutput<"transactions-list">[number]["actions"][number];
Expand Down
3 changes: 3 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
},
"homepage": "https://github.com/near/near-explorer#readme",
"dependencies": {
"@loadable/component": "^5.15.2",
"@stitches/react": "^1.2.6",
"@trpc/client": "^9.23.4",
"@trpc/next": "^9.23.4",
Expand Down Expand Up @@ -60,6 +61,7 @@
"react-flip-move": "^3.0.3",
"react-i18next": "^11.15.1",
"react-infinite-scroll-component": "^5.1.0",
"react-json-view": "^1.21.3",
"react-paginate": "^7.1.5",
"react-query": "^3.39.0",
"styled-components": "^4.4.0",
Expand All @@ -72,6 +74,7 @@
"@types/d3": "^5.16.4",
"@types/isomorphic-fetch": "^0.0.36",
"@types/jest": "^27.0.3",
"@types/loadable__component": "^5.13.4",
"@types/lodash": "^4.14.177",
"@types/mixpanel-browser": "^2.35.7",
"@types/node": "^12.20.37",
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/components/beta/common/AccountLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as React from "react";
import Link from "../../utils/Link";
import { shortenString } from "../../../libraries/formatting";
import { styled } from "../../../libraries/styles";

const AccountLinkWrapper = styled("a", {
whiteSpace: "nowrap",
});

export interface Props {
accountId: string;
}

const AccountLink: React.FC<Props> = React.memo(({ accountId }) => {
return (
<Link href={`/accounts/${accountId}`} passHref>
<AccountLinkWrapper>{shortenString(accountId)}</AccountLinkWrapper>
</Link>
);
});

export default AccountLink;
21 changes: 21 additions & 0 deletions frontend/src/components/beta/common/BlockLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from "react";
import Link from "../../utils/Link";
import { styled } from "../../../libraries/styles";
import { TransactionBlockInfo } from "../../../types/common";

export interface Props {
block: TransactionBlockInfo;
}

const LinkWrapper = styled("a", {
whiteSpace: "nowrap",
cursor: "pointer",
});

const BlockLink: React.FC<Props> = React.memo(({ block }) => (
<Link href="/blocks/[hash]" as={`/blocks/${block.hash}`}>
<LinkWrapper>{`#${block.height}`}</LinkWrapper>
</Link>
));

export default BlockLink;
54 changes: 54 additions & 0 deletions frontend/src/components/beta/common/CodeArgs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as React from "react";
import { hexy } from "hexy";

import { styled } from "../../../libraries/styles";

import CodePreview from "../../utils/CodePreview";
import JsonView from "./JsonView";

const HexArgs = styled("div", {
padding: "10px 0",

"& > div": {
background: "#f8f8f8",
borderRadius: 4,
color: "#3f4246",
padding: 20,
fontSize: "$font-m",
fontWeight: 500,
fontFamily: "SF Mono",

"& textarea, pre": {
background: "inherit",
color: "inherit",
fontFamily: "inherit",
fontSize: "inherit",
border: "none",
padding: 0,
},
},
});

const CodeArgs: React.FC<{ args: string }> = React.memo(({ args }) => {
const decodedArgs = Buffer.from(args, "base64");

let prettyArgs: object | string;
try {
const parsedJSONArgs = JSON.parse(decodedArgs.toString());
prettyArgs =
typeof parsedJSONArgs === "boolean"
? JSON.stringify(parsedJSONArgs)
: parsedJSONArgs;
} catch {
prettyArgs = hexy(decodedArgs, { format: "twos" });
}
return typeof prettyArgs === "object" ? (
<JsonView args={prettyArgs} />
) : (
<HexArgs>
<CodePreview collapseHeight={200} maxHeight={600} value={prettyArgs} />
</HexArgs>
);
});

export default CodeArgs;
25 changes: 25 additions & 0 deletions frontend/src/components/beta/common/JsonView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from "react";
import loadable from "@loadable/component";
// https://github.com/mac-s-g/react-json-view/issues/296#issuecomment-997176256
const ReactJson = loadable(() => import("react-json-view"));

type Props = {
args: object;
};

const JsonView: React.FC<Props> = React.memo(({ args }) => (
<ReactJson
src={args}
name={null}
iconStyle="triangle"
displayObjectSize={false}
displayDataTypes={false}
style={{
fontSize: "14px",
padding: "10px 0",
fontFamily: "SF Mono",
}}
/>
));

export default JsonView;
126 changes: 126 additions & 0 deletions frontend/src/components/beta/transactions/InspectReceipt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import JSBI from "jsbi";
import * as React from "react";
import { styled } from "../../../libraries/styles";
import * as BI from "../../../libraries/bigint";
import { TransactionReceipt } from "../../../types/common";
import { NearAmount } from "../../utils/NearAmount";
import Gas from "../../utils/Gas";
import AccountLink from "../common/AccountLink";
import BlockLink from "../common/BlockLink";

type Props = {
receipt: TransactionReceipt;
refundReceipts?: TransactionReceipt[];
};

const Table = styled("table", {
width: "100%",
fontFamily: "SF Mono",
marginVertical: 24,
});

const TableElement = styled("td", {
color: "#000000",
fontSize: 14,
lineHeight: "175%",
});

const BalanceTitle = styled("div", {
marginTop: 36,
fontWeight: 600,
});

const BalanceAmount = styled("div", {
color: "#1A8300",
});

const InspectReceipt: React.FC<Props> = React.memo(
({ receipt, refundReceipts }) => {
const refund =
refundReceipts
?.reduce(
(acc, receipt) => JSBI.add(acc, JSBI.BigInt(receipt.deposit || 0)),
BI.zero
)
.toString() ?? "0";

return (
<Table>
<tr>
<TableElement>Receipt ID</TableElement>
<TableElement>{receipt.receiptId}</TableElement>
</tr>
<tr>
<TableElement>Executed in Block</TableElement>
<TableElement>
<BlockLink block={receipt.includedInBlock} />
</TableElement>
</tr>
<tr>
<TableElement>Predecessor ID</TableElement>
<TableElement>
<AccountLink accountId={receipt.signerId} />
</TableElement>
</tr>
<tr>
<TableElement>Receiver ID</TableElement>
<TableElement>
<AccountLink accountId={receipt.receiverId} />
</TableElement>
</tr>
<tr>
<TableElement>Attached Gas</TableElement>
<TableElement>
{"args" in receipt.actions[0] &&
"gas" in receipt.actions[0].args ? (
<Gas gas={JSBI.BigInt(receipt.actions[0].args?.gas)} />
) : (
"-"
)}
</TableElement>
</tr>
<tr>
<TableElement>Gas Burned</TableElement>
<TableElement>
<Gas gas={JSBI.BigInt(receipt.gasBurnt || 0)} />
</TableElement>
</tr>
<tr>
<TableElement>Tokens Burned</TableElement>
<TableElement>
<NearAmount amount={receipt.tokensBurnt} decimalPlaces={2} />
</TableElement>
</tr>
<tr>
<TableElement>Refunded</TableElement>
<TableElement>
{refund ? <NearAmount amount={refund} decimalPlaces={2} /> : "0"}
</TableElement>
</tr>
<tr>
<TableElement colSpan={2}>
<BalanceTitle>New Balance</BalanceTitle>
</TableElement>
</tr>
<tr>
<TableElement>
<AccountLink accountId={receipt.signerId} />
</TableElement>
<TableElement>
<BalanceAmount>--.--.--</BalanceAmount>
</TableElement>
</tr>
<tr>
<TableElement>
<AccountLink accountId={receipt.receiverId} />
</TableElement>
<TableElement>
<BalanceAmount>--.--.--</BalanceAmount>
</TableElement>
</tr>
</Table>
);
}
);

export default InspectReceipt;
111 changes: 111 additions & 0 deletions frontend/src/components/beta/transactions/ReceiptDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from "react";
import { useTranslation } from "react-i18next";
import CodeArgs from "../common/CodeArgs";
import JsonView from "../common/JsonView";

import { styled } from "../../../libraries/styles";
import { TransactionReceipt } from "../../../types/common";

type Props = {
receipt: TransactionReceipt;
};

const DetailsWrapper = styled("div", {
display: "flex",
flexDirection: "column",
marginVertical: 24,
});

const Row = styled("div", {
display: "flex",
justifyContent: "space-between",
marginVertical: 20,

"&:last-child": {
marginVertical: 0,
},
});

const Column = styled("div", {
display: "flex",
flexDirection: "column",
width: "48%",
});

const CodeArgsWrapper = styled("div", {
background: "#f8f8f8",
borderRadius: 4,
color: "#3f4246",
padding: 20,
fontSize: "$font-m",
fontWeight: 500,
fontFamily: "SF Mono",

"& textarea, pre": {
background: "inherit",
color: "inherit",
fontFamily: "inherit",
fontSize: "inherit",
border: "none",
padding: 0,
},
});

const Title = styled("h4", {
fontSize: "$font-m",
fontWeight: 500,
fontFamily: "SF Pro Display",
lineHeight: "175%",
color: "#000000",
});

const ReceiptDetails: React.FC<Props> = React.memo(({ receipt }) => {
const { t } = useTranslation();
let statusInfo;
if ("SuccessValue" in receipt.status) {
const { SuccessValue } = receipt.status;
if (SuccessValue.length === 0) {
statusInfo = (
<CodeArgsWrapper>
{t("component.transactions.ReceiptRow.empty_result")}
</CodeArgsWrapper>
);
} else {
statusInfo = <CodeArgs args={SuccessValue} />;
}
} else if ("Failure" in receipt.status) {
const { Failure } = receipt.status;
statusInfo = <JsonView args={Failure} />;
} else if ("SuccessReceiptId" in receipt.status) {
const { SuccessReceiptId } = receipt.status;
statusInfo = (
<CodeArgsWrapper>
<pre>{SuccessReceiptId}</pre>
</CodeArgsWrapper>
);
}
return (
<DetailsWrapper>
<Row>
<Column>
<div>
<Title>Logs</Title>
<CodeArgsWrapper>
{receipt.logs.length === 0 ? (
t("component.transactions.ReceiptRow.no_logs")
) : (
<pre>{receipt.logs.join("\n")}</pre>
)}
</CodeArgsWrapper>
</div>
<div>
<Title>Result</Title>
{statusInfo}
</div>
</Column>
</Row>
</DetailsWrapper>
);
});

export default ReceiptDetails;
Loading

0 comments on commit c8b3d68

Please sign in to comment.