Skip to content

Commit

Permalink
feat(wallet-dashboard): select validator.
Browse files Browse the repository at this point in the history
  • Loading branch information
panteleymonchuk committed Nov 7, 2024
1 parent 1ff40ea commit 953a33e
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ function StakeDialog({
setView(View.SelectValidator);
}

const title = {
[View.SelectValidator]: 'Select Validator',
[View.EnterAmount]: 'Enter Amount',
};

return (
<Dialog open={isOpen} onOpenChange={() => handleClose()}>
{view === View.Details && stakedDetails && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@
// SPDX-License-Identifier: Apache-2.0

import React from 'react';
import { Button } from '@/components';
import { ImageIcon, ImageIconSize, formatPercentageDisplay } from '@iota/core';
import {
Card,
CardBody,
CardImage,
CardAction,
CardActionType,
CardType,
Badge,
BadgeType,
} from '@iota/apps-ui-kit';
import { formatAddress } from '@iota/iota-sdk/utils';
import { useValidatorInfo } from '@/hooks';

interface SelectValidatorViewProps {
validators: string[];
Expand All @@ -11,17 +23,47 @@ interface SelectValidatorViewProps {

function SelectValidatorView({ validators, onSelect }: SelectValidatorViewProps): JSX.Element {
return (
<div>
<h2>Select Validator</h2>
<div className="flex flex-col items-start gap-2">
{validators.map((validator) => (
<Button key={validator} onClick={() => onSelect(validator)}>
{validator}
</Button>
))}
</div>
<div className="flex w-full flex-col items-start gap-2">
{validators.map((validator) => (
<Validator key={validator} address={validator} onClick={onSelect} />
))}
</div>
);
}

function Validator({
address,
showActiveStatus,
onClick,
}: {
address: string;
showActiveStatus?: boolean;
onClick: (address: string) => void;
}) {
const { name, newValidator, isAtRisk, apy, isApyApproxZero } = useValidatorInfo(address);

const subtitle = showActiveStatus ? (
<div className="flex items-center gap-1">
{formatAddress(address)}
{newValidator && <Badge label="New" type={BadgeType.PrimarySoft} />}
{isAtRisk && <Badge label="At Risk" type={BadgeType.PrimarySolid} />}
</div>
) : (
formatAddress(address)
);
return (
<Card type={CardType.Default} onClick={() => onClick(address)}>
<CardImage>
<ImageIcon src={null} label={name} fallback={name} size={ImageIconSize.Large} />
</CardImage>
<CardBody title={name} subtitle={subtitle} isTextTruncated />
<CardAction
type={CardActionType.SupportingText}
title={formatPercentageDisplay(apy, '--', isApyApproxZero)}
iconAfterText
/>
</Card>
);
}

export default SelectValidatorView;
1 change: 1 addition & 0 deletions apps/wallet-dashboard/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './useSendCoinTransaction';
export * from './useCreateSendAssetTransaction';
export * from './useGetCurrentEpochStartTimestamp';
export * from './useTimelockedUnstakeTransaction';
export * from './useValidatorInfo';
46 changes: 46 additions & 0 deletions apps/wallet-dashboard/hooks/useValidatorInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
import { useMemo } from 'react';
import { useIotaClientQuery } from '@iota/dapp-kit';
import { useGetValidatorsApy } from '@iota/core';

export function useValidatorInfo(validatorAddress: string) {
const { data: system } = useIotaClientQuery('getLatestIotaSystemState');
const { data: rollingAverageApys } = useGetValidatorsApy();

const currentEpoch = Number(system?.epoch || 0);

const validatorSummary = useMemo(() => {
if (!system) return null;

return (
system.activeValidators.find(
(validator) => validator.iotaAddress === validatorAddress,
) || null
);
}, [validatorAddress, system]);

const stakingPoolActivationEpoch = Number(validatorSummary?.stakingPoolActivationEpoch || 0);

// flag as new validator if the validator was activated in the last epoch
// for genesis validators, this will be false
const newValidator = currentEpoch - stakingPoolActivationEpoch <= 1 && currentEpoch !== 0;

// flag if the validator is at risk of being removed from the active set
const isAtRisk = system?.atRiskValidators.some((item) => item[0] === validatorAddress);

const { apy, isApyApproxZero } = rollingAverageApys?.[validatorAddress] ?? {
apy: null,
};

return {
validatorSummary,
name: validatorSummary?.name || '',
stakingPoolActivationEpoch,
commission: validatorSummary ? Number(validatorSummary.commissionRate) / 100 : 0,
newValidator,
isAtRisk,
apy,
isApyApproxZero,
};
}

0 comments on commit 953a33e

Please sign in to comment.