customize fees section on tx page and add new option for network validation type
@@ -36,6 +36,7 @@ const additionalFields = (() => {
|
||||
const config = Object.freeze({
|
||||
hiddenFields,
|
||||
additionalFields,
|
||||
groupedFees: getEnvValue('NEXT_PUBLIC_VIEWS_TX_GROUPED_FEES') === 'true',
|
||||
});
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -787,7 +787,7 @@ const schema = yup
|
||||
NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL: yup.string(),
|
||||
NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES: yup.boolean(),
|
||||
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: yup
|
||||
.string<NetworkVerificationTypeEnvs>().oneOf([ 'validation', 'mining' ])
|
||||
.string<NetworkVerificationTypeEnvs>().oneOf([ 'validation', 'mining', 'fee reception' ])
|
||||
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
|
||||
is: (value: string) => value === 'arbitrum' || value === 'zkEvm',
|
||||
then: (schema) => schema.test(
|
||||
@@ -982,6 +982,7 @@ const schema = yup
|
||||
.transform(replaceQuotes)
|
||||
.json()
|
||||
.of(yup.string<TxAdditionalFieldsId>().oneOf(TX_ADDITIONAL_FIELDS_IDS)),
|
||||
NEXT_PUBLIC_VIEWS_TX_GROUPED_FEES: yup.boolean(),
|
||||
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES: yup
|
||||
.array()
|
||||
.transform(replaceQuotes)
|
||||
|
||||
@@ -73,7 +73,7 @@ NEXT_PUBLIC_VIEWS_CONTRACT_LANGUAGE_FILTERS=['solidity','vyper','yul','scilla','
|
||||
NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=['burnt_fees','total_reward']
|
||||
NEXT_PUBLIC_VIEWS_BLOCK_PENDING_UPDATE_ALERT_ENABLED=false
|
||||
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'NFT Marketplace','collection_url':'https://example.com/{hash}','instance_url':'https://example.com/{hash}/{id}','logo_url':'https://example.com/logo.png'}]
|
||||
NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS=['fee_per_gas']
|
||||
NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS=['fee_per_gas','set_max_gas_limit']
|
||||
NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS=['value','fee_currency','gas_price','tx_fee','gas_fees','burnt_fees']
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST=https://example.com
|
||||
NEXT_PUBLIC_VISUALIZE_API_BASE_PATH=https://example.com
|
||||
|
||||
@@ -113,7 +113,7 @@ All json-like values should be single-quoted. If it contains a hash (`#`) or a d
|
||||
| NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS | `string` | Network currency decimals | - | `18` | `6` | v1.0.x+ |
|
||||
| NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL | `string` | Network secondary coin symbol. | - | - | `GNO` | v1.29.0+ |
|
||||
| NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES | `boolean` | Set to `true` for networks where users can pay transaction fees in either the native coin or ERC-20 tokens. | - | `false` | `true` | v1.33.0+ |
|
||||
| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` \| `mining` | Verification type in the network. Irrelevant for Arbitrum (verification type is always `posting`) and ZkEvm (verification type is always `sequencing`) L2s | - | `mining` | `validation` | v1.0.x+ |
|
||||
| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` \| `mining` \| 'fee reception' | Verification type in the network. Irrelevant for Arbitrum (verification type is always `posting`) and ZkEvm (verification type is always `sequencing`) L2s | - | `mining` | `validation` | v1.0.x+ |
|
||||
| NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME | `string` | Name of the standard for creating tokens | - | `ERC` | `BEP` | v1.31.0+ |
|
||||
| NEXT_PUBLIC_IS_TESTNET | `boolean`| Set to true if network is testnet | - | `false` | `true` | v1.0.x+ |
|
||||
|
||||
@@ -297,6 +297,7 @@ Settings for meta tags, OG tags and SEO
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS | `Array<TxFieldsId>` | Array of the transaction fields ids that should be hidden. See below the list of the possible id values. | - | - | `'["value","tx_fee"]'` | v1.15.0+ |
|
||||
| NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS | `Array<TxAdditionalFieldsId>` | Array of the additional fields ids that should be added to the transaction details. See below the list of the possible id values. | - | - | `'["fee_per_gas"]'` | v1.15.0+ |
|
||||
| NEXT_PUBLIC_VIEWS_TX_GROUPED_FEES | `boolean` | In the detailed view group transaction fees under one collapsible section. | - | - | `true` | v2.3.5+ |
|
||||
|
||||
##### Transaction fields list
|
||||
| Id | Description |
|
||||
@@ -314,6 +315,7 @@ Settings for meta tags, OG tags and SEO
|
||||
| Id | Description |
|
||||
| --- | --- |
|
||||
| `fee_per_gas` | Amount of total fee divided by total amount of gas used by transaction |
|
||||
| `set_max_gas_limit` | Max gas price established by the sender |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -28,5 +28,5 @@ export default function getCurrencyValue({ value, accuracy, accuracyUsd, decimal
|
||||
}
|
||||
}
|
||||
|
||||
return { valueStr: valueResult, usd: usdResult, usdBn };
|
||||
return { valueCurr, valueStr: valueResult, usd: usdResult, usdBn };
|
||||
}
|
||||
|
||||
@@ -14,8 +14,11 @@ export default function getNetworkValidationActionText(chainConfig = config) {
|
||||
case 'sequencing': {
|
||||
return 'sequenced';
|
||||
}
|
||||
case 'fee reception': {
|
||||
return 'validated';
|
||||
}
|
||||
default: {
|
||||
return 'miner';
|
||||
return 'mined';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ export default function getNetworkValidatorTitle() {
|
||||
case 'sequencing': {
|
||||
return 'sequencer';
|
||||
}
|
||||
case 'fee reception': {
|
||||
return 'fee recipient';
|
||||
}
|
||||
default: {
|
||||
return 'miner';
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useUpdateEffect } from '../hooks/useUpdateEffect';
|
||||
import type { LinkProps } from './link';
|
||||
import { Link } from './link';
|
||||
|
||||
interface CollapsibleDetailsProps extends LinkProps {
|
||||
export interface CollapsibleDetailsProps extends LinkProps {
|
||||
children: React.ReactNode;
|
||||
id?: string;
|
||||
isExpanded?: boolean;
|
||||
|
||||
@@ -25,6 +25,6 @@ export interface NetworkExplorer {
|
||||
};
|
||||
}
|
||||
|
||||
export type NetworkVerificationTypeEnvs = 'mining' | 'validation';
|
||||
export type NetworkVerificationTypeEnvs = 'mining' | 'validation' | 'fee reception';
|
||||
export type NetworkVerificationTypeComputed = 'posting' | 'sequencing';
|
||||
export type NetworkVerificationType = NetworkVerificationTypeEnvs | NetworkVerificationTypeComputed;
|
||||
|
||||
@@ -15,6 +15,7 @@ export type TxFieldsId = ArrayElement<typeof TX_FIELDS_IDS>;
|
||||
|
||||
export const TX_ADDITIONAL_FIELDS_IDS = [
|
||||
'fee_per_gas',
|
||||
'set_max_gas_limit',
|
||||
] as const;
|
||||
|
||||
export type TxAdditionalFieldsId = ArrayElement<typeof TX_ADDITIONAL_FIELDS_IDS>;
|
||||
|
||||
@@ -11,7 +11,6 @@ import { route, routeParams } from 'nextjs/routes';
|
||||
import config from 'configs/app';
|
||||
import getBlockReward from 'lib/block/getBlockReward';
|
||||
import { useMultichainContext } from 'lib/contexts/multichain';
|
||||
import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText';
|
||||
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
|
||||
import * as arbitrum from 'lib/rollups/arbitrum';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
@@ -111,8 +110,6 @@ const BlockDetails = ({ query }: Props) => {
|
||||
);
|
||||
})();
|
||||
|
||||
const verificationTitle = `${ capitalize(getNetworkValidationActionText()) } by`;
|
||||
|
||||
const txsNum = (() => {
|
||||
const blockTxsNum = (
|
||||
<Link href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: heightOrHash, tab: 'txs' } }, multichainContext) }>
|
||||
@@ -326,7 +323,7 @@ const BlockDetails = ({ query }: Props) => {
|
||||
hint="A block producer who successfully included the block onto the blockchain"
|
||||
isLoading={ isPlaceholderData }
|
||||
>
|
||||
{ verificationTitle }
|
||||
{ capitalize(validatorTitle) }
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<AddressEntity
|
||||
|
||||
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 107 KiB |
@@ -1,4 +1,5 @@
|
||||
import { Box, Flex, Grid } from '@chakra-ui/react';
|
||||
import { capitalize } from 'es-toolkit';
|
||||
import React from 'react';
|
||||
|
||||
import type { Block } from 'types/api/block';
|
||||
@@ -73,7 +74,7 @@ const LatestBlocksItem = ({ block, isLoading, animation }: Props) => {
|
||||
|
||||
{ !config.features.rollup.isEnabled && !config.UI.views.block.hiddenFields?.miner && (
|
||||
<>
|
||||
<Skeleton loading={ isLoading } textTransform="capitalize">{ getNetworkValidatorTitle() }</Skeleton>
|
||||
<Skeleton loading={ isLoading }>{ capitalize(getNetworkValidatorTitle()) }</Skeleton>
|
||||
<AddressEntity
|
||||
address={ block.miner }
|
||||
isLoading={ isLoading }
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useMultichainContext } from 'lib/contexts/multichain';
|
||||
import throwOnAbsentParamError from 'lib/errors/throwOnAbsentParamError';
|
||||
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import getNetworkValidationActionText from 'lib/networks/getNetworkValidationActionText';
|
||||
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import RoutedTabs from 'toolkit/components/RoutedTabs/RoutedTabs';
|
||||
@@ -186,7 +186,7 @@ const BlockPageContent = () => {
|
||||
fontWeight={ 500 }
|
||||
>
|
||||
<chakra.span flexShrink={ 0 }>
|
||||
{ `${ capitalize(getNetworkValidationActionText()) } by` }
|
||||
{ capitalize(getNetworkValidatorTitle()) }
|
||||
</chakra.span>
|
||||
<AddressEntity address={ blockQuery.data.miner }/>
|
||||
</Skeleton>
|
||||
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@@ -2,7 +2,10 @@ import { chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import getCurrencyValue from 'lib/getCurrencyValue';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import { Tooltip } from 'toolkit/chakra/tooltip';
|
||||
import { GWEI } from 'toolkit/utils/consts';
|
||||
|
||||
interface Props {
|
||||
value: string | null;
|
||||
@@ -14,9 +17,10 @@ interface Props {
|
||||
decimals?: string | null;
|
||||
isLoading?: boolean;
|
||||
startElement?: React.ReactNode;
|
||||
showGweiTooltip?: boolean;
|
||||
}
|
||||
|
||||
const CurrencyValue = ({ value, currency = '', decimals, exchangeRate, className, accuracy, accuracyUsd, isLoading, startElement }: Props) => {
|
||||
const CurrencyValue = ({ value, currency = '', decimals, exchangeRate, className, accuracy, accuracyUsd, isLoading, startElement, showGweiTooltip }: Props) => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Skeleton className={ className } loading display="inline-block">0.00 ($0.00)</Skeleton>
|
||||
@@ -30,14 +34,23 @@ const CurrencyValue = ({ value, currency = '', decimals, exchangeRate, className
|
||||
</chakra.span>
|
||||
);
|
||||
}
|
||||
const { valueStr: valueResult, usd: usdResult } = getCurrencyValue({ value, accuracy, accuracyUsd, exchangeRate, decimals });
|
||||
const { valueCurr, valueStr: valueResult, usd: usdResult } = getCurrencyValue({ value, accuracy, accuracyUsd, exchangeRate, decimals });
|
||||
|
||||
const valueElement = (
|
||||
<chakra.span display="inline-block">
|
||||
{ valueResult }{ currency ? ` ${ currency }` : '' }
|
||||
</chakra.span>
|
||||
);
|
||||
const valueInGwei = showGweiTooltip ? valueCurr.multipliedBy(GWEI).toFormat() : null;
|
||||
|
||||
return (
|
||||
<chakra.span className={ className } display="inline-flex" rowGap={ 3 } columnGap={ 1 }>
|
||||
{ startElement }
|
||||
<chakra.span display="inline-block">
|
||||
{ valueResult }{ currency ? ` ${ currency }` : '' }
|
||||
</chakra.span>
|
||||
{ showGweiTooltip ? (
|
||||
<Tooltip content={ `${ valueInGwei } ${ currencyUnits.gwei }` }>
|
||||
{ valueElement }
|
||||
</Tooltip>
|
||||
) : valueElement }
|
||||
{ usdResult && <chakra.span color="text.secondary" fontWeight={ 400 }>(${ usdResult })</chakra.span> }
|
||||
</chakra.span>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Grid, GridItem } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { CollapsibleDetailsProps } from 'toolkit/chakra/collapsible';
|
||||
import { CollapsibleDetails } from 'toolkit/chakra/collapsible';
|
||||
import { Hint } from 'toolkit/components/Hint/Hint';
|
||||
import TruncatedValue from 'ui/shared/TruncatedValue';
|
||||
|
||||
interface ContainerProps extends CollapsibleDetailsProps {}
|
||||
|
||||
export const Container = ({ children, ...rest }: ContainerProps) => {
|
||||
return (
|
||||
<CollapsibleDetails noScroll variant="secondary" display="block" textStyle={ undefined } fontSize="sm" { ...rest }>
|
||||
<Grid
|
||||
gridTemplateColumns="max-content minmax(0px, 1fr)"
|
||||
alignItems="start"
|
||||
textStyle="sm"
|
||||
bgColor={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }}
|
||||
w="100%"
|
||||
p={{ base: 2, lg: 3 }}
|
||||
mt={ 1 }
|
||||
columnGap={ 3 }
|
||||
rowGap={ 4 }
|
||||
borderBottomRightRadius="base"
|
||||
borderBottomLeftRadius="base"
|
||||
>
|
||||
{ children }
|
||||
</Grid>
|
||||
</CollapsibleDetails>
|
||||
);
|
||||
};
|
||||
|
||||
interface RowProps {
|
||||
label: string;
|
||||
hint: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Row = ({ label, hint, children }: RowProps) => {
|
||||
return (
|
||||
<>
|
||||
<GridItem color="text.secondary" display="flex" alignItems="center">
|
||||
<Hint label={ hint } boxSize={ 4 } mr={ 1 }/>
|
||||
<TruncatedValue value={ label } maxW={{ base: '130px', lg: 'unset' }}/>
|
||||
</GridItem>
|
||||
{ children }
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
|
||||
import type { Transaction } from 'types/api/transaction';
|
||||
|
||||
import { Skeleton } from 'toolkit/chakra/skeleton';
|
||||
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
|
||||
import TextSeparator from 'ui/shared/TextSeparator';
|
||||
import Utilization from 'ui/shared/Utilization/Utilization';
|
||||
|
||||
interface Props {
|
||||
isLoading?: boolean;
|
||||
data: Transaction;
|
||||
}
|
||||
|
||||
const TxDetailsGasUsage = ({ isLoading, data }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<DetailedInfo.ItemLabel
|
||||
hint="Actual gas amount used by the transaction"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Gas usage & limit by txn
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ isLoading }>{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
|
||||
<TextSeparator/>
|
||||
<Skeleton loading={ isLoading }>{ BigNumber(data.gas_limit).toFormat() }</Skeleton>
|
||||
<Utilization ml={ 4 } value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() } isLoading={ isLoading }/>
|
||||
</DetailedInfo.ItemValue>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(TxDetailsGasUsage);
|
||||
@@ -0,0 +1,47 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
|
||||
import type { Transaction } from 'types/api/transaction';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
|
||||
|
||||
import TxDetailsGasUsage from './TxDetailsGasUsage';
|
||||
|
||||
interface Props {
|
||||
data: Transaction;
|
||||
}
|
||||
|
||||
const TxDetailsSetMaxGasLimit = ({ data }: Props) => {
|
||||
if (!config.UI.views.tx.additionalFields?.set_max_gas_limit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const maxGasLimit = BigNumber(data.gas_limit || 0).multipliedBy(BigNumber(data.gas_price || 0));
|
||||
|
||||
return (
|
||||
<>
|
||||
<DetailedInfo.ItemLabel
|
||||
hint="The maximum network fee the sender is willing to pay for this transaction"
|
||||
>
|
||||
User’s set max gas limit
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue multiRow>
|
||||
<CurrencyValue
|
||||
value={ maxGasLimit.toString() }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ 'exchange_rate' in data ? data.exchange_rate : null }
|
||||
showGweiTooltip
|
||||
flexWrap="wrap"
|
||||
mr={ 3 }
|
||||
rowGap={ 0 }
|
||||
/>
|
||||
</DetailedInfo.ItemValue>
|
||||
<TxDetailsGasUsage data={ data }/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(TxDetailsSetMaxGasLimit);
|
||||
@@ -0,0 +1,101 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
|
||||
import type { Transaction } from 'types/api/transaction';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import * as DetailedInfo from 'ui/shared/DetailedInfo/DetailedInfo';
|
||||
import * as DetailedInfoItemBreakdown from 'ui/shared/DetailedInfo/DetailedInfoItemBreakdown';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean;
|
||||
data: Transaction;
|
||||
}
|
||||
|
||||
const TxDetailsTxFee = ({ isLoading, data }: Props) => {
|
||||
|
||||
if (config.UI.views.tx.hiddenFields?.tx_fee) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = (() => {
|
||||
if (!config.UI.views.tx.groupedFees) {
|
||||
return (
|
||||
<TxFee
|
||||
tx={ data }
|
||||
isLoading={ isLoading }
|
||||
withUsd
|
||||
rowGap={ 0 }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const baseFeeBn = BigNumber(data.base_fee_per_gas || 0).multipliedBy(data.gas_used || 0);
|
||||
const priorityFeeBn = BigNumber(data.priority_fee || 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CurrencyValue
|
||||
value={ data.fee.value }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ 'exchange_rate' in data ? data.exchange_rate : null }
|
||||
isLoading={ isLoading }
|
||||
showGweiTooltip
|
||||
flexWrap="wrap"
|
||||
mr={ 3 }
|
||||
rowGap={ 0 }
|
||||
/>
|
||||
<DetailedInfoItemBreakdown.Container loading={ isLoading }>
|
||||
<DetailedInfoItemBreakdown.Row
|
||||
label="Base fee"
|
||||
hint="The minimum network fee charged per transaction"
|
||||
>
|
||||
<CurrencyValue
|
||||
value={ baseFeeBn.toString() }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ 'exchange_rate' in data ? data.exchange_rate : null }
|
||||
isLoading={ isLoading }
|
||||
showGweiTooltip
|
||||
flexWrap="wrap"
|
||||
rowGap={ 0 }
|
||||
/>
|
||||
</DetailedInfoItemBreakdown.Row>
|
||||
<DetailedInfoItemBreakdown.Row
|
||||
label="Priority fee"
|
||||
hint="An extra fee set by the sender to speed up transaction execution"
|
||||
>
|
||||
<CurrencyValue
|
||||
value={ priorityFeeBn.toString() }
|
||||
currency={ currencyUnits.ether }
|
||||
exchangeRate={ 'exchange_rate' in data ? data.exchange_rate : null }
|
||||
isLoading={ isLoading }
|
||||
showGweiTooltip
|
||||
flexWrap="wrap"
|
||||
rowGap={ 0 }
|
||||
/>
|
||||
</DetailedInfoItemBreakdown.Row>
|
||||
</DetailedInfoItemBreakdown.Container>
|
||||
|
||||
</>
|
||||
);
|
||||
})();
|
||||
|
||||
return (
|
||||
<>
|
||||
<DetailedInfo.ItemLabel
|
||||
hint={ data.blob_gas_used ? 'Transaction fee without blob fee' : 'Total transaction fee' }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Transaction fee
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue multiRow>
|
||||
{ content }
|
||||
</DetailedInfo.ItemValue>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(TxDetailsTxFee);
|
||||
@@ -121,6 +121,23 @@ test('stability customization', async({ render, page, mockEnvs, mockAssetRespons
|
||||
});
|
||||
});
|
||||
|
||||
test('with grouped fees', async({ render, page, mockEnvs }) => {
|
||||
await mockEnvs([
|
||||
[ 'NEXT_PUBLIC_VIEWS_TX_GROUPED_FEES', 'true' ],
|
||||
[ 'NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS', '["gas_price","gas_fees","burnt_fees"]' ],
|
||||
[ 'NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS', '["set_max_gas_limit"]' ],
|
||||
]);
|
||||
const component = await render(<TxInfo data={ txMock.base } isLoading={ false }/>);
|
||||
|
||||
await component.getByText('View details').first().click();
|
||||
await component.getByText('View details').first().click();
|
||||
|
||||
await expect(component).toHaveScreenshot({
|
||||
mask: [ page.locator(pwConfig.adsBannerSelector) ],
|
||||
maskColor: pwConfig.maskColor,
|
||||
});
|
||||
});
|
||||
|
||||
test('arbitrum L1 status', async({ render, mockEnvs }) => {
|
||||
await mockEnvs(ENVS_MAP.arbitrumRollup);
|
||||
const component = await render(<TxInfo data={ txMock.arbitrumTxn } isLoading={ false }/>);
|
||||
|
||||
@@ -48,7 +48,6 @@ import RawInputData from 'ui/shared/RawInputData';
|
||||
import StatusTag from 'ui/shared/statusTag/StatusTag';
|
||||
import TxStatus from 'ui/shared/statusTag/TxStatus';
|
||||
import TextSeparator from 'ui/shared/TextSeparator';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
import Utilization from 'ui/shared/Utilization/Utilization';
|
||||
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
|
||||
import TxDetailsActions from 'ui/tx/details/txDetailsActions/TxDetailsActions';
|
||||
@@ -64,8 +63,11 @@ import TxExternalTxs from 'ui/tx/TxExternalTxs';
|
||||
import TxSocketAlert from 'ui/tx/TxSocketAlert';
|
||||
import ZkSyncL2TxnBatchHashesInfo from 'ui/txnBatches/zkSyncL2/ZkSyncL2TxnBatchHashesInfo';
|
||||
|
||||
import TxDetailsGasUsage from './TxDetailsGasUsage';
|
||||
import TxDetailsInterop from './TxDetailsInterop';
|
||||
import TxDetailsSetMaxGasLimit from './TxDetailsSetMaxGasLimit';
|
||||
import TxDetailsTacOperation from './TxDetailsTacOperation';
|
||||
import TxDetailsTxFee from './TxDetailsTxFee';
|
||||
import TxDetailsWithdrawalStatusArbitrum from './TxDetailsWithdrawalStatusArbitrum';
|
||||
import TxInfoScrollFees from './TxInfoScrollFees';
|
||||
|
||||
@@ -137,7 +139,7 @@ const TxInfo = ({ data, tacOperations, isLoading, socketStatus }: Props) => {
|
||||
const hasInterop = rollupFeature.isEnabled && rollupFeature.interopEnabled && data.op_interop_messages && data.op_interop_messages.length > 0;
|
||||
|
||||
return (
|
||||
<DetailedInfo.Container>
|
||||
<DetailedInfo.Container templateColumns={{ base: 'minmax(0, 1fr)', lg: 'minmax(215px, auto) minmax(0, 1fr)' }}>
|
||||
|
||||
{ config.features.metasuites.isEnabled && (
|
||||
<>
|
||||
@@ -623,24 +625,7 @@ const TxInfo = ({ data, tacOperations, isLoading, socketStatus }: Props) => {
|
||||
</>
|
||||
) }
|
||||
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<>
|
||||
<DetailedInfo.ItemLabel
|
||||
hint={ data.blob_gas_used ? 'Transaction fee without blob fee' : 'Total transaction fee' }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Transaction fee
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue multiRow>
|
||||
<TxFee
|
||||
tx={ data }
|
||||
isLoading={ isLoading }
|
||||
withUsd
|
||||
rowGap={ 0 }
|
||||
/>
|
||||
</DetailedInfo.ItemValue>
|
||||
</>
|
||||
) }
|
||||
<TxDetailsTxFee isLoading={ isLoading } data={ data }/>
|
||||
|
||||
{ rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && data.arbitrum && (
|
||||
<>
|
||||
@@ -682,18 +667,7 @@ const TxInfo = ({ data, tacOperations, isLoading, socketStatus }: Props) => {
|
||||
|
||||
<TxDetailsFeePerGas txFee={ data.fee.value } gasUsed={ data.gas_used } isLoading={ isLoading }/>
|
||||
|
||||
<DetailedInfo.ItemLabel
|
||||
hint="Actual gas amount used by the transaction"
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
Gas usage & limit by txn
|
||||
</DetailedInfo.ItemLabel>
|
||||
<DetailedInfo.ItemValue>
|
||||
<Skeleton loading={ isLoading }>{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
|
||||
<TextSeparator/>
|
||||
<Skeleton loading={ isLoading }>{ BigNumber(data.gas_limit).toFormat() }</Skeleton>
|
||||
<Utilization ml={ 4 } value={ BigNumber(data.gas_used || 0).dividedBy(BigNumber(data.gas_limit)).toNumber() } isLoading={ isLoading }/>
|
||||
</DetailedInfo.ItemValue>
|
||||
{ !config.UI.views.tx.additionalFields?.set_max_gas_limit && <TxDetailsGasUsage isLoading={ isLoading } data={ data }/> }
|
||||
|
||||
{ rollupFeature.isEnabled && rollupFeature.type === 'arbitrum' && data.arbitrum && data.gas_used && (
|
||||
<>
|
||||
@@ -859,6 +833,8 @@ const TxInfo = ({ data, tacOperations, isLoading, socketStatus }: Props) => {
|
||||
<CollapsibleDetails loading={ isLoading } mt={ 6 } gridColumn={{ base: undefined, lg: '1 / 3' }} isExpanded={ isExpanded } onClick={ handleCutLinkClick }>
|
||||
<GridItem colSpan={{ base: undefined, lg: 2 }} mt={{ base: 1, lg: 4 }}/>
|
||||
|
||||
<TxDetailsSetMaxGasLimit data={ data }/>
|
||||
|
||||
<TxDetailsWithdrawalStatusArbitrum data={ data }/>
|
||||
|
||||
{ (data.blob_gas_used || data.max_fee_per_blob_gas || data.blob_gas_price) && (
|
||||
|
||||
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |