Convert token metadata logic into SWR fetcher
This commit is contained in:
parent
21a549b3e1
commit
9683edf050
@ -6,24 +6,21 @@ import TransactionAddress from "./components/TransactionAddress";
|
||||
import ValueHighlighter from "./components/ValueHighlighter";
|
||||
import FormattedBalance from "./components/FormattedBalance";
|
||||
import USDAmount from "./components/USDAmount";
|
||||
import { AddressContext, TokenMeta, TokenTransfer } from "./types";
|
||||
import { RuntimeContext } from "./useRuntime";
|
||||
import { useBlockNumberContext } from "./useBlockTagContext";
|
||||
import { useTokenMetadata } from "./useErigonHooks";
|
||||
import { useTokenUSDOracle } from "./usePriceOracle";
|
||||
import { AddressContext, TokenTransfer } from "./types";
|
||||
|
||||
type TokenTransferItemProps = {
|
||||
t: TokenTransfer;
|
||||
tokenMeta?: TokenMeta | null | undefined;
|
||||
};
|
||||
|
||||
// TODO: handle partial
|
||||
const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
||||
t,
|
||||
tokenMeta,
|
||||
}) => {
|
||||
const TokenTransferItem: React.FC<TokenTransferItemProps> = ({ t }) => {
|
||||
const { provider } = useContext(RuntimeContext);
|
||||
const blockNumber = useBlockNumberContext();
|
||||
const [quote, decimals] = useTokenUSDOracle(provider, blockNumber, t.token);
|
||||
const tokenMeta = useTokenMetadata(provider, t.token);
|
||||
|
||||
return (
|
||||
<div className="flex items-baseline space-x-2 px-2 py-1 truncate hover:bg-gray-100">
|
||||
|
@ -285,11 +285,7 @@ const Details: React.FC<DetailsProps> = ({ txData }) => {
|
||||
{txData.tokenTransfers.length > 0 && (
|
||||
<InfoRow title={`Tokens Transferred (${txData.tokenTransfers.length})`}>
|
||||
{txData.tokenTransfers.map((t, i) => (
|
||||
<TokenTransferItem
|
||||
key={i}
|
||||
t={t}
|
||||
tokenMeta={txData.tokenMetas[t.token]}
|
||||
/>
|
||||
<TokenTransferItem key={i} t={t} />
|
||||
))}
|
||||
</InfoRow>
|
||||
)}
|
||||
|
@ -37,7 +37,6 @@ export type TransactionData = {
|
||||
to?: string;
|
||||
value: BigNumber;
|
||||
tokenTransfers: TokenTransfer[];
|
||||
tokenMetas: TokenMetas;
|
||||
type: number;
|
||||
maxFeePerGas?: BigNumber | undefined;
|
||||
maxPriorityFeePerGas?: BigNumber | undefined;
|
||||
|
@ -13,13 +13,13 @@ import { arrayify, hexDataSlice, isHexString } from "@ethersproject/bytes";
|
||||
import useSWR from "swr";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
import {
|
||||
TokenMetas,
|
||||
TokenTransfer,
|
||||
TransactionData,
|
||||
InternalOperation,
|
||||
ProcessedTransaction,
|
||||
OperationType,
|
||||
ChecksummedAddress,
|
||||
TokenMeta,
|
||||
} from "./types";
|
||||
import erc20 from "./erc20.json";
|
||||
|
||||
@ -216,40 +216,12 @@ export const useTxData = (
|
||||
}
|
||||
}
|
||||
|
||||
// Extract token meta
|
||||
const tokenMetas: TokenMetas = {};
|
||||
for (const t of tokenTransfers) {
|
||||
if (tokenMetas[t.token] !== undefined) {
|
||||
continue;
|
||||
}
|
||||
const erc20Contract = new Contract(t.token, erc20, provider);
|
||||
try {
|
||||
const [name, symbol, decimals] = await Promise.all([
|
||||
erc20Contract.name(),
|
||||
erc20Contract.symbol(),
|
||||
erc20Contract.decimals(),
|
||||
]);
|
||||
tokenMetas[t.token] = {
|
||||
name,
|
||||
symbol,
|
||||
decimals,
|
||||
};
|
||||
} catch (err) {
|
||||
tokenMetas[t.token] = null;
|
||||
console.warn(
|
||||
`Couldn't get token ${t.token} metadata; ignoring`,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setTxData({
|
||||
transactionHash: _response.hash,
|
||||
from: _response.from,
|
||||
to: _response.to,
|
||||
value: _response.value,
|
||||
tokenTransfers,
|
||||
tokenMetas,
|
||||
type: _response.type ?? 0,
|
||||
maxFeePerGas: _response.maxFeePerGas,
|
||||
maxPriorityFeePerGas: _response.maxPriorityFeePerGas,
|
||||
@ -665,3 +637,55 @@ export const useHasCode = (
|
||||
}
|
||||
return data as boolean | undefined;
|
||||
};
|
||||
|
||||
const tokenMetadataFetcher =
|
||||
(provider: JsonRpcProvider | undefined) =>
|
||||
async (
|
||||
_: "tokenmeta",
|
||||
address: ChecksummedAddress
|
||||
): Promise<TokenMeta | null> => {
|
||||
const erc20Contract = new Contract(address, erc20, provider);
|
||||
try {
|
||||
const name = (await erc20Contract.name()) as string;
|
||||
if (!name.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [symbol, decimals] = (await Promise.all([
|
||||
erc20Contract.symbol(),
|
||||
erc20Contract.decimals(),
|
||||
])) as [string, number];
|
||||
|
||||
// Prevent faulty tokens with empty name/symbol
|
||||
if (!symbol.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
symbol,
|
||||
decimals,
|
||||
};
|
||||
} catch (err) {
|
||||
// Ignore on purpose; this indicates the probe failed and the address
|
||||
// is not a token
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const useTokenMetadata = (
|
||||
provider: JsonRpcProvider | undefined,
|
||||
address: ChecksummedAddress | undefined
|
||||
): TokenMeta | null | undefined => {
|
||||
const fetcher = tokenMetadataFetcher(provider);
|
||||
const { data, error } = useSWRImmutable(
|
||||
provider !== undefined && address !== undefined
|
||||
? ["tokenmeta", address]
|
||||
: null,
|
||||
fetcher
|
||||
);
|
||||
if (error) {
|
||||
return undefined;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user