fix(validators): use weight field and correct uptime format for P-chain validators

- P-chain API returns weight not stakeAmount; fallback to weight when stakeAmount absent
- uptime from API is already 0-100 percentage, not 0-1 fraction; remove double-multiply
- mark connected and stakeAmount as optional to match actual API response shape
This commit is contained in:
Hanzo Dev
2026-03-01 12:45:34 -08:00
parent b4c9806af8
commit 586538da24
5 changed files with 15 additions and 15 deletions
+2 -2
View File
@@ -20,12 +20,12 @@ export interface PChainValidator {
readonly txID: string;
readonly startTime: string;
readonly endTime: string;
readonly stakeAmount: string;
readonly stakeAmount?: string; // may be absent; use weight instead
readonly nodeID: string;
readonly weight: string;
readonly delegationFee: string;
readonly potentialReward: string;
readonly connected: boolean;
readonly connected?: boolean;
readonly uptime: string;
readonly delegators: ReadonlyArray<PChainDelegator> | null;
}
+3 -3
View File
@@ -14,7 +14,6 @@ import { getPChain } from './client';
const VALIDATORS_STALE_TIME_MS = 60_000;
const VALIDATORS_QUERY_KEY = 'pchain:currentValidators' as const;
const UPTIME_PERCENTAGE_SCALE = 100;
const ZERO = BigInt(0);
function computeValidatorStats(
@@ -27,7 +26,7 @@ function computeValidatorStats(
let uptimeSum = 0;
for (const v of validators) {
totalStake += BigInt(v.stakeAmount);
totalStake += BigInt(v.stakeAmount ?? v.weight);
if (v.connected) {
connectedCount += 1;
@@ -43,8 +42,9 @@ function computeValidatorStats(
}
}
// uptime values from the API are already in percentage (0100); no scaling needed
const averageUptime = validators.length > 0 ?
(uptimeSum / validators.length) * UPTIME_PERCENTAGE_SCALE :
uptimeSum / validators.length :
0;
return {
+4 -4
View File
@@ -99,8 +99,8 @@ interface ActiveValidatorsTableProps {
const ActiveValidatorsTable = ({ validators, isLoading }: ActiveValidatorsTableProps) => {
const sorted = React.useMemo(
() => [ ...validators ].sort((a, b) => {
const aStake = BigInt(a.stakeAmount);
const bStake = BigInt(b.stakeAmount);
const aStake = BigInt(a.stakeAmount ?? a.weight);
const bStake = BigInt(b.stakeAmount ?? b.weight);
if (bStake > aStake) return 1;
if (bStake < aStake) return -1;
return 0;
@@ -181,14 +181,14 @@ const ActiveValidatorsTable = ({ validators, isLoading }: ActiveValidatorsTableP
{ truncateNodeId(v.nodeID) }
</Box>
<Box flex={ 1 } fontSize="sm" color="text.primary" textAlign={{ base: 'left', lg: 'right' }}>
{ formatStake(v.stakeAmount) } LUX
{ formatStake(v.stakeAmount ?? v.weight) } LUX
</Box>
<Box w={{ base: 'auto', lg: '120px' }} flexShrink={ 0 } fontSize="sm" color="text.secondary" textAlign={{ base: 'left', lg: 'right' }}>
{ v.delegationFee }%
</Box>
<Flex w={{ base: 'auto', lg: '80px' }} flexShrink={ 0 } justifyContent={{ base: 'flex-start', lg: 'center' }} alignItems="center">
<Box
bgColor={ v.connected ? 'green.400' : 'red.400' }
bgColor={ v.connected !== false ? 'green.400' : 'red.400' }
borderRadius="full"
boxSize="8px"
/>
+4 -4
View File
@@ -33,8 +33,8 @@ const ValidatorsList = ({ validators, isLoading }: ValidatorsListProps) => {
// Sort by stake desc, then filter by search
const filtered = React.useMemo(() => {
const sorted = [ ...validators ].sort((a, b) => {
const aStake = BigInt(a.stakeAmount);
const bStake = BigInt(b.stakeAmount);
const aStake = BigInt(a.stakeAmount ?? a.weight);
const bStake = BigInt(b.stakeAmount ?? b.weight);
if (bStake > aStake) return 1;
if (bStake < aStake) return -1;
return 0;
@@ -158,7 +158,7 @@ const ValidatorsList = ({ validators, isLoading }: ValidatorsListProps) => {
{ truncateNodeId(v.nodeID) }
</Box>
<Box flex={ 1 } fontSize="sm" color="text.primary" textAlign={{ base: 'left', lg: 'right' }}>
{ formatStake(v.stakeAmount) } LUX
{ formatStake(v.stakeAmount ?? v.weight) } LUX
</Box>
<Box w={{ base: 'auto', lg: '120px' }} flexShrink={ 0 } fontSize="sm" color="text.secondary" textAlign={{ base: 'left', lg: 'right' }}>
{ v.delegationFee }%
@@ -168,7 +168,7 @@ const ValidatorsList = ({ validators, isLoading }: ValidatorsListProps) => {
</Box>
<Flex w={{ base: 'auto', lg: '80px' }} flexShrink={ 0 } justifyContent={{ base: 'flex-start', lg: 'center' }} alignItems="center">
<Box
bgColor={ v.connected ? 'green.400' : 'red.400' }
bgColor={ v.connected !== false ? 'green.400' : 'red.400' }
borderRadius="full"
boxSize="8px"
/>
+2 -2
View File
@@ -3,7 +3,6 @@
const LUX_DECIMALS = 9;
const TRUNCATE_PREFIX_LEN = 12;
const TRUNCATE_SUFFIX_LEN = 6;
const UPTIME_PERCENTAGE_SCALE = 100;
const UPTIME_FRACTION_DIGITS = 2;
const STAKE_FRACTION_DIGITS = 2;
@@ -14,7 +13,8 @@ export function formatStake(nanoLux: bigint | string): string {
}
export function formatUptime(uptime: string): string {
return `${ (parseFloat(uptime) * UPTIME_PERCENTAGE_SCALE).toFixed(UPTIME_FRACTION_DIGITS) }%`;
// uptime is already in percentage format (0100), no scaling needed
return `${ parseFloat(uptime).toFixed(UPTIME_FRACTION_DIGITS) }%`;
}
export function truncateNodeId(nodeId: string): string {