Merge branch 'feature/extract-address-resolvers' into develop

This commit is contained in:
Willian Mitsuda 2021-10-30 22:46:31 -03:00
commit d41ab4330a
20 changed files with 268 additions and 121 deletions

View File

@ -26,7 +26,7 @@ import PendingResults from "./search/PendingResults";
import TransactionItem from "./search/TransactionItem"; import TransactionItem from "./search/TransactionItem";
import { SearchController } from "./search/search"; import { SearchController } from "./search/search";
import { RuntimeContext } from "./useRuntime"; import { RuntimeContext } from "./useRuntime";
import { useENSCache } from "./useReverseCache"; import { pageCollector, useResolvedAddresses } from "./useResolvedAddresses";
import { useFeeToggler } from "./search/useFeeToggler"; import { useFeeToggler } from "./search/useFeeToggler";
import { SelectionContext, useSelection } from "./useSelection"; import { SelectionContext, useSelection } from "./useSelection";
import { useMultipleETHUSDOracle } from "./usePriceOracle"; import { useMultipleETHUSDOracle } from "./usePriceOracle";
@ -165,7 +165,8 @@ const AddressTransactions: React.FC = () => {
}, [provider, checksummedAddress, params.direction, hash, controller]); }, [provider, checksummedAddress, params.direction, hash, controller]);
const page = useMemo(() => controller?.getPage(), [controller]); const page = useMemo(() => controller?.getPage(), [controller]);
const reverseCache = useENSCache(provider, page); const addrCollector = useMemo(() => pageCollector(page), [page]);
const resolvedAddresses = useResolvedAddresses(provider, addrCollector);
const blockTags: BlockTag[] = useMemo(() => { const blockTags: BlockTag[] = useMemo(() => {
if (!page) { if (!page) {
@ -277,13 +278,13 @@ const AddressTransactions: React.FC = () => {
feeDisplay={feeDisplay} feeDisplay={feeDisplay}
feeDisplayToggler={feeDisplayToggler} feeDisplayToggler={feeDisplayToggler}
/> />
{controller ? ( {page ? (
<SelectionContext.Provider value={selectionCtx}> <SelectionContext.Provider value={selectionCtx}>
{controller.getPage().map((tx) => ( {page.map((tx) => (
<TransactionItem <TransactionItem
key={tx.hash} key={tx.hash}
tx={tx} tx={tx}
ensCache={reverseCache} resolvedAddresses={resolvedAddresses}
selectedAddress={checksummedAddress} selectedAddress={checksummedAddress}
feeDisplay={feeDisplay} feeDisplay={feeDisplay}
priceMap={priceMap} priceMap={priceMap}

View File

@ -11,6 +11,10 @@ import { useInternalOperations, useTxData } from "./useErigonHooks";
import { useETHUSDOracle } from "./usePriceOracle"; import { useETHUSDOracle } from "./usePriceOracle";
import { useAppConfigContext } from "./useAppConfig"; import { useAppConfigContext } from "./useAppConfig";
import { useSourcify, useTransactionDescription } from "./useSourcify"; import { useSourcify, useTransactionDescription } from "./useSourcify";
import {
transactionDataCollector,
useResolvedAddresses,
} from "./useResolvedAddresses";
const Details = React.lazy( const Details = React.lazy(
() => () =>
@ -36,6 +40,11 @@ const Transaction: React.FC = () => {
const { txhash } = params; const { txhash } = params;
const txData = useTxData(provider, txhash); const txData = useTxData(provider, txhash);
const addrCollector = useMemo(
() => transactionDataCollector(txData),
[txData]
);
const resolvedAddresses = useResolvedAddresses(provider, addrCollector);
const internalOps = useInternalOperations(provider, txData); const internalOps = useInternalOperations(provider, txData);
const sendsEthToMiner = useMemo(() => { const sendsEthToMiner = useMemo(() => {
@ -100,10 +109,15 @@ const Transaction: React.FC = () => {
internalOps={internalOps} internalOps={internalOps}
sendsEthToMiner={sendsEthToMiner} sendsEthToMiner={sendsEthToMiner}
ethUSDPrice={blockETHUSDPrice} ethUSDPrice={blockETHUSDPrice}
resolvedAddresses={resolvedAddresses}
/> />
</Route> </Route>
<Route path="/tx/:txhash/logs/" exact> <Route path="/tx/:txhash/logs/" exact>
<Logs txData={txData} metadata={metadata} /> <Logs
txData={txData}
metadata={metadata}
resolvedAddresses={resolvedAddresses}
/>
</Route> </Route>
</Switch> </Switch>
</React.Suspense> </React.Suspense>

View File

@ -0,0 +1,26 @@
import { BaseProvider } from "@ethersproject/providers";
import { IAddressResolver } from "./address-resolver";
export class CompositeAddressResolver implements IAddressResolver {
private resolvers: IAddressResolver[] = [];
addResolver(resolver: IAddressResolver) {
this.resolvers.push(resolver);
}
async resolveAddress(
provider: BaseProvider,
address: string
): Promise<string | undefined> {
for (const r of this.resolvers) {
const name = r.resolveAddress(provider, address);
if (name !== undefined) {
return name;
}
}
return undefined;
// TODO: fallback to address itself
// return address;
}
}

View File

@ -0,0 +1,15 @@
import { BaseProvider } from "@ethersproject/providers";
import { IAddressResolver } from "./address-resolver";
export class ENSAddressResolver implements IAddressResolver {
async resolveAddress(
provider: BaseProvider,
address: string
): Promise<string | undefined> {
const name = await provider.lookupAddress(address);
if (name === null) {
return undefined;
}
return name;
}
}

View File

@ -0,0 +1,8 @@
import { BaseProvider } from "@ethersproject/providers";
export interface IAddressResolver {
resolveAddress(
provider: BaseProvider,
address: string
): Promise<string | undefined>;
}

View File

@ -0,0 +1,34 @@
import { BaseProvider } from "@ethersproject/providers";
import { IAddressResolver } from "./address-resolver";
import { CompositeAddressResolver } from "./CompositeAddressResolver";
import { ENSAddressResolver } from "./ENSAddressResolver";
export type ResolvedAddresses = Record<string, string>;
// Create and configure the main resolver
const _mainResolver = new CompositeAddressResolver();
_mainResolver.addResolver(new ENSAddressResolver());
export const mainResolver: IAddressResolver = _mainResolver;
export const batchPopulate = async (
provider: BaseProvider,
addresses: string[]
): Promise<ResolvedAddresses> => {
const solvers: Promise<string | undefined>[] = [];
for (const a of addresses) {
solvers.push(mainResolver.resolveAddress(provider, a));
}
const results = await Promise.all(solvers);
const cache: ResolvedAddresses = {};
for (let i = 0; i < results.length; i++) {
const r = results[i];
if (r === undefined) {
continue;
}
cache[addresses[i]] = r;
}
return cache;
};

View File

@ -8,7 +8,7 @@ import TransactionItem from "../search/TransactionItem";
import { useFeeToggler } from "../search/useFeeToggler"; import { useFeeToggler } from "../search/useFeeToggler";
import { RuntimeContext } from "../useRuntime"; import { RuntimeContext } from "../useRuntime";
import { SelectionContext, useSelection } from "../useSelection"; import { SelectionContext, useSelection } from "../useSelection";
import { useENSCache } from "../useReverseCache"; import { pageCollector, useResolvedAddresses } from "../useResolvedAddresses";
import { ProcessedTransaction } from "../types"; import { ProcessedTransaction } from "../types";
import { PAGE_SIZE } from "../params"; import { PAGE_SIZE } from "../params";
import { useMultipleETHUSDOracle } from "../usePriceOracle"; import { useMultipleETHUSDOracle } from "../usePriceOracle";
@ -29,7 +29,8 @@ const BlockTransactionResults: React.FC<BlockTransactionResultsProps> = ({
const selectionCtx = useSelection(); const selectionCtx = useSelection();
const [feeDisplay, feeDisplayToggler] = useFeeToggler(); const [feeDisplay, feeDisplayToggler] = useFeeToggler();
const { provider } = useContext(RuntimeContext); const { provider } = useContext(RuntimeContext);
const reverseCache = useENSCache(provider, page); const addrCollector = useMemo(() => pageCollector(page), [page]);
const resolvedAddresses = useResolvedAddresses(provider, addrCollector);
const blockTags = useMemo(() => [blockTag], [blockTag]); const blockTags = useMemo(() => [blockTag], [blockTag]);
const priceMap = useMultipleETHUSDOracle(provider, blockTags); const priceMap = useMultipleETHUSDOracle(provider, blockTags);
@ -59,7 +60,7 @@ const BlockTransactionResults: React.FC<BlockTransactionResultsProps> = ({
<TransactionItem <TransactionItem
key={tx.hash} key={tx.hash}
tx={tx} tx={tx}
ensCache={reverseCache} resolvedAddresses={resolvedAddresses}
feeDisplay={feeDisplay} feeDisplay={feeDisplay}
priceMap={priceMap} priceMap={priceMap}
/> />

View File

@ -6,8 +6,8 @@ type AddressProps = {
const Address: React.FC<AddressProps> = ({ address }) => ( const Address: React.FC<AddressProps> = ({ address }) => (
<span className="font-address text-gray-400 truncate" title={address}> <span className="font-address text-gray-400 truncate" title={address}>
<span className="truncate">{address}</span> {address}
</span> </span>
); );
export default React.memo(Address); export default Address;

View File

@ -17,11 +17,10 @@ const AddressLink: React.FC<AddressLinkProps> = ({
dontOverrideColors ? "" : "text-link-blue hover:text-link-blue-hover" dontOverrideColors ? "" : "text-link-blue hover:text-link-blue-hover"
} font-address truncate`} } font-address truncate`}
to={`/address/${address}`} to={`/address/${address}`}
title={text ?? address}
> >
<span className="truncate" title={text ?? address}> {text ?? address}
{text ?? address}
</span>
</NavLink> </NavLink>
); );
export default React.memo(AddressLink); export default AddressLink;

View File

@ -3,49 +3,53 @@ import Address from "./Address";
import AddressLink from "./AddressLink"; import AddressLink from "./AddressLink";
import ENSName from "./ENSName"; import ENSName from "./ENSName";
import ENSNameLink from "./ENSNameLink"; import ENSNameLink from "./ENSNameLink";
import { ResolvedAddresses } from "../api/address-resolver";
type AddressOrENSNameProps = { type AddressOrENSNameProps = {
address: string; address: string;
ensName?: string;
selectedAddress?: string; selectedAddress?: string;
text?: string; text?: string;
dontOverrideColors?: boolean; dontOverrideColors?: boolean;
resolvedAddresses?: ResolvedAddresses | undefined;
}; };
const AddressOrENSName: React.FC<AddressOrENSNameProps> = ({ const AddressOrENSName: React.FC<AddressOrENSNameProps> = ({
address, address,
ensName,
selectedAddress, selectedAddress,
text, text,
dontOverrideColors, dontOverrideColors,
}) => ( resolvedAddresses,
<> }) => {
{address === selectedAddress ? ( const name = resolvedAddresses?.[address];
<> return (
{ensName ? ( <>
<ENSName name={ensName} address={address} /> {address === selectedAddress ? (
) : ( <>
<Address address={address} /> {name ? (
)} <ENSName name={name} address={address} />
</> ) : (
) : ( <Address address={address} />
<> )}
{ensName ? ( </>
<ENSNameLink ) : (
name={ensName} <>
address={address} {name ? (
dontOverrideColors={dontOverrideColors} <ENSNameLink
/> name={name}
) : ( address={address}
<AddressLink dontOverrideColors={dontOverrideColors}
address={address} />
text={text} ) : (
dontOverrideColors={dontOverrideColors} <AddressLink
/> address={address}
)} text={text}
</> dontOverrideColors={dontOverrideColors}
)} />
</> )}
); </>
)}
</>
);
};
export default React.memo(AddressOrENSName); export default AddressOrENSName;

View File

@ -8,10 +8,10 @@ import { faCoins } from "@fortawesome/free-solid-svg-icons/faCoins";
import TokenLogo from "./TokenLogo"; import TokenLogo from "./TokenLogo";
import AddressOrENSName from "./AddressOrENSName"; import AddressOrENSName from "./AddressOrENSName";
import { AddressContext, TokenMeta, ZERO_ADDRESS } from "../types"; import { AddressContext, TokenMeta, ZERO_ADDRESS } from "../types";
import { ResolvedAddresses } from "../api/address-resolver";
type DecoratedAddressLinkProps = { type DecoratedAddressLinkProps = {
address: string; address: string;
ensName?: string;
selectedAddress?: string; selectedAddress?: string;
text?: string; text?: string;
addressCtx?: AddressContext; addressCtx?: AddressContext;
@ -21,11 +21,11 @@ type DecoratedAddressLinkProps = {
txFrom?: boolean; txFrom?: boolean;
txTo?: boolean; txTo?: boolean;
tokenMeta?: TokenMeta; tokenMeta?: TokenMeta;
resolvedAddresses?: ResolvedAddresses | undefined;
}; };
const DecoratedAddresssLink: React.FC<DecoratedAddressLinkProps> = ({ const DecoratedAddressLink: React.FC<DecoratedAddressLinkProps> = ({
address, address,
ensName,
selectedAddress, selectedAddress,
text, text,
addressCtx, addressCtx,
@ -35,6 +35,7 @@ const DecoratedAddresssLink: React.FC<DecoratedAddressLinkProps> = ({
txFrom, txFrom,
txTo, txTo,
tokenMeta, tokenMeta,
resolvedAddresses,
}) => { }) => {
const mint = addressCtx === AddressContext.FROM && address === ZERO_ADDRESS; const mint = addressCtx === AddressContext.FROM && address === ZERO_ADDRESS;
const burn = addressCtx === AddressContext.TO && address === ZERO_ADDRESS; const burn = addressCtx === AddressContext.TO && address === ZERO_ADDRESS;
@ -81,13 +82,13 @@ const DecoratedAddresssLink: React.FC<DecoratedAddressLinkProps> = ({
)} )}
<AddressOrENSName <AddressOrENSName
address={address} address={address}
ensName={ensName}
selectedAddress={selectedAddress} selectedAddress={selectedAddress}
text={text} text={text}
dontOverrideColors={mint || burn} dontOverrideColors={mint || burn}
resolvedAddresses={resolvedAddresses}
/> />
</div> </div>
); );
}; };
export default React.memo(DecoratedAddresssLink); export default React.memo(DecoratedAddressLink);

View File

@ -22,4 +22,4 @@ const ENSName: React.FC<ENSNameProps> = ({ name, address }) => (
</div> </div>
); );
export default React.memo(ENSName); export default ENSName;

View File

@ -31,4 +31,4 @@ const ENSNameLink: React.FC<ENSNameLinkProps> = ({
</NavLink> </NavLink>
); );
export default React.memo(ENSNameLink); export default ENSNameLink;

View File

@ -14,14 +14,15 @@ import TransactionDirection, {
Flags, Flags,
} from "../components/TransactionDirection"; } from "../components/TransactionDirection";
import TransactionValue from "../components/TransactionValue"; import TransactionValue from "../components/TransactionValue";
import { ENSReverseCache, ProcessedTransaction } from "../types"; import { ProcessedTransaction } from "../types";
import { FeeDisplay } from "./useFeeToggler"; import { FeeDisplay } from "./useFeeToggler";
import { formatValue } from "../components/formatter"; import { formatValue } from "../components/formatter";
import ETH2USDValue from "../components/ETH2USDValue"; import ETH2USDValue from "../components/ETH2USDValue";
import { ResolvedAddresses } from "../api/address-resolver";
type TransactionItemProps = { type TransactionItemProps = {
tx: ProcessedTransaction; tx: ProcessedTransaction;
ensCache?: ENSReverseCache; resolvedAddresses?: ResolvedAddresses;
selectedAddress?: string; selectedAddress?: string;
feeDisplay: FeeDisplay; feeDisplay: FeeDisplay;
priceMap: Record<BlockTag, BigNumber>; priceMap: Record<BlockTag, BigNumber>;
@ -29,7 +30,7 @@ type TransactionItemProps = {
const TransactionItem: React.FC<TransactionItemProps> = ({ const TransactionItem: React.FC<TransactionItemProps> = ({
tx, tx,
ensCache, resolvedAddresses,
selectedAddress, selectedAddress,
feeDisplay, feeDisplay,
priceMap, priceMap,
@ -50,12 +51,6 @@ const TransactionItem: React.FC<TransactionItemProps> = ({
} }
} }
const ensFrom = ensCache && tx.from && ensCache[tx.from];
const ensTo = ensCache && tx.to && ensCache[tx.to];
const ensCreated =
ensCache &&
tx.createdContractAddress &&
ensCache[tx.createdContractAddress];
const flash = tx.gasPrice.isZero() && tx.internalMinerInteraction; const flash = tx.gasPrice.isZero() && tx.internalMinerInteraction;
return ( return (
@ -87,9 +82,9 @@ const TransactionItem: React.FC<TransactionItemProps> = ({
<AddressHighlighter address={tx.from}> <AddressHighlighter address={tx.from}>
<DecoratedAddressLink <DecoratedAddressLink
address={tx.from} address={tx.from}
ensName={ensFrom}
selectedAddress={selectedAddress} selectedAddress={selectedAddress}
miner={tx.miner === tx.from} miner={tx.miner === tx.from}
resolvedAddresses={resolvedAddresses}
/> />
</AddressHighlighter> </AddressHighlighter>
)} )}
@ -107,18 +102,18 @@ const TransactionItem: React.FC<TransactionItemProps> = ({
<AddressHighlighter address={tx.to}> <AddressHighlighter address={tx.to}>
<DecoratedAddressLink <DecoratedAddressLink
address={tx.to} address={tx.to}
ensName={ensTo}
selectedAddress={selectedAddress} selectedAddress={selectedAddress}
miner={tx.miner === tx.to} miner={tx.miner === tx.to}
resolvedAddresses={resolvedAddresses}
/> />
</AddressHighlighter> </AddressHighlighter>
) : ( ) : (
<AddressHighlighter address={tx.createdContractAddress!}> <AddressHighlighter address={tx.createdContractAddress!}>
<DecoratedAddressLink <DecoratedAddressLink
address={tx.createdContractAddress!} address={tx.createdContractAddress!}
ensName={ensCreated}
selectedAddress={selectedAddress} selectedAddress={selectedAddress}
creation creation
resolvedAddresses={resolvedAddresses}
/> />
</AddressHighlighter> </AddressHighlighter>
)} )}
@ -144,4 +139,4 @@ const TransactionItem: React.FC<TransactionItemProps> = ({
); );
}; };
export default React.memo(TransactionItem); export default TransactionItem;

View File

@ -38,6 +38,7 @@ import ModeTab from "../components/ModeTab";
import DecodedParamsTable from "./decoder/DecodedParamsTable"; import DecodedParamsTable from "./decoder/DecodedParamsTable";
import { rawInputTo4Bytes, use4Bytes } from "../use4Bytes"; import { rawInputTo4Bytes, use4Bytes } from "../use4Bytes";
import { DevDoc, UserDoc } from "../useSourcify"; import { DevDoc, UserDoc } from "../useSourcify";
import { ResolvedAddresses } from "../api/address-resolver";
type DetailsProps = { type DetailsProps = {
txData: TransactionData; txData: TransactionData;
@ -47,6 +48,7 @@ type DetailsProps = {
internalOps?: InternalOperation[]; internalOps?: InternalOperation[];
sendsEthToMiner: boolean; sendsEthToMiner: boolean;
ethUSDPrice: BigNumber | undefined; ethUSDPrice: BigNumber | undefined;
resolvedAddresses: ResolvedAddresses | undefined;
}; };
const Details: React.FC<DetailsProps> = ({ const Details: React.FC<DetailsProps> = ({
@ -57,6 +59,7 @@ const Details: React.FC<DetailsProps> = ({
internalOps, internalOps,
sendsEthToMiner, sendsEthToMiner,
ethUSDPrice, ethUSDPrice,
resolvedAddresses,
}) => { }) => {
const hasEIP1559 = const hasEIP1559 =
txData.confirmedData?.blockBaseFeePerGas !== undefined && txData.confirmedData?.blockBaseFeePerGas !== undefined &&
@ -154,6 +157,7 @@ const Details: React.FC<DetailsProps> = ({
address={txData.from} address={txData.from}
miner={txData.from === txData.confirmedData?.miner} miner={txData.from === txData.confirmedData?.miner}
txFrom txFrom
resolvedAddresses={resolvedAddresses}
/> />
</AddressHighlighter> </AddressHighlighter>
<Copy value={txData.from} /> <Copy value={txData.from} />
@ -171,6 +175,7 @@ const Details: React.FC<DetailsProps> = ({
address={txData.to} address={txData.to}
miner={txData.to === txData.confirmedData?.miner} miner={txData.to === txData.confirmedData?.miner}
txTo txTo
resolvedAddresses={resolvedAddresses}
/> />
</AddressHighlighter> </AddressHighlighter>
<Copy value={txData.to} /> <Copy value={txData.to} />
@ -188,6 +193,7 @@ const Details: React.FC<DetailsProps> = ({
address={txData.confirmedData.createdContractAddress!} address={txData.confirmedData.createdContractAddress!}
creation creation
txTo txTo
resolvedAddresses={resolvedAddresses}
/> />
</AddressHighlighter> </AddressHighlighter>
<Copy value={txData.confirmedData.createdContractAddress!} /> <Copy value={txData.confirmedData.createdContractAddress!} />

View File

@ -10,14 +10,21 @@ import DecodedParamsTable from "./decoder/DecodedParamsTable";
import DecodedLogSignature from "./decoder/DecodedLogSignature"; import DecodedLogSignature from "./decoder/DecodedLogSignature";
import { TransactionData } from "../types"; import { TransactionData } from "../types";
import { useTopic0 } from "../useTopic0"; import { useTopic0 } from "../useTopic0";
import { ResolvedAddresses } from "../api/address-resolver";
type LogEntryProps = { type LogEntryProps = {
txData: TransactionData; txData: TransactionData;
log: Log; log: Log;
logDesc: LogDescription | null | undefined; logDesc: LogDescription | null | undefined;
resolvedAddresses: ResolvedAddresses | undefined;
}; };
const LogEntry: React.FC<LogEntryProps> = ({ txData, log, logDesc }) => { const LogEntry: React.FC<LogEntryProps> = ({
txData,
log,
logDesc,
resolvedAddresses,
}) => {
const rawTopic0 = log.topics[0]; const rawTopic0 = log.topics[0];
const topic0 = useTopic0(rawTopic0); const topic0 = useTopic0(rawTopic0);
@ -62,6 +69,7 @@ const LogEntry: React.FC<LogEntryProps> = ({ txData, log, logDesc }) => {
miner={log.address === txData.confirmedData?.miner} miner={log.address === txData.confirmedData?.miner}
txFrom={log.address === txData.from} txFrom={log.address === txData.from}
txTo={log.address === txData.to} txTo={log.address === txData.to}
resolvedAddresses={resolvedAddresses}
/> />
</AddressHighlighter> </AddressHighlighter>
<Copy value={log.address} /> <Copy value={log.address} />

View File

@ -5,13 +5,15 @@ import LogEntry from "./LogEntry";
import { TransactionData } from "../types"; import { TransactionData } from "../types";
import { useAppConfigContext } from "../useAppConfig"; import { useAppConfigContext } from "../useAppConfig";
import { Metadata, useMultipleMetadata } from "../useSourcify"; import { Metadata, useMultipleMetadata } from "../useSourcify";
import { ResolvedAddresses } from "../api/address-resolver";
type LogsProps = { type LogsProps = {
txData: TransactionData; txData: TransactionData;
metadata: Metadata | null | undefined; metadata: Metadata | null | undefined;
resolvedAddresses: ResolvedAddresses | undefined;
}; };
const Logs: React.FC<LogsProps> = ({ txData, metadata }) => { const Logs: React.FC<LogsProps> = ({ txData, metadata, resolvedAddresses }) => {
const baseMetadatas = useMemo((): Record<string, Metadata | null> => { const baseMetadatas = useMemo((): Record<string, Metadata | null> => {
if (!txData.to || metadata === undefined) { if (!txData.to || metadata === undefined) {
return {}; return {};
@ -70,6 +72,7 @@ const Logs: React.FC<LogsProps> = ({ txData, metadata }) => {
txData={txData} txData={txData}
log={l} log={l}
logDesc={logDescs?.[i]} logDesc={logDescs?.[i]}
resolvedAddresses={resolvedAddresses}
/> />
))} ))}
</> </>

View File

@ -32,10 +32,6 @@ export type TransactionChunk = {
lastPage: boolean; lastPage: boolean;
}; };
export type ENSReverseCache = {
[address: string]: string;
};
export type TransactionData = { export type TransactionData = {
transactionHash: string; transactionHash: string;
from: string; from: string;

View File

@ -0,0 +1,84 @@
import { useState, useEffect } from "react";
import { JsonRpcProvider } from "@ethersproject/providers";
import { ProcessedTransaction, TransactionData } from "./types";
import { batchPopulate, ResolvedAddresses } from "./api/address-resolver";
export type AddressCollector = () => string[];
export const pageCollector =
(page: ProcessedTransaction[] | undefined): AddressCollector =>
() => {
if (!page) {
return [];
}
const uniqueAddresses = new Set<string>();
for (const tx of page) {
if (tx.from) {
uniqueAddresses.add(tx.from);
}
if (tx.to) {
uniqueAddresses.add(tx.to);
}
}
return Array.from(uniqueAddresses);
};
export const transactionDataCollector =
(txData: TransactionData | null | undefined): AddressCollector =>
() => {
if (!txData) {
return [];
}
const uniqueAddresses = new Set<string>();
// Standard fields
uniqueAddresses.add(txData.from);
if (txData.to) {
uniqueAddresses.add(txData.to);
}
if (txData.confirmedData?.createdContractAddress) {
uniqueAddresses.add(txData.confirmedData?.createdContractAddress);
}
// Dig token transfers
for (const t of txData.tokenTransfers) {
uniqueAddresses.add(t.from);
uniqueAddresses.add(t.to);
uniqueAddresses.add(t.token);
}
// Dig log addresses
if (txData.confirmedData) {
for (const l of txData.confirmedData.logs) {
uniqueAddresses.add(l.address);
// TODO: find a way to dig over decoded address log attributes
}
}
return Array.from(uniqueAddresses);
};
export const useResolvedAddresses = (
provider: JsonRpcProvider | undefined,
addrCollector: AddressCollector
) => {
const [names, setNames] = useState<ResolvedAddresses>();
useEffect(() => {
if (!provider) {
return;
}
const populate = async () => {
const _addresses = addrCollector();
const _names = await batchPopulate(provider, _addresses);
setNames(_names);
};
populate();
}, [provider, addrCollector]);
return names;
};

View File

@ -1,48 +0,0 @@
import { useState, useEffect } from "react";
import { JsonRpcProvider } from "@ethersproject/providers";
import { ENSReverseCache, ProcessedTransaction } from "./types";
export const useENSCache = (
provider?: JsonRpcProvider,
page?: ProcessedTransaction[]
) => {
const [reverseCache, setReverseCache] = useState<ENSReverseCache>();
useEffect(() => {
if (!provider || !page) {
return;
}
const addrSet = new Set<string>();
for (const tx of page) {
if (tx.from) {
addrSet.add(tx.from);
}
if (tx.to) {
addrSet.add(tx.to);
}
}
const addresses = Array.from(addrSet);
const reverseResolve = async () => {
const solvers: Promise<string | null>[] = [];
for (const a of addresses) {
solvers.push(provider.lookupAddress(a));
}
const results = await Promise.all(solvers);
const cache: ENSReverseCache = {};
for (let i = 0; i < results.length; i++) {
const r = results[i];
if (r === null) {
continue;
}
cache[addresses[i]] = r;
}
setReverseCache(cache);
};
reverseResolve();
}, [provider, page]);
return reverseCache;
};