First working prototype with call tree
This commit is contained in:
parent
75612fc3cc
commit
1b9f26a3f0
|
@ -25,6 +25,12 @@ const Logs = React.lazy(
|
||||||
/* webpackChunkName: "txlogs", webpackPrefetch: true */ "./transaction/Logs"
|
/* webpackChunkName: "txlogs", webpackPrefetch: true */ "./transaction/Logs"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
const Trace = React.lazy(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "txtrace", webpackPrefetch: true */ "./transaction/Trace"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
type TransactionParams = {
|
type TransactionParams = {
|
||||||
txhash: string;
|
txhash: string;
|
||||||
|
@ -87,6 +93,7 @@ const Transaction: React.FC = () => {
|
||||||
{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`}
|
{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`}
|
||||||
</NavTab>
|
</NavTab>
|
||||||
)}
|
)}
|
||||||
|
<NavTab href={`/tx/${txhash}/trace`}>Trace</NavTab>
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
<React.Suspense fallback={null}>
|
<React.Suspense fallback={null}>
|
||||||
|
@ -105,6 +112,9 @@ const Transaction: React.FC = () => {
|
||||||
<Route path="/tx/:txhash/logs/" exact>
|
<Route path="/tx/:txhash/logs/" exact>
|
||||||
<Logs txData={txData} metadata={metadata} />
|
<Logs txData={txData} metadata={metadata} />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/tx/:txhash/trace" exact>
|
||||||
|
<Trace txData={txData} />
|
||||||
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
</SelectionContext.Provider>
|
</SelectionContext.Provider>
|
||||||
|
|
|
@ -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<TraceProps> = ({ txData }) => {
|
||||||
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
const traces = useTraceTransaction(provider, txData.transactionHash);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContentFrame tabs>
|
||||||
|
<div className="mt-4 mb-5 space-y-3 font-code text-sm flex flex-col items-start">
|
||||||
|
<div>
|
||||||
|
<AddressHighlighter address={txData.from}>
|
||||||
|
<DecoratedAddressLink
|
||||||
|
address={txData.from}
|
||||||
|
miner={txData.from === txData.confirmedData?.miner}
|
||||||
|
txFrom
|
||||||
|
txTo={txData.from === txData.to}
|
||||||
|
/>
|
||||||
|
</AddressHighlighter>
|
||||||
|
</div>
|
||||||
|
{traces?.map((t, i, a) => (
|
||||||
|
<TraceItem key={i} t={t} txData={txData} last={i === a.length - 1} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ContentFrame>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(Trace);
|
|
@ -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<TraceItemProps> = ({ t, txData, last }) => {
|
||||||
|
const raw4Bytes = rawInputTo4Bytes(t.input);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex">
|
||||||
|
<div className="relative w-5">
|
||||||
|
<div className="absolute border-l border-b w-full h-full transform -translate-y-1/2"></div>
|
||||||
|
{!last && (
|
||||||
|
<div className="absolute left-0 border-l w-full h-full transform translate-y-1/2"></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-baseline border rounded px-1 py-px">
|
||||||
|
<span className="mr-2">{t.depth}</span>
|
||||||
|
<span className="text-xs text-gray-400 lowercase">{t.type}</span>
|
||||||
|
<span>
|
||||||
|
<AddressHighlighter address={t.to}>
|
||||||
|
<DecoratedAddressLink
|
||||||
|
address={t.to}
|
||||||
|
miner={t.to === txData.confirmedData?.miner}
|
||||||
|
txFrom={t.to === txData.from}
|
||||||
|
txTo={t.to === txData.to}
|
||||||
|
/>
|
||||||
|
</AddressHighlighter>
|
||||||
|
</span>
|
||||||
|
<span>.</span>
|
||||||
|
<span>{raw4Bytes}</span>
|
||||||
|
<span>(</span>
|
||||||
|
{t.input.length > 10 && (
|
||||||
|
<span className="whitespace-nowrap">
|
||||||
|
input=[{t.input.slice(10)}]
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span>)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{t.children && (
|
||||||
|
<div className="flex">
|
||||||
|
<div className={`w-10 ${last ? "" : "border-l"}`}></div>
|
||||||
|
<div className="space-y-3">
|
||||||
|
{t.children.map((tc, i, a) => (
|
||||||
|
<TraceItem
|
||||||
|
key={i}
|
||||||
|
t={tc}
|
||||||
|
txData={txData}
|
||||||
|
last={i === a.length - 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(TraceItem);
|
|
@ -308,3 +308,89 @@ export const useInternalOperations = (
|
||||||
|
|
||||||
return intTransfers;
|
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<TraceGroup[] | undefined>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue