From 5b5791236ff682a5ff06e266355e15e6c3daea21 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 1 Aug 2021 06:59:59 -0300 Subject: [PATCH 1/7] Extract custom hook --- src/Transaction.tsx | 94 ++----------------------------------- src/useErigonHooks.ts | 105 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 93 deletions(-) diff --git a/src/Transaction.tsx b/src/Transaction.tsx index ad940fe..cb5a4e1 100644 --- a/src/Transaction.tsx +++ b/src/Transaction.tsx @@ -1,19 +1,13 @@ -import React, { useState, useEffect, useMemo, useContext } from "react"; +import React, { useMemo, useContext } from "react"; import { Route, Switch, useParams } from "react-router-dom"; -import { BigNumber, ethers } from "ethers"; import StandardFrame from "./StandardFrame"; import StandardSubtitle from "./StandardSubtitle"; import Tab from "./components/Tab"; import Details from "./transaction/Details"; import Logs from "./transaction/Logs"; -import erc20 from "./erc20.json"; -import { TokenMetas, TokenTransfer, TransactionData } from "./types"; import { RuntimeContext } from "./useRuntime"; import { SelectionContext, useSelection } from "./useSelection"; -import { useInternalOperations } from "./useErigonHooks"; - -const TRANSFER_TOPIC = - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; +import { useInternalOperations, useTxData } from "./useErigonHooks"; type TransactionParams = { txhash: string; @@ -24,89 +18,7 @@ const Transaction: React.FC = () => { const params = useParams(); const { txhash } = params; - const [txData, setTxData] = useState(); - useEffect(() => { - if (!provider) { - return; - } - - 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, - createdContractAddress: _receipt.contractAddress, - value: _response.value, - tokenTransfers, - tokenMetas, - type: _response.type ?? 0, - fee: _response.gasPrice!.mul(_receipt.gasUsed), - blockBaseFeePerGas: _block.baseFeePerGas, - maxFeePerGas: _response.maxFeePerGas, - maxPriorityFeePerGas: _response.maxPriorityFeePerGas, - gasPrice: _response.gasPrice!, - gasUsed: _receipt.gasUsed, - gasLimit: _response.gasLimit, - nonce: _response.nonce, - data: _response.data, - logs: _receipt.logs, - }); - }; - readBlock(); - }, [provider, txhash]); + const txData = useTxData(provider, txhash); const internalOps = useInternalOperations(provider, txData); const sendsEthToMiner = useMemo(() => { diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index d11d2f5..c3c0398 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -1,7 +1,16 @@ -import { ethers, BigNumber } from "ethers"; import { useState, useEffect } from "react"; +import { ethers, BigNumber } from "ethers"; import { getInternalOperations } from "./nodeFunctions"; -import { TransactionData, InternalOperation } from "./types"; +import { + TokenMetas, + TokenTransfer, + TransactionData, + InternalOperation, +} from "./types"; +import erc20 from "./erc20.json"; + +const TRANSFER_TOPIC = + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; export interface ExtendedBlock extends ethers.providers.Block { blockReward: BigNumber; @@ -76,6 +85,98 @@ export const useBlockData = ( return block; }; +export const useTxData = ( + provider: ethers.providers.JsonRpcProvider | undefined, + txhash: string +): TransactionData | undefined => { + const [txData, setTxData] = useState(); + + useEffect(() => { + if (!provider) { + return; + } + + 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, + createdContractAddress: _receipt.contractAddress, + value: _response.value, + tokenTransfers, + tokenMetas, + type: _response.type ?? 0, + fee: _response.gasPrice!.mul(_receipt.gasUsed), + blockBaseFeePerGas: _block.baseFeePerGas, + maxFeePerGas: _response.maxFeePerGas, + maxPriorityFeePerGas: _response.maxPriorityFeePerGas, + gasPrice: _response.gasPrice!, + gasUsed: _receipt.gasUsed, + gasLimit: _response.gasLimit, + nonce: _response.nonce, + data: _response.data, + logs: _receipt.logs, + }); + }; + readBlock(); + }, [provider, txhash]); + + return txData; +}; + export const useInternalOperations = ( provider: ethers.providers.JsonRpcProvider | undefined, txData: TransactionData | undefined From 07e496f476fa46cebf3efbe6aaa6d12d7d38d647 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 1 Aug 2021 07:04:58 -0300 Subject: [PATCH 2/7] Add block total tx count info --- src/transaction/Details.tsx | 2 +- src/types.ts | 1 + src/useErigonHooks.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 7346b0c..f49b6d1 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -229,7 +229,7 @@ const Details: React.FC = ({ {txData.nonce} - {txData.transactionIndex} + {txData.transactionIndex} / {txData.blockTransactionCount - 1} diff --git a/src/types.ts b/src/types.ts index 482828e..8a2cb8a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -40,6 +40,7 @@ export type TransactionData = { status: boolean; blockNumber: number; transactionIndex: number; + blockTransactionCount: number; confirmations: number; timestamp: number; miner?: string; diff --git a/src/useErigonHooks.ts b/src/useErigonHooks.ts index c3c0398..588940e 100644 --- a/src/useErigonHooks.ts +++ b/src/useErigonHooks.ts @@ -149,6 +149,7 @@ export const useTxData = ( status: _receipt.status === 1, blockNumber: _receipt.blockNumber, transactionIndex: _receipt.transactionIndex, + blockTransactionCount: _block.transactions.length, confirmations: _receipt.confirmations, timestamp: _block.timestamp, miner: _block.miner, From e78447f4b696a9e6f57b6255eb9b1264edfc0720 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 1 Aug 2021 07:41:19 -0300 Subject: [PATCH 3/7] Extract and apply relative position component --- src/components/RelativePosition.tsx | 15 +++++++++++++++ src/transaction/Details.tsx | 16 ++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 src/components/RelativePosition.tsx diff --git a/src/components/RelativePosition.tsx b/src/components/RelativePosition.tsx new file mode 100644 index 0000000..e952463 --- /dev/null +++ b/src/components/RelativePosition.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +type RelativePositionProps = { + pos: React.ReactNode; + total: React.ReactNode; +}; + +const RelativePosition: React.FC = ({ pos, total }) => ( + + {pos} + / {total} + +); + +export default React.memo(RelativePosition); diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index f49b6d1..25dddb5 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -23,6 +23,7 @@ import TokenTransferItem from "../TokenTransferItem"; import { TransactionData, InternalOperation } from "../types"; import PercentageBar from "../components/PercentageBar"; import ExternalLink from "../components/ExternalLink"; +import RelativePosition from "../components/RelativePosition"; type DetailsProps = { txData: TransactionData; @@ -192,8 +193,10 @@ const Details: React.FC = ({
- /{" "} - + } + total={} + />
= ({ N/A {txData.nonce} - - - {txData.transactionIndex} / {txData.blockTransactionCount - 1} - + +