2021-11-25 19:20:44 +00:00
|
|
|
import { useState, useEffect, useRef, useContext } from "react";
|
2021-12-17 02:17:13 +00:00
|
|
|
import { BaseProvider, JsonRpcProvider } from "@ethersproject/providers";
|
2021-11-25 19:20:44 +00:00
|
|
|
import { getAddress, isAddress } from "@ethersproject/address";
|
2021-12-17 02:17:13 +00:00
|
|
|
import useSWR from "swr";
|
|
|
|
import {
|
|
|
|
batchPopulate,
|
|
|
|
mainResolver,
|
|
|
|
ResolvedAddresses,
|
|
|
|
} from "./api/address-resolver";
|
|
|
|
import { SelectedResolvedName } from "./api/address-resolver/CompositeAddressResolver";
|
2021-11-01 20:09:47 +00:00
|
|
|
import { TraceGroup } from "./useErigonHooks";
|
2021-11-25 19:20:44 +00:00
|
|
|
import { RuntimeContext } from "./useRuntime";
|
|
|
|
import {
|
|
|
|
ChecksummedAddress,
|
|
|
|
ProcessedTransaction,
|
|
|
|
TransactionData,
|
|
|
|
} from "./types";
|
|
|
|
|
|
|
|
export const useAddressOrENSFromURL = (
|
|
|
|
addressOrName: string,
|
|
|
|
urlFixer: (address: ChecksummedAddress) => void
|
|
|
|
): [
|
|
|
|
ChecksummedAddress | undefined,
|
|
|
|
boolean | undefined,
|
|
|
|
boolean | undefined
|
|
|
|
] => {
|
|
|
|
const { provider } = useContext(RuntimeContext);
|
|
|
|
const [checksummedAddress, setChecksummedAddress] = useState<
|
|
|
|
ChecksummedAddress | undefined
|
|
|
|
>();
|
|
|
|
const [isENS, setENS] = useState<boolean>();
|
|
|
|
const [error, setError] = useState<boolean>();
|
|
|
|
|
|
|
|
// If it looks like it is an ENS name, try to resolve it
|
|
|
|
useEffect(() => {
|
2021-12-04 19:54:27 +00:00
|
|
|
// Reset
|
|
|
|
setENS(false);
|
|
|
|
setError(false);
|
|
|
|
setChecksummedAddress(undefined);
|
|
|
|
|
2021-11-25 19:20:44 +00:00
|
|
|
// TODO: handle and offer fallback to bad checksummed addresses
|
|
|
|
if (isAddress(addressOrName)) {
|
|
|
|
// Normalize to checksummed address
|
|
|
|
const _checksummedAddress = getAddress(addressOrName);
|
|
|
|
if (_checksummedAddress !== addressOrName) {
|
|
|
|
// Request came with a non-checksummed address; fix the URL
|
|
|
|
urlFixer(_checksummedAddress);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
setENS(false);
|
|
|
|
setError(false);
|
|
|
|
setChecksummedAddress(_checksummedAddress);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!provider) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const resolveName = async () => {
|
|
|
|
const resolvedAddress = await provider.resolveName(addressOrName);
|
|
|
|
if (resolvedAddress !== null) {
|
|
|
|
setENS(true);
|
|
|
|
setError(false);
|
|
|
|
setChecksummedAddress(resolvedAddress);
|
|
|
|
} else {
|
|
|
|
setENS(false);
|
|
|
|
setError(true);
|
|
|
|
setChecksummedAddress(undefined);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
resolveName();
|
|
|
|
}, [provider, addressOrName, urlFixer]);
|
|
|
|
|
|
|
|
return [checksummedAddress, isENS, error];
|
|
|
|
};
|
2021-10-31 00:49:23 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2021-12-04 19:59:29 +00:00
|
|
|
if (tx.createdContractAddress) {
|
|
|
|
uniqueAddresses.add(tx.createdContractAddress);
|
|
|
|
}
|
2021-10-31 00:49:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Array.from(uniqueAddresses);
|
|
|
|
};
|
|
|
|
|
2021-10-31 01:44:33 +00:00
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2021-11-01 20:09:47 +00:00
|
|
|
export const tracesCollector =
|
|
|
|
(traces: TraceGroup[] | undefined): AddressCollector =>
|
|
|
|
() => {
|
|
|
|
if (traces === undefined) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
const uniqueAddresses = new Set<string>();
|
|
|
|
let searchTraces = [...traces];
|
|
|
|
while (searchTraces.length > 0) {
|
|
|
|
const nextSearch: TraceGroup[] = [];
|
|
|
|
|
|
|
|
for (const g of searchTraces) {
|
|
|
|
uniqueAddresses.add(g.from);
|
|
|
|
uniqueAddresses.add(g.to);
|
|
|
|
if (g.children) {
|
|
|
|
nextSearch.push(...g.children);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
searchTraces = nextSearch;
|
|
|
|
}
|
|
|
|
return Array.from(uniqueAddresses);
|
|
|
|
};
|
|
|
|
|
2021-10-31 00:49:23 +00:00
|
|
|
export const useResolvedAddresses = (
|
|
|
|
provider: JsonRpcProvider | undefined,
|
|
|
|
addrCollector: AddressCollector
|
|
|
|
) => {
|
|
|
|
const [names, setNames] = useState<ResolvedAddresses>();
|
2021-11-01 07:15:12 +00:00
|
|
|
const ref = useRef<ResolvedAddresses | undefined>();
|
2021-10-31 00:49:23 +00:00
|
|
|
useEffect(() => {
|
2021-11-01 07:15:12 +00:00
|
|
|
ref.current = names;
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(
|
|
|
|
() => {
|
|
|
|
if (!provider) {
|
|
|
|
return;
|
|
|
|
}
|
2021-10-31 00:49:23 +00:00
|
|
|
|
2021-11-01 07:15:12 +00:00
|
|
|
const populate = async () => {
|
|
|
|
const _addresses = addrCollector();
|
|
|
|
const _names = await batchPopulate(provider, _addresses, ref.current);
|
|
|
|
setNames(_names);
|
|
|
|
};
|
|
|
|
populate();
|
|
|
|
},
|
|
|
|
// DON'T put names variables in dependency array; this is intentional; useRef
|
|
|
|
[provider, addrCollector]
|
|
|
|
);
|
2021-10-31 00:49:23 +00:00
|
|
|
|
|
|
|
return names;
|
|
|
|
};
|
2021-12-17 02:17:13 +00:00
|
|
|
|
|
|
|
export const useResolvedAddress = (
|
|
|
|
provider: BaseProvider | undefined,
|
|
|
|
address: ChecksummedAddress
|
|
|
|
): SelectedResolvedName<any> | undefined => {
|
|
|
|
const fetcher = async (
|
|
|
|
key: string
|
|
|
|
): Promise<SelectedResolvedName<any> | undefined> => {
|
|
|
|
if (!provider) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
return mainResolver.resolveAddress(provider, address);
|
|
|
|
};
|
|
|
|
|
|
|
|
const { data, error } = useSWR(address, fetcher);
|
|
|
|
if (error) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
};
|