From 1b9f26a3f0b92a1d607f9e574444a8aa12420aa8 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Tue, 26 Oct 2021 22:10:37 -0300 Subject: [PATCH] First working prototype with call tree --- src/Transaction.tsx | 10 ++++ src/transaction/Trace.tsx | 39 ++++++++++++++++ src/transaction/TraceItem.tsx | 68 +++++++++++++++++++++++++++ src/useErigonHooks.ts | 86 +++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+) create mode 100644 src/transaction/Trace.tsx create mode 100644 src/transaction/TraceItem.tsx diff --git a/src/Transaction.tsx b/src/Transaction.tsx index a240c7e..04cdd8b 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -25,6 +25,12 @@ const Logs = React.lazy( /* webpackChunkName: "txlogs", webpackPrefetch: true */ "./transaction/Logs" ) ); +const Trace = React.lazy( + () => + import( + /* webpackChunkName: "txtrace", webpackPrefetch: true */ "./transaction/Trace" + ) +); type TransactionParams = { txhash: string; @@ -87,6 +93,7 @@ const Transaction: React.FC = () => { {txData && ` (${txData.confirmedData?.logs?.length ?? 0})`} )} + Trace @@ -105,6 +112,9 @@ const Transaction: React.FC = () => { + + + diff --git a/src/transaction/Trace.tsx b/src/transaction/Trace.tsx new file mode 100644 index 0000000..45e438a --- /dev/null +++ b/src/transaction/Trace.tsx @@ -0,0 +1,39 @@ +import React, { useContext } from "react"; +import AddressHighlighter from "../components/AddressHighlighter"; +import DecoratedAddressLink from "../components/DecoratedAddressLink"; +import ContentFrame from "../ContentFrame"; +import { TransactionData } from "../types"; +import { useTraceTransaction } from "../useErigonHooks"; +import { RuntimeContext } from "../useRuntime"; +import TraceItem from "./TraceItem"; + +type TraceProps = { + txData: TransactionData; +}; + +const Trace: React.FC = ({ txData }) => { + const { provider } = useContext(RuntimeContext); + const traces = useTraceTransaction(provider, txData.transactionHash); + + return ( + +
+
+ + + +
+ {traces?.map((t, i, a) => ( + + ))} +
+
+ ); +}; + +export default React.memo(Trace); diff --git a/src/transaction/TraceItem.tsx b/src/transaction/TraceItem.tsx new file mode 100644 index 0000000..f3179ef --- /dev/null +++ b/src/transaction/TraceItem.tsx @@ -0,0 +1,68 @@ +import React from "react"; +import AddressHighlighter from "../components/AddressHighlighter"; +import DecoratedAddressLink from "../components/DecoratedAddressLink"; +import { TransactionData } from "../types"; +import { rawInputTo4Bytes } from "../use4Bytes"; +import { TraceGroup } from "../useErigonHooks"; + +type TraceItemProps = { + t: TraceGroup; + txData: TransactionData; + last: boolean; +}; + +const TraceItem: React.FC = ({ t, txData, last }) => { + const raw4Bytes = rawInputTo4Bytes(t.input); + return ( + <> +
+
+
+ {!last && ( +
+ )} +
+
+ {t.depth} + {t.type} + + + + + + . + {raw4Bytes} + ( + {t.input.length > 10 && ( + + input=[{t.input.slice(10)}] + + )} + ) +
+
+ {t.children && ( +
+
+
+ {t.children.map((tc, i, a) => ( + + ))} +
+
+ )} + + ); +}; + +export default React.memo(TraceItem); diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index 7322a0d..2bb308c 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -308,3 +308,89 @@ export const useInternalOperations = ( return intTransfers; }; + +export type TraceEntry = { + type: string; + depth: number; + from: string; + to: string; + value: BigNumber; + input: string; +}; + +export type TraceGroup = TraceEntry & { + children: TraceGroup[] | null; +}; + +export const useTraceTransaction = ( + provider: JsonRpcProvider | undefined, + txHash: string +): TraceGroup[] | undefined => { + const [traceGroups, setTraceGroups] = useState(); + + useEffect(() => { + if (!provider) { + setTraceGroups(undefined); + return; + } + + const traceTx = async () => { + const results = await provider.send("ots_traceTransaction", [txHash]); + + // Implement better formatter + for (let i = 0; i < results.length; i++) { + results[i].from = provider.formatter.address(results[i].from); + results[i].to = provider.formatter.address(results[i].to); + } + + // Build trace tree + const buildTraceTree = ( + flatList: TraceEntry[], + depth: number = 0 + ): TraceGroup[] => { + const entries: TraceGroup[] = []; + + let children: TraceEntry[] | null = null; + for (let i = 0; i < flatList.length; i++) { + if (flatList[i].depth === depth) { + if (children !== null) { + const childrenTree = buildTraceTree(children, depth + 1); + const prev = entries.pop(); + if (prev) { + prev.children = childrenTree; + entries.push(prev); + } + } + + entries.push({ + ...flatList[i], + children: null, + }); + children = null; + } else { + if (children === null) { + children = []; + } + children.push(flatList[i]); + } + } + if (children !== null) { + const childrenTree = buildTraceTree(children, depth + 1); + const prev = entries.pop(); + if (prev) { + prev.children = childrenTree; + entries.push(prev); + } + } + + return entries; + }; + + const traceTree = buildTraceTree(results); + setTraceGroups(traceTree); + }; + traceTx(); + }, [provider, txHash]); + + return traceGroups; +};