Commit d22d9d0f authored by isstuev's avatar isstuev

txs api start

parent 12088540
/* eslint-disable max-len */
export const tx = {
hash: '0x1ea365d2144796f793883534aa51bf20d23292b19478994eede23dfc599e7c34',
status: 'ok' as Transaction['status'],
block_num: 15006918,
confirmation_num: 283,
confirmation_duration: 30,
timestamp: 1662623567695,
address_from: {
hash: '0x97Aa2EfcF35c0f4c9AaDDCa8c2330fa7A9533830',
type: 'Address',
alias: '',
},
address_to: {
hash: '0x35317007D203b8a86CA727ad44E473E40450E378',
type: 'Contract',
alias: '',
},
amount: {
value: 0.03,
value_usd: 35.5,
},
fee: {
value: 0.002395904453623692,
value_usd: 2.84,
},
gas_price: 0.000000017716513811,
gas_limit: 208420,
gas_used: 159319,
gas_fees: {
base: 13.538410068,
max: 20.27657523,
max_priority: 1.5,
},
burnt_fees: {
value: 0.002156925953623692,
value_usd: 2.55,
},
type: {
value: '2',
eip: 'EIP-1559',
},
nonce: 4,
position: 342,
input_hex: '0x42842e0e0000000000000000000000007767dac225a233ea1055d79fb227b1696d538b75000000000000000000000000fc3017c31fe752fc48e904050ea5d6edfc38a1b00000000000000000000000000000000000000000000000000000000000000e3b',
transferred_tokens: [
{ from: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01', to: '0xF7A558692dFB5F456e291791da7FAE8Dd046574e', token: { symbol: 'VIK', hash: '0xADFE00d92e5A16e773891F59780e6e54f40B532e', name: 'Viktor Coin' }, amount: 192.7, usd: 194.05 },
{ from: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45', to: '0x12E80C27BfFBB76b4A8d26FF2bfd3C9f310FFA01', token: { symbol: 'PAO', hash: '0xC98a06220239818B086CD96756d4E3bC41EC848f', name: 'POA Candy' }, amount: 76.1851851851846, usd: 194.05 },
],
txType: 'transaction' as TxType,
};
export type TxType = 'contract-call' | 'transaction' | 'token-transfer' | 'internal-tx' | 'multicall';
import type { Transaction } from 'types/api/transaction';
import type { Transaction } from 'types/api/transaction';
import { tx } from './tx';
import type { TxType } from './tx';
export const txs = [
{
...tx,
method: 'Withdraw',
txType: 'transaction' as TxType,
errorText: '',
},
{
...tx,
status: 'error' as Transaction['status'],
errorText: 'Error: (Awaiting internal transactions for reason)',
txType: 'contract-call' as TxType,
method: 'CommitHash CommitHash CommitHash CommitHash',
amount: {
value: 0.04,
value_usd: 35.5,
},
fee: {
value: 0.002295904453623692,
value_usd: 2.84,
},
},
{
...tx,
status: null,
txType: 'token-transfer' as TxType,
method: 'Multicall',
address_from: {
hash: '0x97Aa2EfcF35c0f4c9AaDDCa8c2330fa7A9533830',
alias: 'tkdkdkdkdkdkdkdkdkdkdkdkdkdkd.eth',
type: 'ENS name',
},
amount: {
value: 0.02,
value_usd: 35.5,
},
fee: {
value: 0.002495904453623692,
value_usd: 2.84,
},
errorText: '',
},
];
import BigNumber from 'bignumber.js';
export default function compareBns(value1: string | number, value2: string | number) {
const value1Bn = new BigNumber(value1);
const value2Bn = new BigNumber(value2);
if (value1Bn.isGreaterThan(value2Bn)) {
return 1;
}
if (value1Bn.isLessThan(value2Bn)) {
return -1;
}
return 0;
}
import BigNumber from 'bignumber.js';
export default function divideBns(dividend: string | number, divisor: string | number, accuracy?: number) {
const dividendBn = new BigNumber(dividend);
const divisorBn = new BigNumber(divisor);
const result = dividendBn.dividedBy(divisorBn);
let numResult: string;
if (accuracy) {
numResult = result.toFixed(accuracy);
}
numResult = result.toFixed();
return parseFloat(numResult);
}
import BigNumber from 'bignumber.js';
import { WEI, GWEI } from 'lib/consts';
export default function getValue(value: string | number, unit: 'wei' | 'gwei' | 'ether' = 'wei') {
let unitBn: BigNumber.Value;
switch (unit) {
case 'wei':
unitBn = WEI;
break;
case 'gwei':
unitBn = GWEI;
break;
default:
unitBn = new BigNumber(1);
}
const valueBn = new BigNumber(value);
const valueCurr = valueBn.dividedBy(unitBn);
return valueCurr;
}
import type { NextApiRequest } from 'next';
import handler from 'lib/api/handler';
const getUrl = (req: NextApiRequest) => {
const searchParams: Record<string, string> = {};
Object.entries(req.query).forEach(([ key, value ]) => {
searchParams[key] = Array.isArray(value) ? value.join(',') : (value || '');
});
const searchParamsStr = new URLSearchParams(searchParams).toString();
return `/v2/transactions/${ searchParamsStr ? '?' + searchParamsStr : '' }`;
};
const requestHandler = handler(getUrl, [ 'GET' ]);
export default requestHandler;
...@@ -8,13 +8,13 @@ export interface Transaction { ...@@ -8,13 +8,13 @@ export interface Transaction {
result: string; result: string;
confirmations: number; confirmations: number;
status: 'ok' | 'error' | null; status: 'ok' | 'error' | null;
block: number; block: number | null;
timestamp: string; timestamp: string;
confirmation_duration: Array<number>; confirmation_duration: Array<number>;
from: AddressParam; from: AddressParam;
to: AddressParam; to: AddressParam;
created_contract: AddressParam; created_contract: AddressParam;
value: number; value: string;
fee: Fee; fee: Fee;
gas_price: number; gas_price: number;
type: number; type: number;
......
export type Unit = 'wei' | 'gwei' | 'ether';
...@@ -2,45 +2,40 @@ import { Box, Text, chakra } from '@chakra-ui/react'; ...@@ -2,45 +2,40 @@ import { Box, Text, chakra } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import { WEI, GWEI } from 'lib/consts'; import type { Unit } from 'types/unit';
import getValue from 'lib/tx/getValue';
interface Props { interface Props {
value: string; value: string;
unit?: 'wei' | 'gwei' | 'ether'; unit?: Unit;
currency?: string; currency?: string;
exchangeRate?: string; exchangeRate?: string | null;
className?: string; className?: string;
accuracy?: number; accuracy?: number;
accuracyUsd?: number; accuracyUsd?: number;
} }
const CurrencyValue = ({ value, currency = '', unit = 'wei', exchangeRate, className, accuracy, accuracyUsd }: Props) => { const CurrencyValue = ({ value, currency = '', unit, exchangeRate, className, accuracy, accuracyUsd }: Props) => {
let unitBn: BigNumber.Value; const valueCurr = getValue(value, unit);
switch (unit) { const valueResult = parseFloat(accuracy ? valueCurr.toFixed(accuracy) : valueCurr.toFixed());
case 'wei':
unitBn = WEI;
break;
case 'gwei':
unitBn = GWEI;
break;
default:
unitBn = new BigNumber(1);
}
const valueBn = new BigNumber(value); let usdContent;
const valueCurr = valueBn.dividedBy(unitBn); if (exchangeRate !== undefined && exchangeRate !== null) {
const exchangeRateBn = new BigNumber(exchangeRate || 0); const exchangeRateBn = new BigNumber(exchangeRate);
const usdBn = valueCurr.times(exchangeRateBn); const usdBn = valueCurr.times(exchangeRateBn);
const usdResult = parseFloat(accuracyUsd ? usdBn.toFixed(accuracyUsd) : usdBn.toFixed());
usdContent = (
<Text as="span" variant="secondary" whiteSpace="pre" fontWeight={ 400 }> (${ usdResult })</Text>
);
}
return ( return (
<Box as="span" className={ className }> <Box as="span" className={ className }>
<Text as="span"> <Text as="span">
{ accuracy ? valueCurr.toFixed(accuracy) : valueCurr.toFixed() }{ currency ? ` ${ currency }` : '' } { valueResult }{ currency ? ` ${ currency }` : '' }
</Text> </Text>
{ exchangeRate !== undefined && exchangeRate !== null && { usdContent }
// TODO: mb need to implement rounding to the first significant digit
<Text as="span" variant="secondary" whiteSpace="pre" fontWeight={ 400 }> (${ accuracyUsd ? usdBn.toFixed(accuracyUsd) : usdBn.toFixed() })</Text>
}
</Box> </Box>
); );
}; };
......
...@@ -153,13 +153,13 @@ const TxDetails = () => { ...@@ -153,13 +153,13 @@ const TxDetails = () => {
title="Value" title="Value"
hint="Value sent in the native token (and USD) if applicable." hint="Value sent in the native token (and USD) if applicable."
> >
<CurrencyValue value={ String(data.value) } currency={ appConfig.network.currency } exchangeRate={ data.exchange_rate }/> <CurrencyValue value={ data.value } currency={ appConfig.network.currency } exchangeRate={ data.exchange_rate }/>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Transaction fee" title="Transaction fee"
hint="Total transaction fee." hint="Total transaction fee."
> >
<CurrencyValue value={ String(data.fee.value) } currency={ appConfig.network.currency } exchangeRate={ data.exchange_rate }/> <CurrencyValue value={ data.fee.value } currency={ appConfig.network.currency } exchangeRate={ data.exchange_rate }/>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Gas price" title="Gas price"
......
import { Box, Heading, Text, Flex, Link, useColorModeValue } from '@chakra-ui/react'; import { Box, Heading, Text, Flex, Link, useColorModeValue } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import React from 'react'; import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement'; import type { Transaction } from 'types/api/transaction';
import type { txs } from 'data/txs'; import divideBns from 'lib/bigint/divideBns';
import { nbsp } from 'lib/html-entities';
import link from 'lib/link/link'; import link from 'lib/link/link';
import getValue from 'lib/tx/getValue';
import CurrencyValue from 'ui/shared/CurrencyValue';
import TextSeparator from 'ui/shared/TextSeparator'; import TextSeparator from 'ui/shared/TextSeparator';
import Utilization from 'ui/shared/Utilization'; import Utilization from 'ui/shared/Utilization';
const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => { const TxAdditionalInfo = ({ tx }: { tx: Transaction }) => {
const sectionBorderColor = useColorModeValue('gray.200', 'whiteAlpha.200'); const sectionBorderColor = useColorModeValue('gray.200', 'whiteAlpha.200');
const sectionProps = { const sectionProps = {
borderBottom: '1px solid', borderBottom: '1px solid',
...@@ -31,40 +33,54 @@ const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => { ...@@ -31,40 +33,54 @@ const TxAdditionalInfo = ({ tx }: { tx: ArrayElement<typeof txs> }) => {
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Transaction fee</Text> <Text { ...sectionTitleProps }>Transaction fee</Text>
<Flex> <Flex>
<Text>{ tx.fee.value }{ nbsp }{ appConfig.network.currency }</Text> <CurrencyValue
<Text variant="secondary" ml={ 1 }>(${ tx.fee.value_usd.toFixed(2) })</Text> value={ tx.fee.value }
currency={ appConfig.network.currency }
exchangeRate={ tx.exchange_rate }
accuracyUsd={ 2 }
/>
</Flex> </Flex>
</Box> </Box>
{ tx.gas_used !== null && (
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Gas limit & usage by transaction</Text> <Text { ...sectionTitleProps }>Gas limit & usage by transaction</Text>
<Flex> <Flex>
<Text>{ tx.gas_used.toLocaleString('en') }</Text> <Text>{ BigNumber(tx.gas_used).toFormat() }</Text>
<TextSeparator/> <TextSeparator/>
<Text>{ tx.gas_limit.toLocaleString('en') }</Text> <Text>{ BigNumber(tx.gas_limit).toFormat() }</Text>
<Utilization ml={ 4 } value={ tx.gas_used / tx.gas_limit }/> <Utilization ml={ 4 } value={ Number(divideBns(tx.gas_used, tx.gas_limit, 2)) }/>
</Flex> </Flex>
</Box> </Box>
) }
{ (tx.base_fee_per_gas !== null || tx.max_fee_per_gas !== null || tx.max_priority_fee_per_gas !== null) && (
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Gas fees (Gwei)</Text> <Text { ...sectionTitleProps }>Gas fees (Gwei)</Text>
{ tx.base_fee_per_gas !== null && (
<Box> <Box>
<Text as="span" fontWeight="500">Base: </Text> <Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.base }</Text> <Text fontWeight="600" as="span">{ getValue(tx.base_fee_per_gas, 'gwei').toFixed() }</Text>
</Box> </Box>
) }
{ tx.max_fee_per_gas !== null && (
<Box> <Box>
<Text as="span" fontWeight="500">Max: </Text> <Text as="span" fontWeight="500">Max: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.max }</Text> <Text fontWeight="600" as="span">{ getValue(tx.max_fee_per_gas, 'gwei').toFixed() }</Text>
</Box> </Box>
) }
{ tx.max_priority_fee_per_gas !== null && (
<Box> <Box>
<Text as="span" fontWeight="500">Max priority: </Text> <Text as="span" fontWeight="500">Max priority: </Text>
<Text fontWeight="600" as="span">{ tx.gas_fees.max_priority }</Text> <Text fontWeight="600" as="span">{ getValue(tx.max_priority_fee_per_gas, 'gwei').toFixed() }</Text>
</Box> </Box>
) }
</Box> </Box>
) }
<Box { ...sectionProps } mb={ 4 }> <Box { ...sectionProps } mb={ 4 }>
<Text { ...sectionTitleProps }>Others</Text> <Text { ...sectionTitleProps }>Others</Text>
<Box> <Box>
<Text as="span" fontWeight="500">Txn type: </Text> <Text as="span" fontWeight="500">Txn type: </Text>
<Text fontWeight="600" as="span">{ tx.type.value }</Text> <Text fontWeight="600" as="span">{ tx.type }</Text>
<Text fontWeight="400" as="span" ml={ 1 } color="gray.500">({ tx.type.eip })</Text> { tx.type === 2 && <Text fontWeight="400" as="span" ml={ 1 } color="gray.500">(EIP-1559)</Text> }
</Box> </Box>
<Box> <Box>
<Text as="span" fontWeight="500">Nonce: </Text> <Text as="span" fontWeight="500">Nonce: </Text>
......
import { Box, HStack, Show } from '@chakra-ui/react'; import { Box, HStack, Show } from '@chakra-ui/react';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import type { TransactionsResponse } from 'types/api/transaction';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
import { txs } from 'data/txs'; import compareBns from 'lib/bigint/compareBns';
import FilterButton from 'ui/shared/FilterButton'; import FilterButton from 'ui/shared/FilterButton';
import FilterInput from 'ui/shared/FilterInput'; import FilterInput from 'ui/shared/FilterInput';
import Pagination from 'ui/shared/Pagination'; import Pagination from 'ui/shared/Pagination';
...@@ -13,11 +14,12 @@ import TxsListItem from './TxsListItem'; ...@@ -13,11 +14,12 @@ import TxsListItem from './TxsListItem';
import TxsTable from './TxsTable'; import TxsTable from './TxsTable';
type Props = { type Props = {
txs: TransactionsResponse['items'];
showDescription?: boolean; showDescription?: boolean;
showSortButton?: boolean; showSortButton?: boolean;
} }
const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => { const TxsContent = ({ showSortButton = true, showDescription = true, txs }: Props) => {
const [ sorting, setSorting ] = useState<Sort>(); const [ sorting, setSorting ] = useState<Sort>();
const [ sortedTxs, setSortedTxs ] = useState(txs); const [ sortedTxs, setSortedTxs ] = useState(txs);
...@@ -50,21 +52,21 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => ...@@ -50,21 +52,21 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) =>
useEffect(() => { useEffect(() => {
switch (sorting) { switch (sorting) {
case 'val-desc': case 'val-desc':
setSortedTxs([ ...txs ].sort((tx1, tx2) => tx1.amount.value - tx2.amount.value)); setSortedTxs([ ...txs ].sort((tx1, tx2) => compareBns(tx1.value, tx2.value)));
break; break;
case 'val-asc': case 'val-asc':
setSortedTxs([ ...txs ].sort((tx1, tx2) => tx2.amount.value - tx1.amount.value)); setSortedTxs([ ...txs ].sort((tx1, tx2) => compareBns(tx2.value, tx1.value)));
break; break;
case 'fee-desc': case 'fee-desc':
setSortedTxs([ ...txs ].sort((tx1, tx2) => tx1.fee.value - tx2.fee.value)); setSortedTxs([ ...txs ].sort((tx1, tx2) => compareBns(tx1.fee.value, tx2.fee.value)));
break; break;
case 'fee-asc': case 'fee-asc':
setSortedTxs([ ...txs ].sort((tx1, tx2) => tx2.fee.value - tx1.fee.value)); setSortedTxs([ ...txs ].sort((tx1, tx2) => compareBns(tx2.fee.value, tx1.fee.value)));
break; break;
default: default:
setSortedTxs(txs); setSortedTxs(txs);
} }
}, [ sorting ]); }, [ sorting, txs ]);
return ( return (
<> <>
...@@ -93,7 +95,7 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) => ...@@ -93,7 +95,7 @@ const TxsContent = ({ showSortButton = true, showDescription = true }: Props) =>
placeholder="Search by addresses, hash, method..." placeholder="Search by addresses, hash, method..."
/> />
</HStack> </HStack>
<Show below="lg">{ sortedTxs.map(tx => <TxsListItem tx={ tx } key={ tx.hash }/>) }</Show> <Show below="lg"><Box>{ sortedTxs.map(tx => <TxsListItem tx={ tx } key={ tx.hash }/>) }</Box></Show>
<Show above="lg"><TxsTable txs={ sortedTxs } sort={ sort } sorting={ sorting }/></Show> <Show above="lg"><TxsTable txs={ sortedTxs } sort={ sort } sorting={ sorting }/></Show>
<Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}> <Box mx={{ base: 0, lg: 6 }} my={{ base: 6, lg: 3 }}>
<Pagination currentPage={ 1 }/> <Pagination currentPage={ 1 }/>
......
...@@ -13,13 +13,13 @@ import { ...@@ -13,13 +13,13 @@ import {
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import React from 'react'; import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement'; import type { Transaction } from 'types/api/transaction';
import type { txs } from 'data/txs';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import transactionIcon from 'icons/transactions.svg'; import transactionIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import link from 'lib/link/link'; import link from 'lib/link/link';
import getValue from 'lib/tx/getValue';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
...@@ -28,7 +28,7 @@ import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; ...@@ -28,7 +28,7 @@ import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton'; import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton';
import TxType from 'ui/txs/TxType'; import TxType from 'ui/txs/TxType';
const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { const TxsListItem = ({ tx }: {tx: Transaction}) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const iconColor = useColorModeValue('blue.600', 'blue.300'); const iconColor = useColorModeValue('blue.600', 'blue.300');
...@@ -36,11 +36,13 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -36,11 +36,13 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
return ( return (
<> <>
<Box width="100%" borderBottom="1px solid" borderColor={ borderColor } _first={{ borderTop: '1px solid', borderColor: { borderColor } }}> <Box width="100%" borderBottom="1px solid" borderColor={ borderColor } _first={{ borderTop: '1px solid', borderColor }}>
<Flex justifyContent="space-between" mt={ 4 }> <Flex justifyContent="space-between" mt={ 4 }>
<HStack> <HStack>
<TxType type={ tx.txType }/> { /* TODO: we don't recieve type from api */ }
<TxStatus status={ tx.status } errorText={ tx.errorText }/> { /* <TxType type={ tx.type }/> */ }
<TxType type="transaction"/>
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
</HStack> </HStack>
<TxAdditionalInfoButton onClick={ onOpen }/> <TxAdditionalInfoButton onClick={ onOpen }/>
</Flex> </Flex>
...@@ -65,6 +67,7 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -65,6 +67,7 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Flex> </Flex>
<Flex mt={ 3 }> <Flex mt={ 3 }>
<Text as="span" whiteSpace="pre">Method </Text> <Text as="span" whiteSpace="pre">Method </Text>
{ /* TODO: we don't recieve method from api */ }
<Text <Text
as="span" as="span"
variant="secondary" variant="secondary"
...@@ -72,19 +75,22 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -72,19 +75,22 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
whiteSpace="nowrap" whiteSpace="nowrap"
textOverflow="ellipsis" textOverflow="ellipsis"
> >
{ tx.method } { /* { tx.method } */ }
CommitHash
</Text> </Text>
</Flex> </Flex>
{ tx.block !== null && (
<Box mt={ 2 }> <Box mt={ 2 }>
<Text as="span">Block </Text> <Text as="span">Block </Text>
<Link href={ link('block', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link> <Link href={ link('block', { id: tx.block.toString() }) }>{ tx.block }</Link>
</Box> </Box>
) }
<Flex alignItems="center" height={ 6 } mt={ 6 }> <Flex alignItems="center" height={ 6 } mt={ 6 }>
<Address width="calc((100%-40px)/2)"> <Address width="calc((100%-40px)/2)">
<AddressIcon hash={ tx.address_from.hash }/> <AddressIcon hash={ tx.from.hash }/>
<AddressLink <AddressLink
hash={ tx.address_from.hash } hash={ tx.from.hash }
alias={ tx.address_from.alias } alias={ tx.from.name }
fontWeight="500" fontWeight="500"
ml={ 2 } ml={ 2 }
/> />
...@@ -96,10 +102,10 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -96,10 +102,10 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
color="gray.500" color="gray.500"
/> />
<Address width="calc((100%-40px)/2)"> <Address width="calc((100%-40px)/2)">
<AddressIcon hash={ tx.address_to.hash }/> <AddressIcon hash={ tx.to.hash }/>
<AddressLink <AddressLink
hash={ tx.address_to.hash } hash={ tx.to.hash }
alias={ tx.address_to.alias } alias={ tx.to.name }
fontWeight="500" fontWeight="500"
ml={ 2 } ml={ 2 }
/> />
...@@ -107,11 +113,11 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -107,11 +113,11 @@ const TxsListItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Flex> </Flex>
<Box mt={ 2 }> <Box mt={ 2 }>
<Text as="span">Value { appConfig.network.currency } </Text> <Text as="span">Value { appConfig.network.currency } </Text>
<Text as="span" variant="secondary">{ tx.amount.value.toFixed(8) }</Text> <Text as="span" variant="secondary">{ parseFloat(getValue(tx.value).toFixed()) }</Text>
</Box> </Box>
<Box mt={ 2 } mb={ 3 }> <Box mt={ 2 } mb={ 3 }>
<Text as="span">Fee { appConfig.network.currency } </Text> <Text as="span">Fee { appConfig.network.currency } </Text>
<Text as="span" variant="secondary">{ tx.fee.value.toFixed(8) }</Text> <Text as="span" variant="secondary">{ parseFloat(getValue(tx.fee.value).toFixed()) }</Text>
</Box> </Box>
</Box> </Box>
<Modal isOpen={ isOpen } onClose={ onClose } size="full"> <Modal isOpen={ isOpen } onClose={ onClose } size="full">
......
import { Show, Alert } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TransactionsResponse } from 'types/api/transaction';
import useFetch from 'lib/hooks/useFetch';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxsContent from './TxsContent'; import TxsContent from './TxsContent';
import TxsSkeletonDesktop from './TxsSkeletonDesktop';
import TxsSkeletonMobile from './TxsSkeletonMobile';
const TxsValidated = () => {
const fetch = useFetch();
const { data, isLoading, isError } =
useQuery<unknown, unknown, TransactionsResponse>([ 'transactions_pending' ], async() => fetch('/api/transactions/?filter=pending'));
if (isError) {
return <DataFetchAlert/>;
}
if (isLoading) {
return (
<>
<Show below="lg"><TxsSkeletonMobile isPending/></Show>
<Show above="lg"><TxsSkeletonDesktop isPending/></Show>
</>
);
}
if (!data || !data.items) {
return <Alert>There are no transactions.</Alert>;
}
const TxsPending = () => { return <TxsContent txs={ data.items } showDescription={ false }/>;
return <TxsContent showDescription={ false }/>;
}; };
export default TxsPending; export default TxsValidated;
import { Skeleton, Flex } from '@chakra-ui/react';
import React from 'react';
import SkeletonTable from 'ui/shared/SkeletonTable';
const TxsInternalsSkeletonDesktop = ({ isPending }: {isPending?: boolean}) => {
return (
<>
{ !isPending && <Skeleton h={ 6 } w="100%" mb={ 12 }/> }
<Flex columnGap={ 3 } h={ 8 } mb={ 6 }>
<Skeleton w="78px"/>
<Skeleton w="360px"/>
</Flex>
<SkeletonTable columns={ [ '20%', '20%', '20%', '20%', '20%' ] }/>
</>
);
};
export default TxsInternalsSkeletonDesktop;
import { Skeleton, Flex, Box, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
const TxInternalsSkeletonMobile = ({ isPending }: {isPending?: boolean}) => {
const borderColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
return (
<>
{ !isPending && <Skeleton h={ 6 } w="100%" mb={ 12 }/> }
<Flex columnGap={ 3 } h={ 8 } mb={ 6 }>
<Skeleton w="36px" flexShrink={ 0 }/>
<Skeleton w="36px" flexShrink={ 0 }/>
<Skeleton w="100%"/>
</Flex>
<Box>
{ Array.from(Array(2)).map((item, index) => (
<Flex
key={ index }
flexDirection="column"
paddingBottom={ 3 }
paddingTop={ 4 }
borderTopWidth="1px"
borderColor={ borderColor }
_last={{
borderBottomWidth: '1px',
}}
>
<Flex h={ 6 }>
<Skeleton w="100px" mr={ 2 } h={ 6 }/>
<Skeleton w="100px" h={ 6 }/>
</Flex>
<Skeleton w="100%" h="30px" mt={ 3 }/>
<Skeleton w="50%" h={ 6 } mt={ 3 }/>
<Skeleton w="50%" h={ 6 } mt={ 2 }/>
<Skeleton w="100%" h={ 6 } mt={ 6 }/>
<Skeleton w="50%" h={ 6 } mt={ 2 }/>
<Skeleton w="50%" h={ 6 } mt={ 2 }/>
</Flex>
)) }
</Box>
</>
);
};
export default TxInternalsSkeletonMobile;
...@@ -2,15 +2,15 @@ import { Link, Table, Thead, Tbody, Tr, Th, TableContainer, Icon } from '@chakra ...@@ -2,15 +2,15 @@ import { Link, Table, Thead, Tbody, Tr, Th, TableContainer, Icon } from '@chakra
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import React from 'react'; import React from 'react';
import type { Transaction } from 'types/api/transaction';
import type { Sort } from 'types/client/txs-sort'; import type { Sort } from 'types/client/txs-sort';
import type { txs as data } from 'data/txs';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import TxsTableItem from './TxsTableItem'; import TxsTableItem from './TxsTableItem';
type Props = { type Props = {
txs: typeof data; txs: Array<Transaction>;
sort: (field: 'val' | 'fee') => () => void; sort: (field: 'val' | 'fee') => () => void;
sorting: Sort; sorting: Sort;
} }
......
...@@ -18,15 +18,15 @@ import { ...@@ -18,15 +18,15 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type ArrayElement from 'types/utils/ArrayElement'; import type { Transaction } from 'types/api/transaction';
import type { txs } from 'data/txs';
import rightArrowIcon from 'icons/arrows/east.svg'; import rightArrowIcon from 'icons/arrows/east.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import link from 'lib/link/link'; import link from 'lib/link/link';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink'; import AddressLink from 'ui/shared/address/AddressLink';
import CurrencyValue from 'ui/shared/CurrencyValue';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip'; import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import TxStatus from 'ui/shared/TxStatus'; import TxStatus from 'ui/shared/TxStatus';
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo'; import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
...@@ -34,23 +34,28 @@ import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton'; ...@@ -34,23 +34,28 @@ import TxAdditionalInfoButton from 'ui/txs/TxAdditionalInfoButton';
import TxType from './TxType'; import TxType from './TxType';
<<<<<<< HEAD
const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
=======
const TxsTableItem = ({ tx }: {tx: Transaction}) => {
const link = useLink();
>>>>>>> 9239655 (txs api start)
const addressFrom = ( const addressFrom = (
<Address> <Address>
<Tooltip label={ tx.address_from.type }> <Tooltip label={ tx.from.implementation_name }>
<Box display="flex"><AddressIcon hash={ tx.address_from.hash }/></Box> <Box display="flex"><AddressIcon hash={ tx.from.hash }/></Box>
</Tooltip> </Tooltip>
<AddressLink hash={ tx.address_from.hash } alias={ tx.address_from.alias } fontWeight="500" ml={ 2 }/> <AddressLink hash={ tx.from.hash } alias={ tx.from.name } fontWeight="500" ml={ 2 }/>
</Address> </Address>
); );
const addressTo = ( const addressTo = (
<Address> <Address>
<Tooltip label={ tx.address_to.type }> <Tooltip label={ tx.to.implementation_name }>
<Box display="flex"> <AddressIcon hash={ tx.address_to.hash }/></Box> <Box display="flex"><AddressIcon hash={ tx.to.hash }/></Box>
</Tooltip> </Tooltip>
<AddressLink hash={ tx.address_to.hash } alias={ tx.address_to.alias } fontWeight="500" ml={ 2 }/> <AddressLink hash={ tx.to.hash } alias={ tx.to.name } fontWeight="500" ml={ 2 }/>
</Address> </Address>
); );
...@@ -77,8 +82,10 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -77,8 +82,10 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Td> </Td>
<Td> <Td>
<VStack alignItems="start"> <VStack alignItems="start">
<TxType type={ tx.txType }/> { /* TODO: we don't recieve type from api */ }
<TxStatus status={ tx.status } errorText={ tx.errorText }/> { /* <TxType type={ tx.type }/> */ }
<TxType type="transaction"/>
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
</VStack> </VStack>
</Td> </Td>
<Td> <Td>
...@@ -94,17 +101,30 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -94,17 +101,30 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</VStack> </VStack>
</Td> </Td>
<Td> <Td>
<TruncatedTextTooltip label={ tx.method }> { /* TODO: we don't recieve method from api */ }
{ /* <TruncatedTextTooltip label={ tx.method }>
<Tag <Tag
colorScheme={ tx.method === 'Multicall' ? 'teal' : 'gray' } colorScheme={ tx.method === 'Multicall' ? 'teal' : 'gray' }
> >
{ tx.method } { tx.method }
</Tag> </Tag>
</TruncatedTextTooltip> */ }
<TruncatedTextTooltip label="CommitHash">
<Tag
colorScheme="gray"
>
CommitHash
</Tag>
</TruncatedTextTooltip> </TruncatedTextTooltip>
</Td> </Td>
<Td> <Td>
<<<<<<< HEAD
<Link href={ link('block', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link> <Link href={ link('block', { id: tx.block_num.toString() }) }>{ tx.block_num }</Link>
=======
{ tx.block && <Link href={ link('block_index', { id: tx.block.toString() }) }>{ tx.block }</Link> }
>>>>>>> 9239655 (txs api start)
</Td> </Td>
{ /* TODO: fix "show" problem */ }
<Show above="xl"> <Show above="xl">
<Td> <Td>
{ addressFrom } { addressFrom }
...@@ -133,10 +153,10 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => { ...@@ -133,10 +153,10 @@ const TxsTableItem = ({ tx }: {tx: ArrayElement<typeof txs>}) => {
</Td> </Td>
</Show> </Show>
<Td isNumeric> <Td isNumeric>
{ tx.amount.value.toFixed(8) } <CurrencyValue value={ tx.value }/>
</Td> </Td>
<Td isNumeric> <Td isNumeric>
{ tx.fee.value.toFixed(8) } <CurrencyValue value={ tx.fee.value } accuracy={ 8 }/>
</Td> </Td>
</Tr> </Tr>
); );
......
import { Show, Alert } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import type { TransactionsResponse } from 'types/api/transaction';
import useFetch from 'lib/hooks/useFetch';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import TxsContent from './TxsContent'; import TxsContent from './TxsContent';
import TxsSkeletonDesktop from './TxsSkeletonDesktop';
import TxsSkeletonMobile from './TxsSkeletonMobile';
const TxsValidated = () => { const TxsValidated = () => {
return <TxsContent/>; const fetch = useFetch();
const { data, isLoading, isError } =
useQuery<unknown, unknown, TransactionsResponse>([ 'transactions_validated' ], async() => fetch('/api/transactions/?filter=validated'));
if (isError) {
return <DataFetchAlert/>;
}
if (isLoading) {
return (
<>
<Show below="lg"><TxsSkeletonMobile/></Show>
<Show above="lg"><TxsSkeletonDesktop/></Show>
</>
);
}
if (!data || !data.items) {
return <Alert>There are no transactions.</Alert>;
}
return <TxsContent txs={ data.items }/>;
}; };
export default TxsValidated; export default TxsValidated;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment