Merge branch 'feature/refactor-rpc' into develop
This commit is contained in:
commit
a2d9fcb035
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"erigonURL": "ws://localhost:8545",
|
||||
"erigonURL": "http://localhost:8545",
|
||||
"assetsURLPrefix": "http://localhost:3001"
|
||||
}
|
|
@ -83,7 +83,7 @@ const Block: React.FC = () => {
|
|||
className="bg-link-blue bg-opacity-10 text-link-blue hover:bg-opacity-100 hover:text-white rounded-lg px-2 py-1 text-xs"
|
||||
to={blockTxsURL(block.number)}
|
||||
>
|
||||
{block.transactions.length} transactions
|
||||
{block.transactionCount} transactions
|
||||
</NavLink>{" "}
|
||||
in this block
|
||||
</InfoRow>
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import React, { useEffect, useState, useMemo, useContext } from "react";
|
||||
import React, { useMemo, useContext } from "react";
|
||||
import { useParams, useLocation } from "react-router";
|
||||
import { ethers } from "ethers";
|
||||
import queryString from "query-string";
|
||||
import StandardFrame from "./StandardFrame";
|
||||
import BlockTransactionHeader from "./block/BlockTransactionHeader";
|
||||
import BlockTransactionResults from "./block/BlockTransactionResults";
|
||||
import {
|
||||
InternalOperation,
|
||||
OperationType,
|
||||
ProcessedTransaction,
|
||||
} from "./types";
|
||||
import { PAGE_SIZE } from "./params";
|
||||
import { RuntimeContext } from "./useRuntime";
|
||||
import { useBlockTransactions } from "./useErigonHooks";
|
||||
|
||||
type BlockParams = {
|
||||
blockNumber: string;
|
||||
|
@ -38,87 +34,12 @@ const BlockTransactions: React.FC = () => {
|
|||
[params.blockNumber]
|
||||
);
|
||||
|
||||
const [txs, setTxs] = useState<ProcessedTransaction[]>();
|
||||
useEffect(() => {
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
const readBlock = async () => {
|
||||
const [_block, _receipts] = await Promise.all([
|
||||
provider.getBlockWithTransactions(blockNumber.toNumber()),
|
||||
provider.send("eth_getBlockReceipts", [blockNumber.toNumber()]),
|
||||
]);
|
||||
document.title = `Block #${_block.number} Transactions | Otterscan`;
|
||||
|
||||
const responses = _block.transactions
|
||||
.map((t, i): ProcessedTransaction => {
|
||||
return {
|
||||
blockNumber: blockNumber.toNumber(),
|
||||
timestamp: _block.timestamp,
|
||||
miner: _block.miner,
|
||||
idx: i,
|
||||
hash: t.hash,
|
||||
from: t.from,
|
||||
to: t.to,
|
||||
createdContractAddress: _receipts[i].contractAddress,
|
||||
value: t.value,
|
||||
fee:
|
||||
t.type !== 2
|
||||
? provider.formatter
|
||||
.bigNumber(_receipts[i].gasUsed)
|
||||
.mul(t.gasPrice!)
|
||||
: provider.formatter
|
||||
.bigNumber(_receipts[i].gasUsed)
|
||||
.mul(t.maxPriorityFeePerGas!.add(_block.baseFeePerGas!)),
|
||||
gasPrice:
|
||||
t.type !== 2
|
||||
? t.gasPrice!
|
||||
: t.maxPriorityFeePerGas!.add(_block.baseFeePerGas!),
|
||||
data: t.data,
|
||||
status: provider.formatter.number(_receipts[i].status),
|
||||
};
|
||||
})
|
||||
.reverse();
|
||||
setTxs(responses);
|
||||
|
||||
const internalChecks = await Promise.all(
|
||||
responses.map(async (res) => {
|
||||
const r: InternalOperation[] = await provider.send(
|
||||
"ots_getInternalOperations",
|
||||
[res.hash]
|
||||
);
|
||||
for (const op of r) {
|
||||
if (op.type !== OperationType.TRANSFER) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
res.miner &&
|
||||
(res.miner === ethers.utils.getAddress(op.from) ||
|
||||
res.miner === ethers.utils.getAddress(op.to))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
);
|
||||
const processedResponses = responses.map((r, i): ProcessedTransaction => {
|
||||
return { ...r, internalMinerInteraction: internalChecks[i] };
|
||||
});
|
||||
setTxs(processedResponses);
|
||||
};
|
||||
readBlock();
|
||||
}, [provider, blockNumber]);
|
||||
|
||||
const page = useMemo(() => {
|
||||
if (!txs) {
|
||||
return undefined;
|
||||
}
|
||||
const pageStart = (pageNumber - 1) * PAGE_SIZE;
|
||||
return txs.slice(pageStart, pageStart + PAGE_SIZE);
|
||||
}, [txs, pageNumber]);
|
||||
const total = useMemo(() => txs?.length ?? 0, [txs]);
|
||||
const [totalTxs, txs] = useBlockTransactions(
|
||||
provider,
|
||||
blockNumber.toNumber(),
|
||||
pageNumber - 1,
|
||||
PAGE_SIZE
|
||||
);
|
||||
|
||||
document.title = `Block #${blockNumber} Txns | Otterscan`;
|
||||
|
||||
|
@ -126,8 +47,8 @@ const BlockTransactions: React.FC = () => {
|
|||
<StandardFrame>
|
||||
<BlockTransactionHeader blockTag={blockNumber.toNumber()} />
|
||||
<BlockTransactionResults
|
||||
page={page}
|
||||
total={total}
|
||||
page={txs}
|
||||
total={totalTxs ?? 0}
|
||||
pageNumber={pageNumber}
|
||||
/>
|
||||
</StandardFrame>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { ethers } from "ethers";
|
||||
import { TransactionData, InternalOperation } from "./types";
|
||||
import { InternalOperation } from "./types";
|
||||
|
||||
export const getInternalOperations = async (
|
||||
provider: ethers.providers.JsonRpcProvider,
|
||||
txData: TransactionData
|
||||
txHash: string
|
||||
) => {
|
||||
const rawTransfers = await provider.send("ots_getInternalOperations", [
|
||||
txData.transactionHash,
|
||||
txHash,
|
||||
]);
|
||||
|
||||
const _transfers: InternalOperation[] = [];
|
||||
|
|
|
@ -61,7 +61,7 @@ type BlocksProps = {
|
|||
|
||||
const Blocks: React.FC<BlocksProps> = ({ latestBlock, targetBlockNumber }) => {
|
||||
const { provider } = useContext(RuntimeContext);
|
||||
const [blocks, setBlock] = useState<ExtendedBlock[]>([]);
|
||||
const [blocks, setBlocks] = useState<ExtendedBlock[]>([]);
|
||||
const [now, setNow] = useState<number>(Date.now());
|
||||
|
||||
const addBlock = useCallback(
|
||||
|
@ -77,7 +77,7 @@ const Blocks: React.FC<BlocksProps> = ({ latestBlock, targetBlockNumber }) => {
|
|||
|
||||
const extBlock = await readBlock(provider, blockNumber.toString());
|
||||
setNow(Date.now());
|
||||
setBlock((_blocks) => {
|
||||
setBlocks((_blocks) => {
|
||||
if (_blocks.length > 0 && blockNumber === _blocks[0].number) {
|
||||
return _blocks;
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ const Details: React.FC<DetailsProps> = ({
|
|||
<Copy value={txData.createdContractAddress!} />
|
||||
</div>
|
||||
)}
|
||||
{internalOps && (
|
||||
{internalOps && internalOps.length > 0 && (
|
||||
<div className="mt-2 space-y-1">
|
||||
{internalOps.map((op, i) => (
|
||||
<InternalTransactionOperation
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { ethers, BigNumber } from "ethers";
|
||||
import { BlockWithTransactions } from "@ethersproject/abstract-provider";
|
||||
import { getInternalOperations } from "./nodeFunctions";
|
||||
import {
|
||||
TokenMetas,
|
||||
TokenTransfer,
|
||||
TransactionData,
|
||||
InternalOperation,
|
||||
ProcessedTransaction,
|
||||
OperationType,
|
||||
} from "./types";
|
||||
import erc20 from "./erc20.json";
|
||||
|
||||
|
@ -20,6 +23,7 @@ export interface ExtendedBlock extends ethers.providers.Block {
|
|||
sha3Uncles: string;
|
||||
stateRoot: string;
|
||||
totalDifficulty: BigNumber;
|
||||
transactionCount: number;
|
||||
}
|
||||
|
||||
export const readBlock = async (
|
||||
|
@ -28,43 +32,122 @@ export const readBlock = async (
|
|||
) => {
|
||||
let blockPromise: Promise<any>;
|
||||
if (ethers.utils.isHexString(blockNumberOrHash, 32)) {
|
||||
// TODO: fix
|
||||
blockPromise = provider.send("eth_getBlockByHash", [
|
||||
blockNumberOrHash,
|
||||
false,
|
||||
]);
|
||||
} else {
|
||||
blockPromise = provider.send("eth_getBlockByNumber", [
|
||||
blockNumberOrHash,
|
||||
false,
|
||||
]);
|
||||
blockPromise = provider.send("ots_getBlockDetails", [blockNumberOrHash]);
|
||||
}
|
||||
const [_rawBlock, _rawIssuance, _rawReceipts] = await Promise.all([
|
||||
blockPromise,
|
||||
provider.send("erigon_issuance", [blockNumberOrHash]),
|
||||
provider.send("eth_getBlockReceipts", [blockNumberOrHash]),
|
||||
]);
|
||||
const receipts = (_rawReceipts as any[]).map((r) =>
|
||||
provider.formatter.receipt(r)
|
||||
);
|
||||
const fees = receipts.reduce(
|
||||
(acc, r) => acc.add(r.effectiveGasPrice.mul(r.gasUsed)),
|
||||
BigNumber.from(0)
|
||||
);
|
||||
|
||||
const _block = provider.formatter.block(_rawBlock);
|
||||
const _rawBlock = await blockPromise;
|
||||
const _block = provider.formatter.block(_rawBlock.block);
|
||||
const _rawIssuance = _rawBlock.issuance;
|
||||
const fees = provider.formatter.bigNumber(_rawBlock.totalFees);
|
||||
|
||||
const extBlock: ExtendedBlock = {
|
||||
blockReward: provider.formatter.bigNumber(_rawIssuance.blockReward ?? 0),
|
||||
unclesReward: provider.formatter.bigNumber(_rawIssuance.uncleReward ?? 0),
|
||||
feeReward: fees,
|
||||
size: provider.formatter.number(_rawBlock.size),
|
||||
sha3Uncles: _rawBlock.sha3Uncles,
|
||||
stateRoot: _rawBlock.stateRoot,
|
||||
totalDifficulty: provider.formatter.bigNumber(_rawBlock.totalDifficulty),
|
||||
size: provider.formatter.number(_rawBlock.block.size),
|
||||
sha3Uncles: _rawBlock.block.sha3Uncles,
|
||||
stateRoot: _rawBlock.block.stateRoot,
|
||||
totalDifficulty: provider.formatter.bigNumber(
|
||||
_rawBlock.block.totalDifficulty
|
||||
),
|
||||
transactionCount: provider.formatter.number(
|
||||
_rawBlock.block.transactionCount
|
||||
),
|
||||
..._block,
|
||||
};
|
||||
return extBlock;
|
||||
};
|
||||
|
||||
export const useBlockTransactions = (
|
||||
provider: ethers.providers.JsonRpcProvider | undefined,
|
||||
blockNumber: number,
|
||||
pageNumber: number,
|
||||
pageSize: number
|
||||
): [number | undefined, ProcessedTransaction[] | undefined] => {
|
||||
const [totalTxs, setTotalTxs] = useState<number>();
|
||||
const [txs, setTxs] = useState<ProcessedTransaction[]>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
const readBlock = async () => {
|
||||
const result = await provider.send("ots_getBlockTransactions", [
|
||||
blockNumber,
|
||||
pageNumber,
|
||||
pageSize,
|
||||
]);
|
||||
const _block = provider.formatter.blockWithTransactions(
|
||||
result.fullblock
|
||||
) as unknown as BlockWithTransactions;
|
||||
const _receipts = result.receipts;
|
||||
|
||||
const rawTxs = _block.transactions
|
||||
.map(
|
||||
(t, i): ProcessedTransaction => ({
|
||||
blockNumber: blockNumber,
|
||||
timestamp: _block.timestamp,
|
||||
miner: _block.miner,
|
||||
idx: i,
|
||||
hash: t.hash,
|
||||
from: t.from,
|
||||
to: t.to,
|
||||
createdContractAddress: _receipts[i].contractAddress,
|
||||
value: t.value,
|
||||
fee:
|
||||
t.type !== 2
|
||||
? provider.formatter
|
||||
.bigNumber(_receipts[i].gasUsed)
|
||||
.mul(t.gasPrice!)
|
||||
: provider.formatter
|
||||
.bigNumber(_receipts[i].gasUsed)
|
||||
.mul(t.maxPriorityFeePerGas!.add(_block.baseFeePerGas!)),
|
||||
gasPrice:
|
||||
t.type !== 2
|
||||
? t.gasPrice!
|
||||
: t.maxPriorityFeePerGas!.add(_block.baseFeePerGas!),
|
||||
data: t.data,
|
||||
status: provider.formatter.number(_receipts[i].status),
|
||||
})
|
||||
)
|
||||
.reverse();
|
||||
setTxs(rawTxs);
|
||||
setTotalTxs(result.fullblock.transactionCount);
|
||||
|
||||
const checkTouchMinerAddr = await Promise.all(
|
||||
rawTxs.map(async (res) => {
|
||||
const ops = await getInternalOperations(provider, res.hash);
|
||||
return (
|
||||
ops.findIndex(
|
||||
(op) =>
|
||||
op.type === OperationType.TRANSFER &&
|
||||
res.miner !== undefined &&
|
||||
res.miner === ethers.utils.getAddress(op.to)
|
||||
) !== -1
|
||||
);
|
||||
})
|
||||
);
|
||||
const processedTxs = rawTxs.map(
|
||||
(r, i): ProcessedTransaction => ({
|
||||
...r,
|
||||
internalMinerInteraction: checkTouchMinerAddr[i],
|
||||
})
|
||||
);
|
||||
setTxs(processedTxs);
|
||||
};
|
||||
readBlock();
|
||||
}, [provider, blockNumber, pageNumber, pageSize]);
|
||||
|
||||
return [totalTxs, txs];
|
||||
};
|
||||
|
||||
export const useBlockData = (
|
||||
provider: ethers.providers.JsonRpcProvider | undefined,
|
||||
blockNumberOrHash: string
|
||||
|
@ -96,12 +179,12 @@ export const useTxData = (
|
|||
return;
|
||||
}
|
||||
|
||||
const readBlock = async () => {
|
||||
const readTxData = async () => {
|
||||
const [_response, _receipt] = await Promise.all([
|
||||
provider.getTransaction(txhash),
|
||||
provider.getTransactionReceipt(txhash),
|
||||
]);
|
||||
const _block = await provider.getBlock(_receipt.blockNumber);
|
||||
const _block = await readBlock(provider, _receipt.blockNumber.toString());
|
||||
document.title = `Transaction ${_response.hash} | Otterscan`;
|
||||
|
||||
// Extract token transfers
|
||||
|
@ -149,7 +232,7 @@ export const useTxData = (
|
|||
status: _receipt.status === 1,
|
||||
blockNumber: _receipt.blockNumber,
|
||||
transactionIndex: _receipt.transactionIndex,
|
||||
blockTransactionCount: _block.transactions.length,
|
||||
blockTransactionCount: _block.transactionCount,
|
||||
confirmations: _receipt.confirmations,
|
||||
timestamp: _block.timestamp,
|
||||
miner: _block.miner,
|
||||
|
@ -172,7 +255,7 @@ export const useTxData = (
|
|||
logs: _receipt.logs,
|
||||
});
|
||||
};
|
||||
readBlock();
|
||||
readTxData();
|
||||
}, [provider, txhash]);
|
||||
|
||||
return txData;
|
||||
|
@ -190,7 +273,10 @@ export const useInternalOperations = (
|
|||
return;
|
||||
}
|
||||
|
||||
const _transfers = await getInternalOperations(provider, txData);
|
||||
const _transfers = await getInternalOperations(
|
||||
provider,
|
||||
txData.transactionHash
|
||||
);
|
||||
for (const t of _transfers) {
|
||||
t.from = provider.formatter.address(t.from);
|
||||
t.to = provider.formatter.address(t.to);
|
||||
|
|
Loading…
Reference in New Issue