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:
Willian Mitsuda 2022-03-24 15:20:54 -03:00
parent 0e73d4098a
commit f1a9a2f65d
7 changed files with 102 additions and 103 deletions

View File

@ -1,4 +1,4 @@
import React, { useContext } from "react";
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCaretRight } from "@fortawesome/free-solid-svg-icons/faCaretRight";
import TransactionAddress from "./components/TransactionAddress";
@ -9,17 +9,13 @@ import {
ChecksummedAddress,
TokenMeta,
TokenTransfer,
TransactionData,
} from "./types";
import { RuntimeContext } from "./useRuntime";
import { useHasCode } from "./useErigonHooks";
import { Metadata } from "./sourcify/useSourcify";
type TokenTransferItemProps = {
t: TokenTransfer;
tokenMeta?: TokenMeta | null | undefined;
metadatas: Record<ChecksummedAddress, Metadata | null | undefined>;
txData: TransactionData;
};
// TODO: handle partial
@ -27,20 +23,7 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
t,
tokenMeta,
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 (
<div className="flex items-baseline space-x-2 px-2 py-1 truncate hover:bg-gray-100">
<span className="text-gray-500">
@ -53,7 +36,7 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
address={t.from}
addressCtx={AddressContext.FROM}
metadata={metadatas[t.from]}
eoa={fromHasCode === undefined ? undefined : !fromHasCode}
showCodeIndicator
/>
</div>
<div className="col-span-2 flex space-x-1">
@ -62,7 +45,7 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
address={t.to}
addressCtx={AddressContext.TO}
metadata={metadatas[t.to]}
eoa={toHasCode === undefined ? undefined : !toHasCode}
showCodeIndicator
/>
</div>
<div className="col-span-3 flex space-x-1">

View File

@ -9,6 +9,7 @@ import { RuntimeContext } from "./useRuntime";
import { useInternalOperations, useTxData } from "./useErigonHooks";
import { SelectionContext, useSelection } from "./useSelection";
import { SelectedTransactionContext } from "./useSelectedTransaction";
import { BlockNumberContext } from "./useBlockTagContext";
import { useETHUSDOracle } from "./usePriceOracle";
import { useAppConfigContext } from "./useAppConfig";
import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify";
@ -74,56 +75,60 @@ const TransactionPageContent: React.FC<TransactionPageContentProps> = ({
return (
<SelectedTransactionContext.Provider value={txData}>
<StandardFrame>
<StandardSubtitle>Transaction Details</StandardSubtitle>
{txData === null && (
<ContentFrame>
<div className="py-4 text-sm">
Transaction <span className="font-hash">{txHash}</span> not found.
</div>
</ContentFrame>
)}
{txData && (
<SelectionContext.Provider value={selectionCtx}>
<Tab.Group>
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
<NavTab href=".">Overview</NavTab>
{txData.confirmedData?.blockNumber !== undefined && (
<NavTab href="logs">
Logs
{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`}
</NavTab>
)}
<NavTab href="trace">Trace</NavTab>
</Tab.List>
</Tab.Group>
<React.Suspense fallback={null}>
<Routes>
<Route
index
element={
<Details
txData={txData}
txDesc={txDesc}
toMetadata={metadata}
userDoc={metadata?.output.userdoc}
devDoc={metadata?.output.devdoc}
internalOps={internalOps}
sendsEthToMiner={sendsEthToMiner}
ethUSDPrice={blockETHUSDPrice}
/>
}
/>
<Route
path="logs"
element={<Logs txData={txData} metadata={metadata} />}
/>
<Route path="trace" element={<Trace txData={txData} />} />
</Routes>
</React.Suspense>
</SelectionContext.Provider>
)}
</StandardFrame>
<BlockNumberContext.Provider value={txData?.confirmedData?.blockNumber}>
<StandardFrame>
<StandardSubtitle>Transaction Details</StandardSubtitle>
{txData === null && (
<ContentFrame>
<div className="py-4 text-sm">
Transaction <span className="font-hash">{txHash}</span> not
found.
</div>
</ContentFrame>
)}
{txData && (
<SelectionContext.Provider value={selectionCtx}>
<Tab.Group>
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
<NavTab href=".">Overview</NavTab>
{txData.confirmedData?.blockNumber !== undefined && (
<NavTab href="logs">
Logs
{txData &&
` (${txData.confirmedData?.logs?.length ?? 0})`}
</NavTab>
)}
<NavTab href="trace">Trace</NavTab>
</Tab.List>
</Tab.Group>
<React.Suspense fallback={null}>
<Routes>
<Route
index
element={
<Details
txData={txData}
txDesc={txDesc}
toMetadata={metadata}
userDoc={metadata?.output.userdoc}
devDoc={metadata?.output.devdoc}
internalOps={internalOps}
sendsEthToMiner={sendsEthToMiner}
ethUSDPrice={blockETHUSDPrice}
/>
}
/>
<Route
path="logs"
element={<Logs txData={txData} metadata={metadata} />}
/>
<Route path="trace" element={<Trace txData={txData} />} />
</Routes>
</React.Suspense>
</SelectionContext.Provider>
)}
</StandardFrame>
</BlockNumberContext.Provider>
</SelectedTransactionContext.Provider>
);
};

View File

@ -1,27 +1,38 @@
import React from "react";
import React, { useContext } from "react";
import AddressHighlighter from "./AddressHighlighter";
import DecoratedAddressLink from "./DecoratedAddressLink";
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 { AddressContext, ChecksummedAddress } from "../types";
type TransactionAddressProps = {
address: string;
address: ChecksummedAddress;
addressCtx?: AddressContext | undefined;
metadata?: Metadata | null | undefined;
eoa?: boolean | undefined;
showCodeIndicator?: boolean;
};
const TransactionAddress: React.FC<TransactionAddressProps> = ({
address,
addressCtx,
metadata,
eoa,
showCodeIndicator = false,
}) => {
const txData = useSelectedTransaction();
// TODO: push down creation coloring logic into DecoratedAddressLink
const creation = address === txData?.confirmedData?.createdContractAddress;
const { provider } = useContext(RuntimeContext);
const blockNumber = useBlockNumberContext();
const toHasCode = useHasCode(
provider,
address,
blockNumber !== undefined ? blockNumber - 1 : undefined
);
return (
<AddressHighlighter address={address}>
<DecoratedAddressLink
@ -32,7 +43,11 @@ const TransactionAddress: React.FC<TransactionAddressProps> = ({
txTo={address === txData?.to || creation}
creation={creation}
metadata={metadata}
eoa={eoa}
eoa={
showCodeIndicator !== undefined && blockNumber !== undefined
? !toHasCode
: undefined
}
/>
</AddressHighlighter>
);

View File

@ -45,7 +45,7 @@ import {
import { DevDoc, Metadata, useError, UserDoc } from "../sourcify/useSourcify";
import { RuntimeContext } from "../useRuntime";
import { useContractsMetadata } from "../hooks";
import { useHasCode, useTransactionError } from "../useErigonHooks";
import { useTransactionError } from "../useErigonHooks";
type DetailsProps = {
txData: TransactionData;
@ -118,12 +118,6 @@ const Details: React.FC<DetailsProps> = ({
: undefined;
const [expanded, setExpanded] = useState<boolean>(false);
const toHasCode = useHasCode(
provider,
txData.to,
txData.confirmedData ? txData.confirmedData.blockNumber - 1 : undefined
);
return (
<ContentFrame tabs>
<InfoRow title="Transaction Hash">
@ -270,7 +264,7 @@ const Details: React.FC<DetailsProps> = ({
<TransactionAddress
address={txData.to}
metadata={metadatas?.[txData.to]}
eoa={toHasCode === undefined ? undefined : !toHasCode}
showCodeIndicator
/>
<Copy value={txData.to} />
</div>
@ -314,7 +308,6 @@ const Details: React.FC<DetailsProps> = ({
t={t}
tokenMeta={txData.tokenMetas[t.token]}
metadatas={metadatas}
txData={txData}
/>
))}
</InfoRow>

View File

@ -1,4 +1,4 @@
import React, { useContext, useState } from "react";
import React, { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faBomb } from "@fortawesome/free-solid-svg-icons/faBomb";
import TransactionAddress from "../components/TransactionAddress";
@ -6,8 +6,7 @@ import FormattedBalance from "../components/FormattedBalance";
import FunctionSignature from "./FunctionSignature";
import InputDecoder from "./decoder/InputDecoder";
import ExpanderSwitch from "../components/ExpanderSwitch";
import { RuntimeContext } from "../useRuntime";
import { TraceEntry, useHasCode } from "../useErigonHooks";
import { TraceEntry } from "../useErigonHooks";
import {
extract4Bytes,
use4Bytes,
@ -35,15 +34,6 @@ const TraceInput: React.FC<TraceInputProps> = ({ t, txData }) => {
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 (
<div
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>
<TransactionAddress
address={t.to}
eoa={toHasCode === undefined ? undefined : !toHasCode}
/>
<TransactionAddress address={t.to} showCodeIndicator />
</span>
{t.type !== "CREATE" && t.type !== "CREATE2" && (
<>

View File

@ -1,14 +1,15 @@
import React from "react";
import TransactionAddress from "../../components/TransactionAddress";
import Copy from "../../components/Copy";
import { ChecksummedAddress } from "../../types";
type AddressDecoderProps = {
r: string;
r: ChecksummedAddress;
};
const AddressDecoder: React.FC<AddressDecoderProps> = ({ r }) => (
<div className="flex items-baseline space-x-2 -ml-1 mr-3">
<TransactionAddress address={r} />
<TransactionAddress address={r} showCodeIndicator />
<Copy value={r} />
</div>
);

View File

@ -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);