diff --git a/src/components/TransactionAddress.tsx b/src/components/TransactionAddress.tsx index a313b24..6a76dea 100644 --- a/src/components/TransactionAddress.tsx +++ b/src/components/TransactionAddress.tsx @@ -9,12 +9,14 @@ type TransactionAddressProps = { address: string; addressCtx?: AddressContext | undefined; metadata?: Metadata | null | undefined; + eoa?: boolean | undefined; }; const TransactionAddress: React.FC = ({ address, addressCtx, metadata, + eoa, }) => { const txData = useSelectedTransaction(); // TODO: push down creation coloring logic into DecoratedAddressLink @@ -30,6 +32,7 @@ const TransactionAddress: React.FC = ({ txTo={address === txData?.to || creation} creation={creation} metadata={metadata} + eoa={eoa} /> ); diff --git a/src/search/TransactionItem.tsx b/src/search/TransactionItem.tsx index daa0088..58de25b 100644 --- a/src/search/TransactionItem.tsx +++ b/src/search/TransactionItem.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useContext } from "react"; import { BlockTag } from "@ethersproject/abstract-provider"; import { BigNumber } from "@ethersproject/bignumber"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -16,6 +16,8 @@ import TransactionDirection, { import TransactionValue from "../components/TransactionValue"; import { ChecksummedAddress, ProcessedTransaction } from "../types"; import { FeeDisplay } from "./useFeeToggler"; +import { RuntimeContext } from "../useRuntime"; +import { useHasCode } from "../useErigonHooks"; import { formatValue } from "../components/formatter"; import ETH2USDValue from "../components/ETH2USDValue"; import { Metadata } from "../sourcify/useSourcify"; @@ -35,6 +37,13 @@ const TransactionItem: React.FC = ({ priceMap, metadatas, }) => { + const { provider } = useContext(RuntimeContext); + const toHasCode = useHasCode( + provider, + tx.to ?? undefined, + tx.blockNumber - 1 + ); + let direction: Direction | undefined; if (selectedAddress) { if (tx.from === selectedAddress && tx.to === selectedAddress) { @@ -107,6 +116,7 @@ const TransactionItem: React.FC = ({ selectedAddress={selectedAddress} miner={tx.miner === tx.to} metadata={metadatas[tx.to]} + eoa={toHasCode === undefined ? undefined : !toHasCode} /> ) : ( @@ -116,6 +126,7 @@ const TransactionItem: React.FC = ({ selectedAddress={selectedAddress} creation metadata={metadatas[tx.createdContractAddress!]} + eoa={false} /> )} diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 261e651..258cb84 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -45,7 +45,7 @@ import { import { DevDoc, Metadata, useError, UserDoc } from "../sourcify/useSourcify"; import { RuntimeContext } from "../useRuntime"; import { useContractsMetadata } from "../hooks"; -import { useTransactionError } from "../useErigonHooks"; +import { useHasCode, useTransactionError } from "../useErigonHooks"; type DetailsProps = { txData: TransactionData; @@ -118,6 +118,12 @@ const Details: React.FC = ({ : undefined; const [expanded, setExpanded] = useState(false); + const toHasCode = useHasCode( + provider, + txData.to, + txData.confirmedData ? txData.confirmedData.blockNumber - 1 : undefined + ); + return ( @@ -264,6 +270,7 @@ const Details: React.FC = ({ diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index cbc6208..b918230 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -1,5 +1,9 @@ import { useState, useEffect } from "react"; -import { Block, BlockWithTransactions } from "@ethersproject/abstract-provider"; +import { + Block, + BlockWithTransactions, + BlockTag, +} from "@ethersproject/abstract-provider"; import { JsonRpcProvider } from "@ethersproject/providers"; import { getAddress } from "@ethersproject/address"; import { Contract } from "@ethersproject/contracts"; @@ -679,3 +683,40 @@ export const useAddressBalance = ( return balance; }; + +/** + * This is a generic fetch for SWR, where the key is an array, whose + * element 0 is the JSON-RPC method, and the remaining are the method + * arguments. + */ +export const providerFetcher = + (provider: JsonRpcProvider | undefined) => + async (...key: any[]): Promise => { + if (provider === undefined) { + return undefined; + } + for (const a of key) { + if (a === undefined) { + return undefined; + } + } + + const method = key[0]; + const args = key.slice(1); + const result = await provider.send(method, args); + // console.log(`providerFetcher: ${method} ${args} === ${result}`); + return result; + }; + +export const useHasCode = ( + provider: JsonRpcProvider | undefined, + address: ChecksummedAddress | undefined, + blockTag: BlockTag = "latest" +): boolean | undefined => { + const fetcher = providerFetcher(provider); + const { data, error } = useSWR(["ots_hasCode", address, blockTag], fetcher); + if (error) { + return undefined; + } + return data as boolean | undefined; +};