mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
The fee-destination gate landed head-anchored: useFeeDestination() read
reading.status, which deriveStatus() computes against the HEAD block's
timestamp, and every per-block surface then rendered that one verdict.
Head status answers "is the split active NOW". A block page asks "was THIS
block's fee burned". They agree only while the split has never been active —
which is why this is invisible today: mainnet 96369 has no feeSplitTimestamp
at all (eth_getChainConfig answers, chainId 96369 present, and feeSplit
appears nowhere in it including upgrades/precompileUpgrades/stateUpgrades),
so every block correctly reads "Fees to coinbase".
The day feeSplitTimestamp is set and passes, all ~1.1M pre-activation blocks
would be relabelled "Burnt fees" with a burn ratio — the same false deflation
claim this module exists to remove, moved into history, and this time about
blocks whose fees demonstrably went to the coinbase 0x0100..0000 (3868.48 LUX
and climbing, measured at head 1098308).
So the destination is derived per block:
feeDestinationAt(reading, blockTime) pure; re-derives the gate against the
block's own time via deriveStatus
useFeeDestination(blockTime?) one-line wrapper over it
blockTimeSeconds(apiTimestamp) ISO -> seconds, undefined when absent
Passed at the four surfaces that name one block (block details, blocks table
row, blocks list row, tx details). BlocksTable keeps the head reading with a
comment saying why: a column header spans many blocks, so it cannot be a
per-block claim, and the per-row label and ratio are each gated individually.
A missing timestamp falls back to the head reading, so a loading row keeps
today's behaviour rather than flashing "destination unknown".
Decomplected on purpose: the decision is a pure function of (reading,
blockTime), so it is tested without a React tree. Reverting the body to
deriveFeeDestination(reading.status) fails "does NOT report a pre-activation
block as burned" — the regression is pinned, not asserted.
32/32 vitest green (5 new cases); tsc reports 0 errors in all 7 touched files
and eslint is clean on them (control: 232 pre-existing tsc errors, none in
these files).
Co-authored-by: Hanzo Dev <[email protected]>
154 lines
5.7 KiB
TypeScript
154 lines
5.7 KiB
TypeScript
import { Skeleton } from '@luxfi/ui/skeleton';
|
|
import { TableCell, TableRow } from '@luxfi/ui/table';
|
|
import { Tooltip } from '@luxfi/ui/tooltip';
|
|
import BigNumber from 'bignumber.js';
|
|
import React from 'react';
|
|
|
|
import type { Block } from 'types/api/block';
|
|
import type { ClusterChainConfig } from 'types/multichain';
|
|
|
|
import { route } from 'nextjs-routes';
|
|
|
|
import config from 'configs/app';
|
|
import { blockTimeSeconds, feeDestinationCopy, useFeeDestination } from 'lib/api/cchain/useFeeSplit';
|
|
import getBlockTotalReward from 'lib/block/getBlockTotalReward';
|
|
import { Link } from 'toolkit/next/link';
|
|
import BlockGasUsed from 'ui/shared/block/BlockGasUsed';
|
|
import BlockPendingUpdateHint from 'ui/shared/block/BlockPendingUpdateHint';
|
|
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
|
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
|
|
import ChainIcon from 'ui/shared/externalChains/ChainIcon';
|
|
import IconSvg from 'ui/shared/IconSvg';
|
|
import TimeWithTooltip from 'ui/shared/time/TimeWithTooltip';
|
|
import Utilization from 'ui/shared/Utilization/Utilization';
|
|
import NativeCoinValue from 'ui/shared/value/NativeCoinValue';
|
|
import SimpleValue from 'ui/shared/value/SimpleValue';
|
|
|
|
interface Props {
|
|
data: Block;
|
|
isLoading?: boolean;
|
|
animation?: string;
|
|
enableTimeIncrement?: boolean;
|
|
chainData?: ClusterChainConfig;
|
|
}
|
|
|
|
const isRollup = config.features.rollup.isEnabled;
|
|
|
|
const BlocksTableItem = ({ data, isLoading, enableTimeIncrement, animation, chainData }: Props) => {
|
|
// A live read gated on THIS block's timestamp, not an assumption: base fees are
|
|
// only destroyed while the fee split is active. See lib/api/cchain/useFeeSplit.
|
|
const feeCopy = feeDestinationCopy(useFeeDestination(blockTimeSeconds(data.timestamp)));
|
|
const totalReward = getBlockTotalReward(data);
|
|
const burntFees = BigNumber(data.burnt_fees || 0);
|
|
const txFees = BigNumber(data.transaction_fees || 0);
|
|
|
|
return (
|
|
<TableRow animation={ animation }>
|
|
{ chainData && (
|
|
<TableCell>
|
|
<ChainIcon data={ chainData } isLoading={ isLoading }/>
|
|
</TableCell>
|
|
) }
|
|
<TableCell >
|
|
<div className="flex gap-x-2 items-center mb-2">
|
|
{ data.celo?.l1_era_finalized_epoch_number && (
|
|
<Tooltip content={ `Finalized epoch #${ data.celo.l1_era_finalized_epoch_number }` }>
|
|
<IconSvg name="checkered_flag" className="w-5 h-5 p-px shrink-0" isLoading={ isLoading }/>
|
|
</Tooltip>
|
|
) }
|
|
{ data.is_pending_update && <BlockPendingUpdateHint/> }
|
|
<Tooltip disabled={ data.type !== 'reorg' } content="Chain reorganizations">
|
|
<span>
|
|
<BlockEntity
|
|
isLoading={ isLoading }
|
|
number={ data.height }
|
|
hash={ data.type !== 'block' ? data.hash : undefined }
|
|
noIcon
|
|
className="font-semibold"
|
|
/>
|
|
</span>
|
|
</Tooltip>
|
|
</div>
|
|
<TimeWithTooltip
|
|
timestamp={ data.timestamp }
|
|
enableIncrement={ enableTimeIncrement }
|
|
isLoading={ isLoading }
|
|
color="text.secondary"
|
|
fontWeight="400"
|
|
display="inline-block"
|
|
/>
|
|
</TableCell>
|
|
<TableCell >
|
|
<Skeleton loading={ isLoading } display="inline-block">
|
|
{ data.size?.toLocaleString() || 'N/A' }
|
|
</Skeleton>
|
|
</TableCell>
|
|
{ !config.UI.views.block.hiddenFields?.miner && (
|
|
<TableCell >
|
|
<AddressEntity
|
|
address={ data.miner }
|
|
isLoading={ isLoading }
|
|
truncation="constant"
|
|
/>
|
|
</TableCell>
|
|
) }
|
|
<TableCell isNumeric >
|
|
{ data.transactions_count > 0 ? (
|
|
<Skeleton loading={ isLoading } display="inline-block">
|
|
<Link href={ route({
|
|
pathname: '/block/[height_or_hash]',
|
|
query: { height_or_hash: String(data.height), tab: 'txs' },
|
|
}) }>
|
|
{ data.transactions_count }
|
|
</Link>
|
|
</Skeleton>
|
|
) : data.transactions_count }
|
|
</TableCell>
|
|
<TableCell >
|
|
<Skeleton loading={ isLoading } display="inline-block">{ BigNumber(data.gas_used || 0).toFormat() }</Skeleton>
|
|
<div className="mt-2">
|
|
<BlockGasUsed
|
|
gasUsed={ data.gas_used || undefined }
|
|
gasLimit={ data.gas_limit }
|
|
isLoading={ isLoading }
|
|
gasTarget={ data.gas_target_percentage || undefined }
|
|
/>
|
|
</div>
|
|
</TableCell>
|
|
{ !isRollup && !config.UI.views.block.hiddenFields?.total_reward && (
|
|
<TableCell >
|
|
<SimpleValue value={ totalReward } loading={ isLoading }/>
|
|
</TableCell>
|
|
) }
|
|
{ !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees && (
|
|
<TableCell >
|
|
<NativeCoinValue
|
|
amount={ data.burnt_fees }
|
|
noSymbol
|
|
startElement={ <IconSvg name="flame" className="mr-2 w-5 h-5" isLoading={ isLoading }/> }
|
|
loading={ isLoading }
|
|
display="flex"
|
|
/>
|
|
{ feeCopy.showBurnRatio && (
|
|
<Tooltip content="Burnt fees / Txn fees * 100%" disabled={ isLoading }>
|
|
<Utilization className="mt-2 w-min" value={ burntFees.div(txFees).toNumber() } isLoading={ isLoading }/>
|
|
</Tooltip>
|
|
) }
|
|
</TableCell>
|
|
) }
|
|
{ !isRollup && !config.UI.views.block.hiddenFields?.base_fee && data.base_fee_per_gas && (
|
|
<TableCell isNumeric>
|
|
<NativeCoinValue
|
|
amount={ data.base_fee_per_gas }
|
|
loading={ isLoading }
|
|
gweiThreshold={ 4 }
|
|
units="wei"
|
|
/>
|
|
</TableCell>
|
|
) }
|
|
</TableRow>
|
|
);
|
|
};
|
|
|
|
export default React.memo(BlocksTableItem);
|