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 { 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">

View File

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

View File

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

View File

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

View File

@ -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" && (
<> <>

View File

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

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