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]>
106 lines
4.4 KiB
TypeScript
106 lines
4.4 KiB
TypeScript
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from '@luxfi/ui/table';
|
|
import { capitalize } from 'es-toolkit';
|
|
import React from 'react';
|
|
|
|
import type { Block } from 'types/api/block';
|
|
import type { ClusterChainConfig } from 'types/multichain';
|
|
|
|
import config from 'configs/app';
|
|
import { feeDestinationCopy, useFeeDestination } from 'lib/api/cchain/useFeeSplit';
|
|
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
|
|
import useInitialList from 'lib/hooks/useInitialList';
|
|
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
|
|
import { currencyUnits } from 'lib/units';
|
|
import BlocksTableItem from 'ui/blocks/BlocksTableItem';
|
|
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
|
|
import TimeFormatToggle from 'ui/shared/time/TimeFormatToggle';
|
|
|
|
interface Props {
|
|
data: Array<Block>;
|
|
isLoading?: boolean;
|
|
top: number;
|
|
page: number;
|
|
socketInfoNum?: number;
|
|
showSocketErrorAlert?: boolean;
|
|
showSocketInfo?: boolean;
|
|
chainData?: ClusterChainConfig;
|
|
}
|
|
|
|
const VALIDATOR_COL_WEIGHT = 23;
|
|
const GAS_COL_WEIGHT = 33;
|
|
const REWARD_COL_WEIGHT = 22;
|
|
const FEES_COL_WEIGHT = 22;
|
|
|
|
const isRollup = config.features.rollup.isEnabled;
|
|
|
|
const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum, showSocketErrorAlert, chainData }: Props) => {
|
|
// Column heading is a live read — see lib/api/cchain/useFeeSplit.
|
|
// No block time: this is a column header spanning many blocks, so it can only
|
|
// report the policy in force at the head. The per-row label and burn ratio are
|
|
// each gated on their own block's timestamp — see BlocksTableItem.
|
|
const feeCopy = feeDestinationCopy(useFeeDestination());
|
|
const initialList = useInitialList({
|
|
data: data ?? [],
|
|
idFn: (item) => item.height,
|
|
enabled: !isLoading,
|
|
});
|
|
|
|
const widthBase =
|
|
(!config.UI.views.block.hiddenFields?.miner ? VALIDATOR_COL_WEIGHT : 0) +
|
|
GAS_COL_WEIGHT +
|
|
(!isRollup && !config.UI.views.block.hiddenFields?.total_reward ? REWARD_COL_WEIGHT : 0) +
|
|
(!isRollup && !config.UI.views.block.hiddenFields?.burnt_fees ? FEES_COL_WEIGHT : 0);
|
|
|
|
return (
|
|
<AddressHighlightProvider>
|
|
<TableRoot minWidth={ chainData ? '1100px' : '1070px' } className="font-medium">
|
|
<TableHeaderSticky top={ top }>
|
|
<TableRow>
|
|
{ chainData && <TableColumnHeader width="38px"/> }
|
|
<TableColumnHeader width="180px">
|
|
Block
|
|
<TimeFormatToggle/>
|
|
</TableColumnHeader>
|
|
<TableColumnHeader width="120px">Size, bytes</TableColumnHeader>
|
|
{ !config.UI.views.block.hiddenFields?.miner && (
|
|
<TableColumnHeader width={ `${ VALIDATOR_COL_WEIGHT / widthBase * 100 }%` } minW="160px">
|
|
{ capitalize(getNetworkValidatorTitle()) }
|
|
</TableColumnHeader>
|
|
) }
|
|
<TableColumnHeader width="64px" isNumeric>Txn</TableColumnHeader>
|
|
<TableColumnHeader width={ `${ GAS_COL_WEIGHT / widthBase * 100 }%` }>Gas used</TableColumnHeader>
|
|
{ !isRollup && !config.UI.views.block.hiddenFields?.total_reward &&
|
|
<TableColumnHeader width={ `${ REWARD_COL_WEIGHT / widthBase * 100 }%` }>Reward { currencyUnits.ether }</TableColumnHeader> }
|
|
{ !isRollup && !config.UI.views.block.hiddenFields?.burnt_fees &&
|
|
<TableColumnHeader width={ `${ FEES_COL_WEIGHT / widthBase * 100 }%` }>{ feeCopy.label } { currencyUnits.ether }</TableColumnHeader> }
|
|
{ !isRollup && !config.UI.views.block.hiddenFields?.base_fee &&
|
|
<TableColumnHeader width="150px" isNumeric>Base fee</TableColumnHeader> }
|
|
</TableRow>
|
|
</TableHeaderSticky>
|
|
<TableBody>
|
|
{ showSocketInfo && (
|
|
<SocketNewItemsNotice.Desktop
|
|
showErrorAlert={ showSocketErrorAlert }
|
|
num={ socketInfoNum }
|
|
type="block"
|
|
isLoading={ isLoading }
|
|
/>
|
|
) }
|
|
{ data.map((item, index) => (
|
|
<BlocksTableItem
|
|
key={ item.height + (isLoading ? `${ index }_${ page }` : '') }
|
|
data={ item }
|
|
enableTimeIncrement={ page === 1 && !isLoading }
|
|
isLoading={ isLoading }
|
|
animation={ initialList.getAnimationProp(item) }
|
|
chainData={ chainData }
|
|
/>
|
|
)) }
|
|
</TableBody>
|
|
</TableRoot>
|
|
</AddressHighlightProvider>
|
|
);
|
|
};
|
|
|
|
export default BlocksTable;
|