Merge branch 'feature/refactor-rpc' into develop

This commit is contained in:
Willian Mitsuda 2021-08-03 19:07:22 -03:00
commit a2d9fcb035
7 changed files with 130 additions and 123 deletions

View File

@ -1,4 +1,4 @@
{ {
"erigonURL": "ws://localhost:8545", "erigonURL": "http://localhost:8545",
"assetsURLPrefix": "http://localhost:3001" "assetsURLPrefix": "http://localhost:3001"
} }

View File

@ -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" 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)} to={blockTxsURL(block.number)}
> >
{block.transactions.length} transactions {block.transactionCount} transactions
</NavLink>{" "} </NavLink>{" "}
in this block in this block
</InfoRow> </InfoRow>

View File

@ -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 { useParams, useLocation } from "react-router";
import { ethers } from "ethers"; import { ethers } from "ethers";
import queryString from "query-string"; import queryString from "query-string";
import StandardFrame from "./StandardFrame"; import StandardFrame from "./StandardFrame";
import BlockTransactionHeader from "./block/BlockTransactionHeader"; import BlockTransactionHeader from "./block/BlockTransactionHeader";
import BlockTransactionResults from "./block/BlockTransactionResults"; import BlockTransactionResults from "./block/BlockTransactionResults";
import {
InternalOperation,
OperationType,
ProcessedTransaction,
} from "./types";
import { PAGE_SIZE } from "./params"; import { PAGE_SIZE } from "./params";
import { RuntimeContext } from "./useRuntime"; import { RuntimeContext } from "./useRuntime";
import { useBlockTransactions } from "./useErigonHooks";
type BlockParams = { type BlockParams = {
blockNumber: string; blockNumber: string;
@ -38,87 +34,12 @@ const BlockTransactions: React.FC = () => {
[params.blockNumber] [params.blockNumber]
); );
const [txs, setTxs] = useState<ProcessedTransaction[]>(); const [totalTxs, txs] = useBlockTransactions(
useEffect(() => { provider,
if (!provider) { blockNumber.toNumber(),
return; pageNumber - 1,
} PAGE_SIZE
);
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]);
document.title = `Block #${blockNumber} Txns | Otterscan`; document.title = `Block #${blockNumber} Txns | Otterscan`;
@ -126,8 +47,8 @@ const BlockTransactions: React.FC = () => {
<StandardFrame> <StandardFrame>
<BlockTransactionHeader blockTag={blockNumber.toNumber()} /> <BlockTransactionHeader blockTag={blockNumber.toNumber()} />
<BlockTransactionResults <BlockTransactionResults
page={page} page={txs}
total={total} total={totalTxs ?? 0}
pageNumber={pageNumber} pageNumber={pageNumber}
/> />
</StandardFrame> </StandardFrame>

View File

@ -1,12 +1,12 @@
import { ethers } from "ethers"; import { ethers } from "ethers";
import { TransactionData, InternalOperation } from "./types"; import { InternalOperation } from "./types";
export const getInternalOperations = async ( export const getInternalOperations = async (
provider: ethers.providers.JsonRpcProvider, provider: ethers.providers.JsonRpcProvider,
txData: TransactionData txHash: string
) => { ) => {
const rawTransfers = await provider.send("ots_getInternalOperations", [ const rawTransfers = await provider.send("ots_getInternalOperations", [
txData.transactionHash, txHash,
]); ]);
const _transfers: InternalOperation[] = []; const _transfers: InternalOperation[] = [];

View File

@ -61,7 +61,7 @@ type BlocksProps = {
const Blocks: React.FC<BlocksProps> = ({ latestBlock, targetBlockNumber }) => { const Blocks: React.FC<BlocksProps> = ({ latestBlock, targetBlockNumber }) => {
const { provider } = useContext(RuntimeContext); const { provider } = useContext(RuntimeContext);
const [blocks, setBlock] = useState<ExtendedBlock[]>([]); const [blocks, setBlocks] = useState<ExtendedBlock[]>([]);
const [now, setNow] = useState<number>(Date.now()); const [now, setNow] = useState<number>(Date.now());
const addBlock = useCallback( const addBlock = useCallback(
@ -77,7 +77,7 @@ const Blocks: React.FC<BlocksProps> = ({ latestBlock, targetBlockNumber }) => {
const extBlock = await readBlock(provider, blockNumber.toString()); const extBlock = await readBlock(provider, blockNumber.toString());
setNow(Date.now()); setNow(Date.now());
setBlock((_blocks) => { setBlocks((_blocks) => {
if (_blocks.length > 0 && blockNumber === _blocks[0].number) { if (_blocks.length > 0 && blockNumber === _blocks[0].number) {
return _blocks; return _blocks;
} }

View File

@ -130,7 +130,7 @@ const Details: React.FC<DetailsProps> = ({
<Copy value={txData.createdContractAddress!} /> <Copy value={txData.createdContractAddress!} />
</div> </div>
)} )}
{internalOps && ( {internalOps && internalOps.length > 0 && (
<div className="mt-2 space-y-1"> <div className="mt-2 space-y-1">
{internalOps.map((op, i) => ( {internalOps.map((op, i) => (
<InternalTransactionOperation <InternalTransactionOperation

View File

@ -1,11 +1,14 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { ethers, BigNumber } from "ethers"; import { ethers, BigNumber } from "ethers";
import { BlockWithTransactions } from "@ethersproject/abstract-provider";
import { getInternalOperations } from "./nodeFunctions"; import { getInternalOperations } from "./nodeFunctions";
import { import {
TokenMetas, TokenMetas,
TokenTransfer, TokenTransfer,
TransactionData, TransactionData,
InternalOperation, InternalOperation,
ProcessedTransaction,
OperationType,
} from "./types"; } from "./types";
import erc20 from "./erc20.json"; import erc20 from "./erc20.json";
@ -20,6 +23,7 @@ export interface ExtendedBlock extends ethers.providers.Block {
sha3Uncles: string; sha3Uncles: string;
stateRoot: string; stateRoot: string;
totalDifficulty: BigNumber; totalDifficulty: BigNumber;
transactionCount: number;
} }
export const readBlock = async ( export const readBlock = async (
@ -28,43 +32,122 @@ export const readBlock = async (
) => { ) => {
let blockPromise: Promise<any>; let blockPromise: Promise<any>;
if (ethers.utils.isHexString(blockNumberOrHash, 32)) { if (ethers.utils.isHexString(blockNumberOrHash, 32)) {
// TODO: fix
blockPromise = provider.send("eth_getBlockByHash", [ blockPromise = provider.send("eth_getBlockByHash", [
blockNumberOrHash, blockNumberOrHash,
false, false,
]); ]);
} else { } else {
blockPromise = provider.send("eth_getBlockByNumber", [ blockPromise = provider.send("ots_getBlockDetails", [blockNumberOrHash]);
blockNumberOrHash,
false,
]);
} }
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 = { const extBlock: ExtendedBlock = {
blockReward: provider.formatter.bigNumber(_rawIssuance.blockReward ?? 0), blockReward: provider.formatter.bigNumber(_rawIssuance.blockReward ?? 0),
unclesReward: provider.formatter.bigNumber(_rawIssuance.uncleReward ?? 0), unclesReward: provider.formatter.bigNumber(_rawIssuance.uncleReward ?? 0),
feeReward: fees, feeReward: fees,
size: provider.formatter.number(_rawBlock.size), size: provider.formatter.number(_rawBlock.block.size),
sha3Uncles: _rawBlock.sha3Uncles, sha3Uncles: _rawBlock.block.sha3Uncles,
stateRoot: _rawBlock.stateRoot, stateRoot: _rawBlock.block.stateRoot,
totalDifficulty: provider.formatter.bigNumber(_rawBlock.totalDifficulty), totalDifficulty: provider.formatter.bigNumber(
_rawBlock.block.totalDifficulty
),
transactionCount: provider.formatter.number(
_rawBlock.block.transactionCount
),
..._block, ..._block,
}; };
return extBlock; 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 = ( export const useBlockData = (
provider: ethers.providers.JsonRpcProvider | undefined, provider: ethers.providers.JsonRpcProvider | undefined,
blockNumberOrHash: string blockNumberOrHash: string
@ -96,12 +179,12 @@ export const useTxData = (
return; return;
} }
const readBlock = async () => { const readTxData = async () => {
const [_response, _receipt] = await Promise.all([ const [_response, _receipt] = await Promise.all([
provider.getTransaction(txhash), provider.getTransaction(txhash),
provider.getTransactionReceipt(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`; document.title = `Transaction ${_response.hash} | Otterscan`;
// Extract token transfers // Extract token transfers
@ -149,7 +232,7 @@ export const useTxData = (
status: _receipt.status === 1, status: _receipt.status === 1,
blockNumber: _receipt.blockNumber, blockNumber: _receipt.blockNumber,
transactionIndex: _receipt.transactionIndex, transactionIndex: _receipt.transactionIndex,
blockTransactionCount: _block.transactions.length, blockTransactionCount: _block.transactionCount,
confirmations: _receipt.confirmations, confirmations: _receipt.confirmations,
timestamp: _block.timestamp, timestamp: _block.timestamp,
miner: _block.miner, miner: _block.miner,
@ -172,7 +255,7 @@ export const useTxData = (
logs: _receipt.logs, logs: _receipt.logs,
}); });
}; };
readBlock(); readTxData();
}, [provider, txhash]); }, [provider, txhash]);
return txData; return txData;
@ -190,7 +273,10 @@ export const useInternalOperations = (
return; return;
} }
const _transfers = await getInternalOperations(provider, txData); const _transfers = await getInternalOperations(
provider,
txData.transactionHash
);
for (const t of _transfers) { for (const t of _transfers) {
t.from = provider.formatter.address(t.from); t.from = provider.formatter.address(t.from);
t.to = provider.formatter.address(t.to); t.to = provider.formatter.address(t.to);