import React, { useState, useEffect, useCallback, useMemo } from "react"; import { Route, Switch, useParams } from "react-router-dom"; import { BigNumber, ethers } from "ethers"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheckCircle, faTimesCircle, faCaretRight, } from "@fortawesome/free-solid-svg-icons"; import { provider } from "./ethersconfig"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import Tab from "./components/Tab"; import ContentFrame from "./ContentFrame"; import BlockLink from "./components/BlockLink"; import AddressLink from "./components/AddressLink"; import Copy from "./components/Copy"; import Timestamp from "./components/Timestamp"; import InternalTransfer from "./components/InternalTransfer"; import TokenLogo from "./components/TokenLogo"; import GasValue from "./components/GasValue"; import FormattedBalance from "./components/FormattedBalance"; import erc20 from "./erc20.json"; import { TokenMetas, TokenTransfer, TransactionData, Transfer } from "./types"; const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; type TransactionParams = { txhash: string; }; const Transaction: React.FC = () => { const params = useParams(); const { txhash } = params; const [txData, setTxData] = useState(); useEffect(() => { const readBlock = async () => { const [_response, _receipt] = await Promise.all([ provider.getTransaction(txhash), provider.getTransactionReceipt(txhash), ]); const _block = await provider.getBlock(_receipt.blockNumber); document.title = `Transaction ${_response.hash} | Otterscan`; // Extract token transfers const tokenTransfers: TokenTransfer[] = []; for (const l of _receipt.logs) { if (l.topics.length !== 3) { continue; } if (l.topics[0] !== TRANSFER_TOPIC) { continue; } tokenTransfers.push({ token: l.address, from: ethers.utils.getAddress( ethers.utils.hexDataSlice(ethers.utils.arrayify(l.topics[1]), 12) ), to: ethers.utils.getAddress( ethers.utils.hexDataSlice(ethers.utils.arrayify(l.topics[2]), 12) ), value: BigNumber.from(l.data), }); } // Extract token meta const tokenMetas: TokenMetas = {}; for (const t of tokenTransfers) { if (tokenMetas[t.token]) { continue; } const erc20Contract = new ethers.Contract(t.token, erc20, provider); const [name, symbol, decimals] = await Promise.all([ erc20Contract.name(), erc20Contract.symbol(), erc20Contract.decimals(), ]); tokenMetas[t.token] = { name, symbol, decimals, }; } setTxData({ transactionHash: _receipt.transactionHash, status: _receipt.status === 1, blockNumber: _receipt.blockNumber, transactionIndex: _receipt.transactionIndex, confirmations: _receipt.confirmations, timestamp: _block.timestamp, miner: _block.miner, from: _receipt.from, to: _receipt.to, value: _response.value, tokenTransfers, tokenMetas, fee: _response.gasPrice!.mul(_receipt.gasUsed), gasPrice: _response.gasPrice!, gasLimit: _response.gasLimit, gasUsed: _receipt.gasUsed, gasUsedPerc: _receipt.gasUsed.toNumber() / _response.gasLimit.toNumber(), nonce: _response.nonce, data: _response.data, logs: _receipt.logs, }); }; readBlock(); }, [txhash]); const [transfers, setTransfers] = useState(); const sendsEthToMiner = useMemo(() => { if (!txData || !transfers) { return false; } for (const t of transfers) { if (t.to === txData.miner) { return true; } } return false; }, [txData, transfers]); const traceTransfersUsingOtsTrace = useCallback(async () => { if (!txData) { return; } const r = await provider.send("ots_getTransactionTransfers", [ txData.transactionHash, ]); const _transfers: Transfer[] = []; for (const t of r) { _transfers.push({ from: ethers.utils.getAddress(t.from), to: ethers.utils.getAddress(t.to), value: t.value, }); } setTransfers(_transfers); }, [txData]); useEffect(() => { traceTransfersUsingOtsTrace(); }, [traceTransfersUsingOtsTrace]); return ( Transaction Details {txData && ( <>
Overview Logs{txData && ` (${txData.logs.length})`}
{txData.transactionHash}
{txData.status ? ( Success ) : ( Fail )}
{txData.confirmations} Block Confirmations
{transfers && (
{transfers.map((t, i) => ( ))}
)}
{txData.tokenTransfers.length > 0 && (
{txData.tokenTransfers && txData.tokenTransfers.map((t, i) => (
From To For {txData.tokenMetas[t.token] ? ( <>
) : ( )}
))}
)} {ethers.utils.formatEther(txData.value)} Ether Ether
Ether ( {" "} Gwei) {sendsEthToMiner && Flashbots}
N/A ( {(txData.gasUsedPerc * 100).toFixed(2)}%) {txData.nonce} {txData.transactionIndex}