Skip to content

Commit 1c971af

Browse files
Michael Borisovclaude
authored andcommitted
fix: mining dashboard clarity — drop misleading emission rates, add economy section
- Remove emission rate cards (mining/staking/gross) — these are internal PID controller values (atomic LI/s), not actual token emission rates - Add Economy section: supply (minted/cap), PoW/PoS share, fees, burned - Add total_minted query from core contract - Replace Alpha/Beta with PoW/PoS share (computed from emission ratios) - Replace D-rate with Net. throughput (bits/hr) - Add Your share (miner's network share %) - Add window progress bar with fill indicator - Fix horizontal scroll (overflow-x: hidden, min-width: 0) - Add P (peta) tier to compactLi for supply cap display - StatCard: add children prop for progress bar Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 171d26a commit 1c971af

File tree

5 files changed

+94
-55
lines changed

5 files changed

+94
-55
lines changed

src/pages/Mining/Mining.module.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
width: 100%;
55
gap: 20px;
66
min-height: 70vh;
7+
overflow-x: hidden;
78
}
89

910
// Header: wallet + status pill
@@ -88,6 +89,7 @@
8889
display: grid;
8990
grid-template-columns: repeat(4, 1fr);
9091
gap: 12px;
92+
min-width: 0;
9193

9294
@media (max-width: 600px) {
9395
grid-template-columns: repeat(2, 1fr);
@@ -103,6 +105,7 @@
103105
border: 1px solid rgba(54, 214, 174, 0.3);
104106
border-radius: 4px;
105107
text-align: center;
108+
min-width: 0;
106109
}
107110

108111
.statCardLabel {
@@ -875,3 +878,20 @@
875878
color: #ef4444;
876879
margin-top: 4px;
877880
}
881+
882+
// Window progress bar
883+
.windowProgress {
884+
width: 100%;
885+
height: 4px;
886+
background: rgba(54, 214, 174, 0.15);
887+
border-radius: 2px;
888+
overflow: hidden;
889+
margin-top: 4px;
890+
}
891+
892+
.windowProgressFill {
893+
height: 100%;
894+
background: #36d6ae;
895+
border-radius: 2px;
896+
transition: width 0.3s ease;
897+
}

src/pages/Mining/Mining.tsx

Lines changed: 68 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
WindowStatusResponse,
1717
ConfigResponse,
1818
} from 'src/generated/lithium/LitiumMine.types';
19+
import type { TotalMintedResponse } from 'src/generated/lithium/LitiumCore.types';
1920

2021
type ActivateAccountResponse = {
2122
ok?: boolean;
@@ -259,6 +260,11 @@ function Mining() {
259260
const { block: latestBlock, refetchBlock } = useLatestBlock();
260261
const { emission, refetch: refetchEmission } = useEmissionInfo();
261262
const { burnStats, refetch: refetchBurnStats } = useBurnStats();
263+
const { data: totalMintedData, refetch: refetchTotalMinted } = useQueryContract(
264+
LITIUM_CORE_CONTRACT,
265+
{ total_minted: {} }
266+
);
267+
const totalMinted = totalMintedData as TotalMintedResponse | undefined;
262268

263269
// Mining status from Redux (kept in sync by useMiningMonitor in App.tsx)
264270
const miningStatus = useAppSelector((s) => s.mining.status);
@@ -346,7 +352,7 @@ function Mining() {
346352
const samples = useHashrateSamples(hashrate, autoMining);
347353
const { uniqueMiners, totalProofs, avgDifficulty, refetch: refetchMinerStats } = useMinerStats();
348354
const {
349-
dRate, similarDevices, proofCount: windowProofCount, baseRate,
355+
dRate, similarDevices, proofCount: windowProofCount, windowSize, baseRate,
350356
refetchWindow: refetchPeerWindow,
351357
} = usePeerEstimate(hashrate);
352358

@@ -359,9 +365,10 @@ function Mining() {
359365
refetchPeerWindow();
360366
refetchEmission();
361367
refetchBurnStats();
368+
refetchTotalMinted();
362369
refetchMinerStats();
363370
refetchReward();
364-
}, [refetchWindow, refetchBlock, refetchPeerWindow, refetchEmission, refetchBurnStats, refetchMinerStats, refetchReward]);
371+
}, [refetchWindow, refetchBlock, refetchPeerWindow, refetchEmission, refetchBurnStats, refetchTotalMinted, refetchMinerStats, refetchReward]);
365372

366373
// Resync config + balance after proof submit (success or permanent failure)
367374
const resyncAfterProof = useCallback(() => {
@@ -1326,9 +1333,21 @@ function Mining() {
13261333
grossRewardPerProof, estimatedLiPerHour, proofLog, isNative, rpcOnline,
13271334
]);
13281335

1329-
// Format alpha/beta from micros to percentage
1330-
const alphaPercent = config ? (config.alpha / 10_000).toFixed(1) : '...';
1331-
const betaPercent = config ? (config.beta / 10_000).toFixed(1) : '...';
1336+
// PoW / PoS emission share (computed from actual rates)
1337+
const powSharePercent = emission && Number(emission.gross_rate) > 0
1338+
? ((Number(emission.mining_rate) / Number(emission.gross_rate)) * 100).toFixed(1)
1339+
: '...';
1340+
const posSharePercent = emission && Number(emission.gross_rate) > 0
1341+
? ((Number(emission.staking_rate) / Number(emission.gross_rate)) * 100).toFixed(1)
1342+
: '...';
1343+
1344+
// Miner's network share
1345+
const userDRate = hashrate > 0 && userDifficulty > 0
1346+
? userDifficulty * (hashrate / Math.pow(2, userDifficulty))
1347+
: 0;
1348+
const networkSharePercent = userDRate > 0 && dRate > 0
1349+
? Math.min((userDRate / dRate) * 100, 100)
1350+
: 0;
13321351

13331352
return (
13341353
<MainContainer>
@@ -1408,41 +1427,41 @@ function Mining() {
14081427
<span>{compactLi(liBalance)} LI</span>
14091428
</div>
14101429

1411-
{/* Emission info */}
1412-
{emission && (
1413-
<div className={styles.sectionBox}>
1414-
<span className={styles.sectionTitle}>Emission (per second)</span>
1415-
<div className={styles.statsGrid}>
1416-
<StatCard
1417-
label="Mining"
1418-
value={formatLi(emission.mining_rate)}
1419-
suffix="LI/s"
1420-
/>
1421-
<StatCard
1422-
label="Staking"
1423-
value={formatLi(emission.staking_rate)}
1424-
suffix="LI/s"
1425-
/>
1430+
{/* Token economy */}
1431+
<div className={styles.sectionBox}>
1432+
<span className={styles.sectionTitle}>Economy</span>
1433+
<div className={styles.statsGrid}>
1434+
{totalMinted && (
14261435
<StatCard
1427-
label="Gross rate"
1428-
value={formatLi(emission.gross_rate)}
1429-
suffix="LI/s"
1436+
label="Supply"
1437+
value={`${formatLi(totalMinted.total_minted)} / ${formatLi(totalMinted.supply_cap)}`}
1438+
suffix="LI"
14301439
/>
1440+
)}
1441+
<StatCard
1442+
label="PoW share"
1443+
value={`${powSharePercent}%`}
1444+
/>
1445+
<StatCard
1446+
label="PoS share"
1447+
value={`${posSharePercent}%`}
1448+
/>
1449+
{emission && (
14311450
<StatCard
14321451
label="Windowed fees"
14331452
value={formatLi(emission.windowed_fees)}
14341453
suffix="LI"
14351454
/>
1436-
{burnStats && (
1437-
<StatCard
1438-
label="Total burned"
1439-
value={formatLi(burnStats.total_burned)}
1440-
suffix="LI"
1441-
/>
1442-
)}
1443-
</div>
1455+
)}
1456+
{burnStats && (
1457+
<StatCard
1458+
label="Total burned"
1459+
value={formatLi(burnStats.total_burned)}
1460+
suffix="LI"
1461+
/>
1462+
)}
14441463
</div>
1445-
)}
1464+
</div>
14461465

14471466
{/* Network info */}
14481467
<div className={styles.sectionBox}>
@@ -1458,38 +1477,34 @@ function Mining() {
14581477
value={`${userDifficulty}`}
14591478
suffix={`bits (min: ${minDifficulty})`}
14601479
/>
1480+
<StatCard
1481+
label="Your share"
1482+
value={networkSharePercent > 0 ? `${networkSharePercent.toFixed(1)}%` : '\u2014'}
1483+
/>
14611484
<StatCard
14621485
label="Base rate"
14631486
value={baseRate !== '0' ? formatLi(baseRate) : '...'}
14641487
suffix="LI/bit"
14651488
/>
1489+
<StatCard label="Window" value={`${windowProofCount} / ${windowSize || '...'}`}>
1490+
{windowSize > 0 && (
1491+
<div className={styles.windowProgress}>
1492+
<div
1493+
className={styles.windowProgressFill}
1494+
style={{ width: `${Math.min((windowProofCount / windowSize) * 100, 100)}%` }}
1495+
/>
1496+
</div>
1497+
)}
1498+
</StatCard>
14661499
<StatCard
1467-
label="Window proofs"
1468-
value={windowProofCount}
1469-
/>
1470-
<StatCard
1471-
label="D-rate"
1472-
value={dRate > 0 ? dRate.toFixed(2) : '...'}
1473-
suffix="bits/s"
1474-
/>
1475-
<StatCard
1476-
label="Alpha"
1477-
value={`${alphaPercent}%`}
1478-
/>
1479-
<StatCard
1480-
label="Beta"
1481-
value={`${betaPercent}%`}
1500+
label="Net. throughput"
1501+
value={dRate > 0 ? (dRate * 3600).toFixed(0) : '...'}
1502+
suffix="bits/hr"
14821503
/>
14831504
<StatCard
14841505
label="All-time miners"
14851506
value={uniqueMiners}
14861507
/>
1487-
{latestBlock && (
1488-
<StatCard
1489-
label="Block"
1490-
value={latestBlock.height}
1491-
/>
1492-
)}
14931508
</div>
14941509
</div>
14951510

src/pages/Mining/components/StatCard.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@ type Props = {
44
label: string;
55
value: string | number;
66
suffix?: string;
7+
children?: React.ReactNode;
78
};
89

9-
function StatCard({ label, value, suffix }: Props) {
10+
function StatCard({ label, value, suffix, children }: Props) {
1011
return (
1112
<div className={styles.statCard}>
1213
<span className={styles.statCardLabel}>{label}</span>
1314
<span className={styles.statCardValue}>
1415
{value}
1516
{suffix && <span className={styles.statCardSuffix}> {suffix}</span>}
1617
</span>
18+
{children}
1719
</div>
1820
);
1921
}

src/pages/Mining/hooks/usePeerEstimate.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ function usePeerEstimate(localHashrate: number) {
6060
similarDevices,
6161
windowEntries,
6262
proofCount,
63+
windowSize: resp?.window_size ?? 0,
6364
baseRate: resp?.base_rate ?? '0',
6465
alpha: resp?.alpha ?? '0',
6566
beta: resp?.beta ?? '0',

src/pages/Mining/utils/formatLi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
export function compactLi(val: number): string {
66
if (val === 0) return '0';
77
const abs = Math.abs(val);
8+
if (abs >= 1e15) return `${(val / 1e15).toFixed(2)}P`;
89
if (abs >= 1e12) return `${(val / 1e12).toFixed(2)}T`;
910
if (abs >= 1e9) return `${(val / 1e9).toFixed(2)}B`;
1011
if (abs >= 1e6) return `${(val / 1e6).toFixed(2)}M`;
@@ -21,4 +22,4 @@ export function formatLi(amount: string | undefined): string {
2122
if (!amount) return '0';
2223
const val = Number(amount) / 1_000_000;
2324
return compactLi(val);
24-
}
25+
}

0 commit comments

Comments
 (0)