Commit 88c99dbd authored by tom's avatar tom

Merge branch 'block-details-api' of github.com:blockscout/frontend into blocks-list-api

parents c58606b7 925f7fdd
import { utils } from 'ethers'; import BigNumber from 'bignumber.js';
import type { Block } from 'types/api/block'; import type { Block } from 'types/api/block';
export default function getBlockReward(block: Block) { export default function getBlockReward(block: Block) {
const txFees = utils.parseUnits(block.tx_fees || '0', 'wei'); const txFees = BigNumber(block.tx_fees || 0);
const burntFees = utils.parseUnits(String(block.burnt_fees || '0'), 'wei'); const burntFees = BigNumber(block.burnt_fees || 0);
const totalReward = utils.parseUnits( const minerReward = block.rewards?.find(({ type }) => type === 'Miner Reward' || type === 'Validator Reward')?.reward;
String( const totalReward = BigNumber(minerReward || 0);
block.rewards?.find(({ type }) => type === 'Miner Reward' || type === 'Validator Reward')?.reward || const staticReward = totalReward.minus(txFees).plus(burntFees);
'0',
),
'wei',
);
const staticReward = totalReward.sub(txFees).add(burntFees);
return { return {
totalReward, totalReward,
......
export const WEI = BigInt(10 ** 18); import BigNumber from 'bignumber.js';
export const GWEI = BigInt(10 ** 9);
export const WEI = new BigNumber(10 ** 18);
export const GWEI = new BigNumber(10 ** 9);
export const WEI_IN_GWEI = WEI.dividedBy(GWEI);
export const ZERO = new BigNumber(0);
import { Grid, GridItem, Text, Icon, Link, Box, Tooltip, Alert } from '@chakra-ui/react'; import { Grid, GridItem, Text, Icon, Link, Box, Tooltip, Alert } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { utils, constants } from 'ethers'; import BigNumber from 'bignumber.js';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
...@@ -10,6 +10,7 @@ import type { Block } from 'types/api/block'; ...@@ -10,6 +10,7 @@ import type { Block } from 'types/api/block';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import getBlockReward from 'lib/block/getBlockReward'; import getBlockReward from 'lib/block/getBlockReward';
import { WEI, WEI_IN_GWEI, ZERO } from 'lib/consts';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
import type { ErrorType } from 'lib/hooks/useFetch'; import type { ErrorType } from 'lib/hooks/useFetch';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
...@@ -119,7 +120,7 @@ const BlockDetails = () => { ...@@ -119,7 +120,7 @@ const BlockDetails = () => {
{ /* api doesn't return the block processing time yet */ } { /* api doesn't return the block processing time yet */ }
{ /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ } { /* <Text>{ dayjs.duration(block.minedIn, 'second').humanize(true) }</Text> */ }
</DetailsInfoItem> </DetailsInfoItem>
{ !totalReward.eq(constants.Zero) && ( { !totalReward.isEqualTo(ZERO) && (
<DetailsInfoItem <DetailsInfoItem
title="Block reward" title="Block reward"
hint={ hint={
...@@ -128,25 +129,25 @@ const BlockDetails = () => { ...@@ -128,25 +129,25 @@ const BlockDetails = () => {
} }
columnGap={ 1 } columnGap={ 1 }
> >
<Text>{ utils.formatUnits(totalReward) } { network?.currency }</Text> <Text>{ totalReward.dividedBy(WEI).toFixed() } { network?.currency }</Text>
{ (!txFees.eq(constants.Zero) || !burntFees.eq(constants.Zero)) && ( { (!txFees.isEqualTo(ZERO) || !burntFees.isEqualTo(ZERO)) && (
<Text variant="secondary" whiteSpace="break-spaces">( <Text variant="secondary" whiteSpace="break-spaces">(
<Tooltip label="Static block reward"> <Tooltip label="Static block reward">
<span>{ utils.formatUnits(staticReward) }</span> <span>{ staticReward.dividedBy(WEI).toFixed() }</span>
</Tooltip> </Tooltip>
{ !txFees.eq(constants.Zero) && ( { !txFees.isEqualTo(ZERO) && (
<> <>
{ space }+{ space } { space }+{ space }
<Tooltip label="Txn fees"> <Tooltip label="Txn fees">
<span>{ utils.formatUnits(txFees) }</span> <span>{ txFees.dividedBy(WEI).toFixed() }</span>
</Tooltip> </Tooltip>
</> </>
) } ) }
{ !burntFees.eq(constants.Zero) && ( { !burntFees.isEqualTo(ZERO) && (
<> <>
{ space }-{ space } { space }-{ space }
<Tooltip label="Burnt fees"> <Tooltip label="Burnt fees">
<span>{ utils.formatUnits(burntFees) }</span> <span>{ burntFees.dividedBy(WEI).toFixed() }</span>
</Tooltip> </Tooltip>
</> </>
) } ) }
...@@ -161,12 +162,12 @@ const BlockDetails = () => { ...@@ -161,12 +162,12 @@ const BlockDetails = () => {
title="Gas used" title="Gas used"
hint="The total gas amount used in the block and its percentage of gas filled in the block." hint="The total gas amount used in the block and its percentage of gas filled in the block."
> >
<Text>{ utils.commify(data.gas_used) }</Text> <Text>{ BigNumber(data.gas_used).toFormat() }</Text>
<Utilization <Utilization
ml={ 4 } ml={ 4 }
mr={ 5 } mr={ 5 }
colorScheme="gray" colorScheme="gray"
value={ utils.parseUnits(data.gas_used).mul(10_000).div(utils.parseUnits(data.gas_limit)).toNumber() / 10_000 } value={ BigNumber(data.gas_used).dividedBy(BigNumber(data.gas_limit)).toNumber() }
/> />
<GasUsedToTargetRatio value={ data.gas_target_percentage || undefined }/> <GasUsedToTargetRatio value={ data.gas_target_percentage || undefined }/>
</DetailsInfoItem> </DetailsInfoItem>
...@@ -174,16 +175,16 @@ const BlockDetails = () => { ...@@ -174,16 +175,16 @@ const BlockDetails = () => {
title="Gas limit" title="Gas limit"
hint="Total gas limit provided by all transactions in the block." hint="Total gas limit provided by all transactions in the block."
> >
<Text>{ utils.commify(data.gas_limit) }</Text> <Text>{ BigNumber(data.gas_limit).toFormat() }</Text>
</DetailsInfoItem> </DetailsInfoItem>
{ data.base_fee_per_gas && ( { data.base_fee_per_gas && (
<DetailsInfoItem <DetailsInfoItem
title="Base fee per gas" title="Base fee per gas"
hint="Minimum fee required per unit of gas. Fee adjusts based on network congestion." hint="Minimum fee required per unit of gas. Fee adjusts based on network congestion."
> >
<Text>{ utils.formatUnits(utils.parseUnits(String(data.base_fee_per_gas), 'wei')) } { network?.currency } </Text> <Text>{ BigNumber(data.base_fee_per_gas).dividedBy(WEI).toFixed() } { network?.currency } </Text>
<Text variant="secondary" whiteSpace="pre"> <Text variant="secondary" whiteSpace="pre">
{ space }({ utils.formatUnits(utils.parseUnits(String(data.base_fee_per_gas), 'wei'), 'gwei') } Gwei) { space }({ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() } Gwei)
</Text> </Text>
</DetailsInfoItem> </DetailsInfoItem>
) } ) }
...@@ -195,18 +196,26 @@ const BlockDetails = () => { ...@@ -195,18 +196,26 @@ const BlockDetails = () => {
} }
> >
<Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/> <Icon as={ flameIcon } boxSize={ 5 } color="gray.500"/>
<Text ml={ 1 }>{ utils.formatUnits(burntFees) } { network?.currency }</Text> <Text ml={ 1 }>{ burntFees.dividedBy(WEI).toFixed() } { network?.currency }</Text>
{ !txFees.eq(constants.Zero) && ( { !txFees.isEqualTo(ZERO) && (
<Tooltip label="Burnt fees / Txn fees * 100%"> <Tooltip label="Burnt fees / Txn fees * 100%">
<Box> <Box>
<Utilization <Utilization
ml={ 4 } ml={ 4 }
value={ burntFees.mul(10_000).div(txFees).toNumber() / 10_000 } value={ burntFees.dividedBy(txFees).toNumber() }
/> />
</Box> </Box>
</Tooltip> </Tooltip>
) } ) }
</DetailsInfoItem> </DetailsInfoItem>
{ data.priority_fee && (
<DetailsInfoItem
title="Priority fee / Tip"
hint="User-defined tips sent to validator for transaction priority/inclusion."
>
{ BigNumber(data.priority_fee).dividedBy(WEI).toFixed() } { network?.currency }
</DetailsInfoItem>
) }
{ /* api doesn't support extra data yet */ } { /* api doesn't support extra data yet */ }
{ /* <DetailsInfoItem { /* <DetailsInfoItem
title="Extra data" title="Extra data"
...@@ -241,13 +250,13 @@ const BlockDetails = () => { ...@@ -241,13 +250,13 @@ const BlockDetails = () => {
title="Difficulty" title="Difficulty"
hint="Block difficulty for miner, used to calibrate block generation time." hint="Block difficulty for miner, used to calibrate block generation time."
> >
{ utils.commify(data.difficulty) } { BigNumber(data.difficulty).toFormat() }
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Total difficulty" title="Total difficulty"
hint="Total difficulty of the chain until this block." hint="Total difficulty of the chain until this block."
> >
{ utils.commify(data.total_difficulty) } { BigNumber(data.total_difficulty).toFormat() }
</DetailsInfoItem> </DetailsInfoItem>
{ sectionGap } { sectionGap }
...@@ -293,7 +302,7 @@ const BlockDetails = () => { ...@@ -293,7 +302,7 @@ const BlockDetails = () => {
title={ type } title={ type }
hint="Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees." hint="Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees."
> >
{ utils.formatUnits(utils.parseUnits(String(reward), 'wei')) } { network?.currency } { BigNumber(reward).dividedBy(WEI).toFixed() } { network?.currency }
</DetailsInfoItem> </DetailsInfoItem>
)) } )) }
</> </>
......
...@@ -25,7 +25,9 @@ const MarketplaceApp = ({ app, isLoading }: Props) => { ...@@ -25,7 +25,9 @@ const MarketplaceApp = ({ app, isLoading }: Props) => {
const sandboxAttributeValue = 'allow-forms allow-orientation-lock ' + const sandboxAttributeValue = 'allow-forms allow-orientation-lock ' +
'allow-pointer-lock allow-popups-to-escape-sandbox ' + 'allow-pointer-lock allow-popups-to-escape-sandbox ' +
'allow-same-origin allow-scripts ' + 'allow-same-origin allow-scripts ' +
'allow-top-navigation-by-user-activation'; 'allow-top-navigation-by-user-activation allow-popups';
const allowAttributeValue = 'clipboard-read; clipboard-write;';
useEffect(() => { useEffect(() => {
if (app && !isFrameLoading) { if (app && !isFrameLoading) {
...@@ -46,6 +48,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => { ...@@ -46,6 +48,7 @@ const MarketplaceApp = ({ app, isLoading }: Props) => {
{ app && ( { app && (
<Box <Box
allow={ allowAttributeValue }
ref={ ref } ref={ ref }
sandbox={ sandboxAttributeValue } sandbox={ sandboxAttributeValue }
as="iframe" as="iframe"
......
import { Box, Text, chakra } from '@chakra-ui/react'; import { Box, Text, chakra } from '@chakra-ui/react';
import { utils, constants } from 'ethers'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import { WEI, GWEI } from 'lib/consts';
interface Props { interface Props {
value: string; value: string;
unit?: 'wei' | 'gwei' | 'ether'; unit?: 'wei' | 'gwei' | 'ether';
currency?: string; currency?: string;
exchangeRate?: string; exchangeRate?: string;
className?: string; className?: string;
accuracyUsd?: number;
} }
const CurrencyValue = ({ value, currency = '', unit = 'wei', exchangeRate, className }: Props) => { const CurrencyValue = ({ value, currency = '', unit = 'wei', exchangeRate, className, accuracyUsd }: Props) => {
const valueBn = utils.parseUnits(value, unit); let unitBn: BigNumber.Value;
const exchangeRateBn = utils.parseUnits(exchangeRate || '0', 'ether'); switch (unit) {
const usdBn = valueBn.mul(exchangeRateBn).div(constants.WeiPerEther); 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);
const exchangeRateBn = new BigNumber(exchangeRate || 0);
const usdBn = valueCurr.times(exchangeRateBn);
return ( return (
<Box as="span" className={ className }> <Box as="span" className={ className }>
<Text as="span"> <Text as="span">
{ Number(utils.formatUnits(valueBn)).toLocaleString() }{ currency ? ` ${ currency }` : '' }</Text> { valueCurr.toFixed() }{ currency ? ` ${ currency }` : '' }
</Text>
{ exchangeRate !== undefined && exchangeRate !== null && { exchangeRate !== undefined && exchangeRate !== null &&
<Text as="span" variant="secondary" whiteSpace="pre" fontWeight={ 400 }> (${ utils.formatUnits(usdBn) })</Text> } // 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>
); );
}; };
......
import { Grid, GridItem, Text, Box, Icon, Link, Flex } from '@chakra-ui/react'; import { Grid, GridItem, Text, Box, Icon, Link, Flex } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { utils } from 'ethers'; import BigNumber from 'bignumber.js';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { scroller, Element } from 'react-scroll'; import { scroller, Element } from 'react-scroll';
...@@ -9,6 +9,7 @@ import type { Transaction } from 'types/api/transaction'; ...@@ -9,6 +9,7 @@ import type { Transaction } from 'types/api/transaction';
import clockIcon from 'icons/clock.svg'; import clockIcon from 'icons/clock.svg';
import flameIcon from 'icons/flame.svg'; import flameIcon from 'icons/flame.svg';
import { WEI, WEI_IN_GWEI } from 'lib/consts';
// import errorIcon from 'icons/status/error.svg'; // import errorIcon from 'icons/status/error.svg';
// import successIcon from 'icons/status/success.svg'; // import successIcon from 'icons/status/success.svg';
import dayjs from 'lib/date/dayjs'; import dayjs from 'lib/date/dayjs';
...@@ -166,17 +167,17 @@ const TxDetails = () => { ...@@ -166,17 +167,17 @@ const TxDetails = () => {
title="Gas price" title="Gas price"
hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage." hint="Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage."
> >
<Text mr={ 1 }>{ utils.formatUnits(utils.parseUnits(String(data.gas_price), 'wei')) } { selectedNetwork?.currency }</Text> <Text mr={ 1 }>{ BigNumber(data.gas_price).dividedBy(WEI).toFixed() } { selectedNetwork?.currency }</Text>
<Text variant="secondary">({ Number(utils.formatUnits(utils.parseUnits(String(data.gas_price), 'wei'), 'gwei')) } Gwei)</Text> <Text variant="secondary">({ BigNumber(data.gas_price).dividedBy(WEI_IN_GWEI).toFixed() } Gwei)</Text>
</DetailsInfoItem> </DetailsInfoItem>
<DetailsInfoItem <DetailsInfoItem
title="Gas limit & usage by txn" title="Gas limit & usage by txn"
hint="Actual gas amount used by the transaction." hint="Actual gas amount used by the transaction."
> >
<Text>{ utils.commify(data.gas_used) }</Text> <Text>{ BigNumber(data.gas_used).toFormat() }</Text>
<TextSeparator/> <TextSeparator/>
<Text >{ utils.commify(data.gas_limit) }</Text> <Text >{ BigNumber(data.gas_limit).toFormat() }</Text>
<Utilization ml={ 4 } value={ utils.parseUnits(data.gas_used).mul(10_000).div(utils.parseUnits(data.gas_limit)).toNumber() / 10_000 }/> <Utilization ml={ 4 } value={ BigNumber(data.gas_used).dividedBy(BigNumber(data.gas_limit)).toNumber() }/>
</DetailsInfoItem> </DetailsInfoItem>
{ (data.base_fee_per_gas || data.max_fee_per_gas || data.max_priority_fee_per_gas) && ( { (data.base_fee_per_gas || data.max_fee_per_gas || data.max_priority_fee_per_gas) && (
<DetailsInfoItem <DetailsInfoItem
...@@ -187,21 +188,21 @@ const TxDetails = () => { ...@@ -187,21 +188,21 @@ const TxDetails = () => {
{ data.base_fee_per_gas && ( { data.base_fee_per_gas && (
<Box> <Box>
<Text as="span" fontWeight="500">Base: </Text> <Text as="span" fontWeight="500">Base: </Text>
<Text fontWeight="600" as="span">{ utils.formatUnits(utils.parseUnits(String(data.base_fee_per_gas), 'wei'), 'gwei') }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.base_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Box> </Box>
) } ) }
{ data.max_fee_per_gas && ( { data.max_fee_per_gas && (
<Box> <Box>
<TextSeparator/> <TextSeparator/>
<Text as="span" fontWeight="500">Max: </Text> <Text as="span" fontWeight="500">Max: </Text>
<Text fontWeight="600" as="span">{ utils.formatUnits(utils.parseUnits(String(data.max_fee_per_gas), 'wei'), 'gwei') }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.max_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Box> </Box>
) } ) }
{ data.max_priority_fee_per_gas && ( { data.max_priority_fee_per_gas && (
<Box> <Box>
<TextSeparator/> <TextSeparator/>
<Text as="span" fontWeight="500">Max priority: </Text> <Text as="span" fontWeight="500">Max priority: </Text>
<Text fontWeight="600" as="span">{ utils.formatUnits(utils.parseUnits(String(data.max_priority_fee_per_gas), 'wei'), 'gwei') }</Text> <Text fontWeight="600" as="span">{ BigNumber(data.max_priority_fee_per_gas).dividedBy(WEI_IN_GWEI).toFixed() }</Text>
</Box> </Box>
) } ) }
</DetailsInfoItem> </DetailsInfoItem>
......
...@@ -2460,6 +2460,11 @@ big.js@^3.1.3: ...@@ -2460,6 +2460,11 @@ big.js@^3.1.3:
resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==
bignumber.js@^9.1.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62"
integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==
bn.js@^4.11.9: bn.js@^4.11.9:
version "4.12.0" version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
......
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