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 ValueHighlighter from "./components/ValueHighlighter";
|
||||||
import FormattedBalance from "./components/FormattedBalance";
|
import FormattedBalance from "./components/FormattedBalance";
|
||||||
import USDAmount from "./components/USDAmount";
|
import USDAmount from "./components/USDAmount";
|
||||||
import { AddressContext, TokenMeta, TokenTransfer } from "./types";
|
|
||||||
import { RuntimeContext } from "./useRuntime";
|
import { RuntimeContext } from "./useRuntime";
|
||||||
import { useBlockNumberContext } from "./useBlockTagContext";
|
import { useBlockNumberContext } from "./useBlockTagContext";
|
||||||
|
import { useTokenMetadata } from "./useErigonHooks";
|
||||||
import { useTokenUSDOracle } from "./usePriceOracle";
|
import { useTokenUSDOracle } from "./usePriceOracle";
|
||||||
|
import { AddressContext, TokenTransfer } from "./types";
|
||||||
|
|
||||||
type TokenTransferItemProps = {
|
type TokenTransferItemProps = {
|
||||||
t: TokenTransfer;
|
t: TokenTransfer;
|
||||||
tokenMeta?: TokenMeta | null | undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: handle partial
|
const TokenTransferItem: React.FC<TokenTransferItemProps> = ({ t }) => {
|
||||||
const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
|
||||||
t,
|
|
||||||
tokenMeta,
|
|
||||||
}) => {
|
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
const blockNumber = useBlockNumberContext();
|
const blockNumber = useBlockNumberContext();
|
||||||
const [quote, decimals] = useTokenUSDOracle(provider, blockNumber, t.token);
|
const [quote, decimals] = useTokenUSDOracle(provider, blockNumber, t.token);
|
||||||
|
const tokenMeta = useTokenMetadata(provider, t.token);
|
||||||
|
|
||||||
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">
|
||||||
|
@ -285,11 +285,7 @@ const Details: React.FC<DetailsProps> = ({ txData }) => {
|
|||||||
{txData.tokenTransfers.length > 0 && (
|
{txData.tokenTransfers.length > 0 && (
|
||||||
<InfoRow title={`Tokens Transferred (${txData.tokenTransfers.length})`}>
|
<InfoRow title={`Tokens Transferred (${txData.tokenTransfers.length})`}>
|
||||||
{txData.tokenTransfers.map((t, i) => (
|
{txData.tokenTransfers.map((t, i) => (
|
||||||
<TokenTransferItem
|
<TokenTransferItem key={i} t={t} />
|
||||||
key={i}
|
|
||||||
t={t}
|
|
||||||
tokenMeta={txData.tokenMetas[t.token]}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</InfoRow>
|
</InfoRow>
|
||||||
)}
|
)}
|
||||||
|
@ -37,7 +37,6 @@ export type TransactionData = {
|
|||||||
to?: string;
|
to?: string;
|
||||||
value: BigNumber;
|
value: BigNumber;
|
||||||
tokenTransfers: TokenTransfer[];
|
tokenTransfers: TokenTransfer[];
|
||||||
tokenMetas: TokenMetas;
|
|
||||||
type: number;
|
type: number;
|
||||||
maxFeePerGas?: BigNumber | undefined;
|
maxFeePerGas?: BigNumber | undefined;
|
||||||
maxPriorityFeePerGas?: BigNumber | undefined;
|
maxPriorityFeePerGas?: BigNumber | undefined;
|
||||||
|
@ -13,13 +13,13 @@ import { arrayify, hexDataSlice, isHexString } from "@ethersproject/bytes";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import useSWRImmutable from "swr/immutable";
|
import useSWRImmutable from "swr/immutable";
|
||||||
import {
|
import {
|
||||||
TokenMetas,
|
|
||||||
TokenTransfer,
|
TokenTransfer,
|
||||||
TransactionData,
|
TransactionData,
|
||||||
InternalOperation,
|
InternalOperation,
|
||||||
ProcessedTransaction,
|
ProcessedTransaction,
|
||||||
OperationType,
|
OperationType,
|
||||||
ChecksummedAddress,
|
ChecksummedAddress,
|
||||||
|
TokenMeta,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import erc20 from "./erc20.json";
|
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({
|
setTxData({
|
||||||
transactionHash: _response.hash,
|
transactionHash: _response.hash,
|
||||||
from: _response.from,
|
from: _response.from,
|
||||||
to: _response.to,
|
to: _response.to,
|
||||||
value: _response.value,
|
value: _response.value,
|
||||||
tokenTransfers,
|
tokenTransfers,
|
||||||
tokenMetas,
|
|
||||||
type: _response.type ?? 0,
|
type: _response.type ?? 0,
|
||||||
maxFeePerGas: _response.maxFeePerGas,
|
maxFeePerGas: _response.maxFeePerGas,
|
||||||
maxPriorityFeePerGas: _response.maxPriorityFeePerGas,
|
maxPriorityFeePerGas: _response.maxPriorityFeePerGas,
|
||||||
@ -665,3 +637,55 @@ export const useHasCode = (
|
|||||||
}
|
}
|
||||||
return data as boolean | undefined;
|
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