Add support for eoa/contract indicator on address input decoders; generalized indicator support by pushing it down to TransactionAddress component
This commit is contained in:
parent
0e73d4098a
commit
f1a9a2f65d
|
@ -1,4 +1,4 @@
|
||||||
import React, { useContext } from "react";
|
import React from "react";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faCaretRight } from "@fortawesome/free-solid-svg-icons/faCaretRight";
|
import { faCaretRight } from "@fortawesome/free-solid-svg-icons/faCaretRight";
|
||||||
import TransactionAddress from "./components/TransactionAddress";
|
import TransactionAddress from "./components/TransactionAddress";
|
||||||
|
@ -9,17 +9,13 @@ import {
|
||||||
ChecksummedAddress,
|
ChecksummedAddress,
|
||||||
TokenMeta,
|
TokenMeta,
|
||||||
TokenTransfer,
|
TokenTransfer,
|
||||||
TransactionData,
|
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { RuntimeContext } from "./useRuntime";
|
|
||||||
import { useHasCode } from "./useErigonHooks";
|
|
||||||
import { Metadata } from "./sourcify/useSourcify";
|
import { Metadata } from "./sourcify/useSourcify";
|
||||||
|
|
||||||
type TokenTransferItemProps = {
|
type TokenTransferItemProps = {
|
||||||
t: TokenTransfer;
|
t: TokenTransfer;
|
||||||
tokenMeta?: TokenMeta | null | undefined;
|
tokenMeta?: TokenMeta | null | undefined;
|
||||||
metadatas: Record<ChecksummedAddress, Metadata | null | undefined>;
|
metadatas: Record<ChecksummedAddress, Metadata | null | undefined>;
|
||||||
txData: TransactionData;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: handle partial
|
// TODO: handle partial
|
||||||
|
@ -27,20 +23,7 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
||||||
t,
|
t,
|
||||||
tokenMeta,
|
tokenMeta,
|
||||||
metadatas,
|
metadatas,
|
||||||
txData,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { provider } = useContext(RuntimeContext);
|
|
||||||
const fromHasCode = useHasCode(
|
|
||||||
provider,
|
|
||||||
t.from,
|
|
||||||
txData.confirmedData ? txData.confirmedData.blockNumber - 1 : undefined
|
|
||||||
);
|
|
||||||
const toHasCode = useHasCode(
|
|
||||||
provider,
|
|
||||||
t.to,
|
|
||||||
txData.confirmedData ? txData.confirmedData.blockNumber - 1 : undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-baseline space-x-2 px-2 py-1 truncate hover:bg-gray-100">
|
<div className="flex items-baseline space-x-2 px-2 py-1 truncate hover:bg-gray-100">
|
||||||
<span className="text-gray-500">
|
<span className="text-gray-500">
|
||||||
|
@ -53,7 +36,7 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
||||||
address={t.from}
|
address={t.from}
|
||||||
addressCtx={AddressContext.FROM}
|
addressCtx={AddressContext.FROM}
|
||||||
metadata={metadatas[t.from]}
|
metadata={metadatas[t.from]}
|
||||||
eoa={fromHasCode === undefined ? undefined : !fromHasCode}
|
showCodeIndicator
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 flex space-x-1">
|
<div className="col-span-2 flex space-x-1">
|
||||||
|
@ -62,7 +45,7 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
||||||
address={t.to}
|
address={t.to}
|
||||||
addressCtx={AddressContext.TO}
|
addressCtx={AddressContext.TO}
|
||||||
metadata={metadatas[t.to]}
|
metadata={metadatas[t.to]}
|
||||||
eoa={toHasCode === undefined ? undefined : !toHasCode}
|
showCodeIndicator
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-3 flex space-x-1">
|
<div className="col-span-3 flex space-x-1">
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { RuntimeContext } from "./useRuntime";
|
||||||
import { useInternalOperations, useTxData } from "./useErigonHooks";
|
import { useInternalOperations, useTxData } from "./useErigonHooks";
|
||||||
import { SelectionContext, useSelection } from "./useSelection";
|
import { SelectionContext, useSelection } from "./useSelection";
|
||||||
import { SelectedTransactionContext } from "./useSelectedTransaction";
|
import { SelectedTransactionContext } from "./useSelectedTransaction";
|
||||||
|
import { BlockNumberContext } from "./useBlockTagContext";
|
||||||
import { useETHUSDOracle } from "./usePriceOracle";
|
import { useETHUSDOracle } from "./usePriceOracle";
|
||||||
import { useAppConfigContext } from "./useAppConfig";
|
import { useAppConfigContext } from "./useAppConfig";
|
||||||
import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify";
|
import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify";
|
||||||
|
@ -74,56 +75,60 @@ const TransactionPageContent: React.FC<TransactionPageContentProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectedTransactionContext.Provider value={txData}>
|
<SelectedTransactionContext.Provider value={txData}>
|
||||||
<StandardFrame>
|
<BlockNumberContext.Provider value={txData?.confirmedData?.blockNumber}>
|
||||||
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
<StandardFrame>
|
||||||
{txData === null && (
|
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
||||||
<ContentFrame>
|
{txData === null && (
|
||||||
<div className="py-4 text-sm">
|
<ContentFrame>
|
||||||
Transaction <span className="font-hash">{txHash}</span> not found.
|
<div className="py-4 text-sm">
|
||||||
</div>
|
Transaction <span className="font-hash">{txHash}</span> not
|
||||||
</ContentFrame>
|
found.
|
||||||
)}
|
</div>
|
||||||
{txData && (
|
</ContentFrame>
|
||||||
<SelectionContext.Provider value={selectionCtx}>
|
)}
|
||||||
<Tab.Group>
|
{txData && (
|
||||||
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
|
<SelectionContext.Provider value={selectionCtx}>
|
||||||
<NavTab href=".">Overview</NavTab>
|
<Tab.Group>
|
||||||
{txData.confirmedData?.blockNumber !== undefined && (
|
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
|
||||||
<NavTab href="logs">
|
<NavTab href=".">Overview</NavTab>
|
||||||
Logs
|
{txData.confirmedData?.blockNumber !== undefined && (
|
||||||
{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`}
|
<NavTab href="logs">
|
||||||
</NavTab>
|
Logs
|
||||||
)}
|
{txData &&
|
||||||
<NavTab href="trace">Trace</NavTab>
|
` (${txData.confirmedData?.logs?.length ?? 0})`}
|
||||||
</Tab.List>
|
</NavTab>
|
||||||
</Tab.Group>
|
)}
|
||||||
<React.Suspense fallback={null}>
|
<NavTab href="trace">Trace</NavTab>
|
||||||
<Routes>
|
</Tab.List>
|
||||||
<Route
|
</Tab.Group>
|
||||||
index
|
<React.Suspense fallback={null}>
|
||||||
element={
|
<Routes>
|
||||||
<Details
|
<Route
|
||||||
txData={txData}
|
index
|
||||||
txDesc={txDesc}
|
element={
|
||||||
toMetadata={metadata}
|
<Details
|
||||||
userDoc={metadata?.output.userdoc}
|
txData={txData}
|
||||||
devDoc={metadata?.output.devdoc}
|
txDesc={txDesc}
|
||||||
internalOps={internalOps}
|
toMetadata={metadata}
|
||||||
sendsEthToMiner={sendsEthToMiner}
|
userDoc={metadata?.output.userdoc}
|
||||||
ethUSDPrice={blockETHUSDPrice}
|
devDoc={metadata?.output.devdoc}
|
||||||
/>
|
internalOps={internalOps}
|
||||||
}
|
sendsEthToMiner={sendsEthToMiner}
|
||||||
/>
|
ethUSDPrice={blockETHUSDPrice}
|
||||||
<Route
|
/>
|
||||||
path="logs"
|
}
|
||||||
element={<Logs txData={txData} metadata={metadata} />}
|
/>
|
||||||
/>
|
<Route
|
||||||
<Route path="trace" element={<Trace txData={txData} />} />
|
path="logs"
|
||||||
</Routes>
|
element={<Logs txData={txData} metadata={metadata} />}
|
||||||
</React.Suspense>
|
/>
|
||||||
</SelectionContext.Provider>
|
<Route path="trace" element={<Trace txData={txData} />} />
|
||||||
)}
|
</Routes>
|
||||||
</StandardFrame>
|
</React.Suspense>
|
||||||
|
</SelectionContext.Provider>
|
||||||
|
)}
|
||||||
|
</StandardFrame>
|
||||||
|
</BlockNumberContext.Provider>
|
||||||
</SelectedTransactionContext.Provider>
|
</SelectedTransactionContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,27 +1,38 @@
|
||||||
import React from "react";
|
import React, { useContext } from "react";
|
||||||
import AddressHighlighter from "./AddressHighlighter";
|
import AddressHighlighter from "./AddressHighlighter";
|
||||||
import DecoratedAddressLink from "./DecoratedAddressLink";
|
import DecoratedAddressLink from "./DecoratedAddressLink";
|
||||||
import { useSelectedTransaction } from "../useSelectedTransaction";
|
import { useSelectedTransaction } from "../useSelectedTransaction";
|
||||||
import { AddressContext } from "../types";
|
import { useBlockNumberContext } from "../useBlockTagContext";
|
||||||
|
import { RuntimeContext } from "../useRuntime";
|
||||||
|
import { useHasCode } from "../useErigonHooks";
|
||||||
import { Metadata } from "../sourcify/useSourcify";
|
import { Metadata } from "../sourcify/useSourcify";
|
||||||
|
import { AddressContext, ChecksummedAddress } from "../types";
|
||||||
|
|
||||||
type TransactionAddressProps = {
|
type TransactionAddressProps = {
|
||||||
address: string;
|
address: ChecksummedAddress;
|
||||||
addressCtx?: AddressContext | undefined;
|
addressCtx?: AddressContext | undefined;
|
||||||
metadata?: Metadata | null | undefined;
|
metadata?: Metadata | null | undefined;
|
||||||
eoa?: boolean | undefined;
|
showCodeIndicator?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TransactionAddress: React.FC<TransactionAddressProps> = ({
|
const TransactionAddress: React.FC<TransactionAddressProps> = ({
|
||||||
address,
|
address,
|
||||||
addressCtx,
|
addressCtx,
|
||||||
metadata,
|
metadata,
|
||||||
eoa,
|
showCodeIndicator = false,
|
||||||
}) => {
|
}) => {
|
||||||
const txData = useSelectedTransaction();
|
const txData = useSelectedTransaction();
|
||||||
// TODO: push down creation coloring logic into DecoratedAddressLink
|
// TODO: push down creation coloring logic into DecoratedAddressLink
|
||||||
const creation = address === txData?.confirmedData?.createdContractAddress;
|
const creation = address === txData?.confirmedData?.createdContractAddress;
|
||||||
|
|
||||||
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
const blockNumber = useBlockNumberContext();
|
||||||
|
const toHasCode = useHasCode(
|
||||||
|
provider,
|
||||||
|
address,
|
||||||
|
blockNumber !== undefined ? blockNumber - 1 : undefined
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AddressHighlighter address={address}>
|
<AddressHighlighter address={address}>
|
||||||
<DecoratedAddressLink
|
<DecoratedAddressLink
|
||||||
|
@ -32,7 +43,11 @@ const TransactionAddress: React.FC<TransactionAddressProps> = ({
|
||||||
txTo={address === txData?.to || creation}
|
txTo={address === txData?.to || creation}
|
||||||
creation={creation}
|
creation={creation}
|
||||||
metadata={metadata}
|
metadata={metadata}
|
||||||
eoa={eoa}
|
eoa={
|
||||||
|
showCodeIndicator !== undefined && blockNumber !== undefined
|
||||||
|
? !toHasCode
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</AddressHighlighter>
|
</AddressHighlighter>
|
||||||
);
|
);
|
||||||
|
|
|
@ -45,7 +45,7 @@ import {
|
||||||
import { DevDoc, Metadata, useError, UserDoc } from "../sourcify/useSourcify";
|
import { DevDoc, Metadata, useError, UserDoc } from "../sourcify/useSourcify";
|
||||||
import { RuntimeContext } from "../useRuntime";
|
import { RuntimeContext } from "../useRuntime";
|
||||||
import { useContractsMetadata } from "../hooks";
|
import { useContractsMetadata } from "../hooks";
|
||||||
import { useHasCode, useTransactionError } from "../useErigonHooks";
|
import { useTransactionError } from "../useErigonHooks";
|
||||||
|
|
||||||
type DetailsProps = {
|
type DetailsProps = {
|
||||||
txData: TransactionData;
|
txData: TransactionData;
|
||||||
|
@ -118,12 +118,6 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
: undefined;
|
: undefined;
|
||||||
const [expanded, setExpanded] = useState<boolean>(false);
|
const [expanded, setExpanded] = useState<boolean>(false);
|
||||||
|
|
||||||
const toHasCode = useHasCode(
|
|
||||||
provider,
|
|
||||||
txData.to,
|
|
||||||
txData.confirmedData ? txData.confirmedData.blockNumber - 1 : undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentFrame tabs>
|
<ContentFrame tabs>
|
||||||
<InfoRow title="Transaction Hash">
|
<InfoRow title="Transaction Hash">
|
||||||
|
@ -270,7 +264,7 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
<TransactionAddress
|
<TransactionAddress
|
||||||
address={txData.to}
|
address={txData.to}
|
||||||
metadata={metadatas?.[txData.to]}
|
metadata={metadatas?.[txData.to]}
|
||||||
eoa={toHasCode === undefined ? undefined : !toHasCode}
|
showCodeIndicator
|
||||||
/>
|
/>
|
||||||
<Copy value={txData.to} />
|
<Copy value={txData.to} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -314,7 +308,6 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
t={t}
|
t={t}
|
||||||
tokenMeta={txData.tokenMetas[t.token]}
|
tokenMeta={txData.tokenMetas[t.token]}
|
||||||
metadatas={metadatas}
|
metadatas={metadatas}
|
||||||
txData={txData}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</InfoRow>
|
</InfoRow>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useContext, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faBomb } from "@fortawesome/free-solid-svg-icons/faBomb";
|
import { faBomb } from "@fortawesome/free-solid-svg-icons/faBomb";
|
||||||
import TransactionAddress from "../components/TransactionAddress";
|
import TransactionAddress from "../components/TransactionAddress";
|
||||||
|
@ -6,8 +6,7 @@ import FormattedBalance from "../components/FormattedBalance";
|
||||||
import FunctionSignature from "./FunctionSignature";
|
import FunctionSignature from "./FunctionSignature";
|
||||||
import InputDecoder from "./decoder/InputDecoder";
|
import InputDecoder from "./decoder/InputDecoder";
|
||||||
import ExpanderSwitch from "../components/ExpanderSwitch";
|
import ExpanderSwitch from "../components/ExpanderSwitch";
|
||||||
import { RuntimeContext } from "../useRuntime";
|
import { TraceEntry } from "../useErigonHooks";
|
||||||
import { TraceEntry, useHasCode } from "../useErigonHooks";
|
|
||||||
import {
|
import {
|
||||||
extract4Bytes,
|
extract4Bytes,
|
||||||
use4Bytes,
|
use4Bytes,
|
||||||
|
@ -35,15 +34,6 @@ const TraceInput: React.FC<TraceInputProps> = ({ t, txData }) => {
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState<boolean>(false);
|
const [expanded, setExpanded] = useState<boolean>(false);
|
||||||
|
|
||||||
const { provider } = useContext(RuntimeContext);
|
|
||||||
const toHasCode = useHasCode(
|
|
||||||
provider,
|
|
||||||
t.to,
|
|
||||||
txData.confirmedData !== undefined
|
|
||||||
? txData.confirmedData.blockNumber - 1
|
|
||||||
: undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`ml-5 border hover:border-gray-500 rounded px-1 py-0.5 ${
|
className={`ml-5 border hover:border-gray-500 rounded px-1 py-0.5 ${
|
||||||
|
@ -59,10 +49,7 @@ const TraceInput: React.FC<TraceInputProps> = ({ t, txData }) => {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
<TransactionAddress
|
<TransactionAddress address={t.to} showCodeIndicator />
|
||||||
address={t.to}
|
|
||||||
eoa={toHasCode === undefined ? undefined : !toHasCode}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
{t.type !== "CREATE" && t.type !== "CREATE2" && (
|
{t.type !== "CREATE" && t.type !== "CREATE2" && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import TransactionAddress from "../../components/TransactionAddress";
|
import TransactionAddress from "../../components/TransactionAddress";
|
||||||
import Copy from "../../components/Copy";
|
import Copy from "../../components/Copy";
|
||||||
|
import { ChecksummedAddress } from "../../types";
|
||||||
|
|
||||||
type AddressDecoderProps = {
|
type AddressDecoderProps = {
|
||||||
r: string;
|
r: ChecksummedAddress;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddressDecoder: React.FC<AddressDecoderProps> = ({ r }) => (
|
const AddressDecoder: React.FC<AddressDecoderProps> = ({ r }) => (
|
||||||
<div className="flex items-baseline space-x-2 -ml-1 mr-3">
|
<div className="flex items-baseline space-x-2 -ml-1 mr-3">
|
||||||
<TransactionAddress address={r} />
|
<TransactionAddress address={r} showCodeIndicator />
|
||||||
<Copy value={r} />
|
<Copy value={r} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This context means child components have a temporal context expressed in
|
||||||
|
* terms of BlockTag.
|
||||||
|
*
|
||||||
|
* One obvious example is: child components want to show an UI indicator if
|
||||||
|
* the address is an EOA or contract. But if this address is currently a
|
||||||
|
* contract and it is an UI element of an existing transaction, it may be that
|
||||||
|
* at the time (block) the transaction happened it was still an EOA (create2),
|
||||||
|
* so it should be displayed as an EOA.
|
||||||
|
*/
|
||||||
|
export const BlockNumberContext = createContext<number | undefined>(undefined);
|
||||||
|
|
||||||
|
export const useBlockNumberContext = () => useContext(BlockNumberContext);
|
Loading…
Reference in New Issue