From 63488720fdfed293760fe8175dd815efb51d0316 Mon Sep 17 00:00:00 2001 From: artemkolodko Date: Mon, 24 Oct 2022 10:39:22 +0100 Subject: [PATCH] Add internal txs export to csv --- .../tabs/transactions/Transactions.tsx | 2 +- src/pages/ExportData/export-utils.ts | 31 +++- src/pages/ExportData/index.tsx | 155 ++++++++++++------ src/types/blockchain.ts | 1 + 4 files changed, 136 insertions(+), 53 deletions(-) diff --git a/src/pages/AddressPage/tabs/transactions/Transactions.tsx b/src/pages/AddressPage/tabs/transactions/Transactions.tsx index e17fb51..b1b69ab 100644 --- a/src/pages/AddressPage/tabs/transactions/Transactions.tsx +++ b/src/pages/AddressPage/tabs/transactions/Transactions.tsx @@ -305,7 +305,7 @@ export function Transactions(props: { rowDetails={props.rowDetails} showPages={totalElements > 0} /> - {['transaction', 'erc20', 'erc721', 'erc1155'].includes(props.type) && + {['transaction', 'erc20', 'erc721', 'erc1155', 'internal_transaction'].includes(props.type) && diff --git a/src/pages/ExportData/export-utils.ts b/src/pages/ExportData/export-utils.ts index 916e402..880cf11 100644 --- a/src/pages/ExportData/export-utils.ts +++ b/src/pages/ExportData/export-utils.ts @@ -1,4 +1,4 @@ -import { RelatedTransaction } from "../../types"; +import {InternalTransaction, RelatedTransaction} from "../../types"; import dayjs from "dayjs"; import Big from "big.js"; import { calculateFee, calculateFeePriceUSD } from "../../utils/fee"; @@ -62,6 +62,24 @@ const mapHrc20TxToExport = (ownerAddress: string, tx: any, erc20Map: Record { + const txDate = dayjs(tx.timestamp) + const isSender = ownerAddress === tx.from + return { + Txhash: tx.transactionHash, + Blockno: tx.blockNumber, + UnixTimestamp: txDate.unix(), + DateTime: txDate.format('YYYY-MM-DD HH:MM:ss'), + From: tx.from, + To: tx.to, + ['Value_IN(ONE)']: convertValue(isSender ? '0': tx.value), + ['Value_OUT(ONE)']: convertValue(isSender ? tx.value : '0'), + [`CurrentValue @ $${onePrice}/ONE`]: convertValue(onePrice * +tx.value), + Method: tx.input.slice(0, 10), + Type: tx.type + } +} + export interface IDownloadCsvParams { type: TRelatedTransaction address: string @@ -76,12 +94,15 @@ export const downloadCSV = (params: IDownloadCsvParams, filename: string) => { const { type, address, txs, onePrice, erc20Map, erc721Map, erc1155Map } = params const mapTx = (tx: any) => { - return type === 'transaction' - ? mapRelatedTxToExport(address, tx, onePrice) - : mapHrc20TxToExport(address, tx, erc20Map, erc721Map, erc1155Map) + if(type === 'erc20') { + return mapHrc20TxToExport(address, tx, erc20Map, erc721Map, erc1155Map) + } else if (type === 'internal_transaction') { + return mapInternalTxToExport(address, tx, onePrice) + } + return mapRelatedTxToExport(address, tx, onePrice) } - const mappedTxs = txs.map((tx) => mapTx(tx)) + const mappedTxs = txs.map(mapTx) const header = mappedTxs.filter((_, index) => index === 0).map(item => Object.keys(item)) const body = mappedTxs .map((item) => Object.values(item)) diff --git a/src/pages/ExportData/index.tsx b/src/pages/ExportData/index.tsx index dbf99b3..df2dada 100644 --- a/src/pages/ExportData/index.tsx +++ b/src/pages/ExportData/index.tsx @@ -4,7 +4,7 @@ import { Address, BaseContainer, BasePage, Button, TipContent } from "src/compon import { Heading, DateInput, Box, Spinner, Tip, Text } from "grommet"; import styled from "styled-components"; import useQuery from "../../hooks/useQuery"; -import { getRelatedTransactionsByType } from "../../api/client"; +import {getBlocks, getRelatedTransactionsByType} from "../../api/client"; import { downloadCSV } from "./export-utils"; import dayjs from "dayjs"; import { toaster } from "../../App"; @@ -35,9 +35,11 @@ const DownloadButton = styled(Button)` height: 42px; ` +const DefaultLimit = 5000 + export const ExportData = () => { const query = useQuery(); - const address = query.get('address') || ''; + const address = (query.get('address') || '').toLowerCase() const type = (query.get('type') || 'transaction') as TRelatedTransaction; const dateFormat = 'YYYY-MM-DD' @@ -63,22 +65,6 @@ export const ExportData = () => { inputProps: { width: '170px' } } - const filter = { - offset: 0, - limit: 5000, - orderBy: 'block_number', - orderDirection: 'desc', - filters: [{ - type: 'gt', - property: 'timestamp', - value: `'${dateFrom}'` - }, { - type: 'lt', - property: 'timestamp', - value: `'${dayjs(dateTo).add(1, 'day').format(dateFormat)}'` - }] - } - const showErrorNotification = () => { toaster.show({ message: () => ( @@ -93,11 +79,81 @@ export const ExportData = () => { const onDownloadClicked = async () => { try { setIsDownloading(true) + + const txsFilter: any = { + offset: 0, + limit: DefaultLimit, + orderBy: 'block_number', + orderDirection: 'desc', + filters: [] + } + + // if (type === 'transaction') { + // if (dateFrom) { + // txsFilter.filters.push({ + // type: 'gt', + // property: 'timestamp', + // value: `'${dateFrom}'` + // }) + // } + // if (dateTo) { + // txsFilter.filters.push({ + // type: 'lt', + // property: 'timestamp', + // value: `'${dayjs(dateTo).add(1, 'day').format(dateFormat)}'` + // }) + // } + // } + + // Get block numbers first, then filter internal by block number (not timestamp) + if (dateFrom) { + const blockFromFilter = { + offset: 0, + limit: 1, + orderBy: 'number', + orderDirection: 'asc', + filters: [{ + type: 'gte', + property: 'timestamp', + value: `'${dateFrom}'` + }] + } + const [blockFrom] = await getBlocks([0, blockFromFilter]); + if(blockFrom) { + txsFilter.filters.push({ + type: 'gte', + property: 'block_number', + value: blockFrom.number + }) + } + } + if (dateTo) { + const blockToFilter = { + offset: 0, + limit: 1, + orderBy: 'number', + orderDirection: 'desc', + filters: [{ + type: 'lt', + property: 'timestamp', + value: `'${dayjs(dateTo).add(1, 'day').format(dateFormat)}'` + }] + } + const [blockTo] = await getBlocks([0, blockToFilter]); + if(blockTo) { + txsFilter.filters.push({ + type: 'lte', + property: 'block_number', + value: blockTo.number + }) + } + } + const txs = await getRelatedTransactionsByType([ 0, address, type, - filter, + txsFilter, ]); const downloadParams = { type, @@ -126,42 +182,47 @@ export const ExportData = () => { } const getTxTextType = (type: TRelatedTransaction) => { - return type === 'transaction' ? 'transactions' : type + ' transactions' + if(type === 'transaction') { + return 'transactions' + } else if (type === 'internal_transaction') { + return 'internal transactions' + } + return type + ' transactions' } - return + return - Export transactions + + Download Data + ({getTxTextType(type)}) + - Export the last {filter.limit} {getTxTextType(type)} for
+ Export the last {DefaultLimit} {getTxTextType(type)} for
{type === 'transaction' && 'starting from'} - {/* TODO: support timestamp filter on backend side */} - {type === 'transaction' && - - - }> - onChangeDateFrom(value)} - /> - - -
to
- - }> - onChangeDateTo(value)} - /> - - -
- } + + + }> + onChangeDateFrom(value)} + /> + + +
to
+ + }> + onChangeDateTo(value)} + /> + + +