Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 151 additions & 16 deletions build/ui/src/components/Setup/MinipoolCard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,135 @@
import React from "react";
import { Card, CardContent, Typography, Chip, Button } from "@mui/material";
import { Minipool } from "../../types/MinipoolStatus";
import { toEtherString } from "../../utils/Utils";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import "./minipool.css";
import { Button, Card, CardContent, Chip, Tooltip, Typography } from "@mui/material";
import CopyToClipboardButton from "../Buttons/CopyToClipboardButton";
import { Minipool } from "../../types/MinipoolStatus";
import { ethBalanceGreaterThanMinStake, shortenAddress, toEtherString } from "../../utils/Utils";
import ImportToSignerDialog from "./ImportToSignerDialog";
import "./minipool.css";

enum MinipoolStatus {
STAKING = "Staking",
DISSOLVED = "Dissolved",
PRELAUNCH = "Prelaunch",
FINALISED = "Finalised"
}

enum ValidatorStatus {
EXITED = "Exited",
ENQUEUED = "Enqueued",
ACTIVE = "Active",
READY_TO_CLOSE = "Ready to Close",
INACTIVE = "Inactive"
}

// Color theme constants
const COLOR_THEME = {
SUCCESS: "#81C784", // Green
WARNING: "#FFC107", // Amber
ERROR: "#E57373", // Red
INFO: "#FFB74D" // Orange
} as const;

// Status information interface
interface StatusInfo {
status: string;
color: string;
}

// Validator Status Service
class ValidatorStatusService {
/**
* Determines the minipool status and returns status info
*/
getMinipoolStatusInfo(minipoolData: Minipool): StatusInfo {
const status = this.determineMinipoolStatus(minipoolData);
const color = this.getMinipoolStatusColor(status);
return { status, color };
}

/**
* Determines the validator status and returns status info
*/
getValidatorStatusInfo(minipoolData: Minipool): StatusInfo {
const status = this.determineValidatorStatus(minipoolData);
const color = this.getValidatorStatusColor(status, minipoolData);
return { status, color };
}

/**
* Determines the minipool status based on finalised state
*/
private determineMinipoolStatus(minipoolData: Minipool): string {
return minipoolData.finalised ? MinipoolStatus.FINALISED : minipoolData.status.status;
}

/**
* Determines the validator status with priority-based logic
*/
private determineValidatorStatus(minipoolData: Minipool): string {
// Priority 1: Check if finalised
if (minipoolData.finalised) {
return ValidatorStatus.EXITED;
}

// Priority 2: Check if enqueued (staking but no validator index)
if (minipoolData.status.status === MinipoolStatus.STAKING && !minipoolData.validator.index) {
return ValidatorStatus.ENQUEUED;
}

// Priority 3: Check if validator is active
if (minipoolData.validator.active) {
return ValidatorStatus.ACTIVE;
}

// Priority 4: Check if ready to close (staking with sufficient balance)
if (minipoolData.status.status === MinipoolStatus.STAKING &&
ethBalanceGreaterThanMinStake(minipoolData.balances.eth)) {
return ValidatorStatus.READY_TO_CLOSE;
}

// Default: Inactive
return ValidatorStatus.INACTIVE;
}

/**
* Gets the color for minipool status
*/
private getMinipoolStatusColor(status: string): string {
switch (status) {
case MinipoolStatus.STAKING:
return COLOR_THEME.SUCCESS;
case MinipoolStatus.DISSOLVED:
return COLOR_THEME.ERROR;
case MinipoolStatus.FINALISED:
return COLOR_THEME.WARNING;
default:
return COLOR_THEME.INFO;
}
}

/**
* Gets the color for validator status
*/
private getValidatorStatusColor(validatorStatus: string, minipoolData: Minipool): string {
// Success color for finalised or active validators
if (minipoolData.finalised || minipoolData.validator.active) {
return COLOR_THEME.SUCCESS;
}

// Warning color for prelaunch, enqueued, or ready to close
if (minipoolData.status.status === MinipoolStatus.PRELAUNCH ||
[ValidatorStatus.ENQUEUED, ValidatorStatus.READY_TO_CLOSE].includes(validatorStatus as ValidatorStatus)) {
return COLOR_THEME.WARNING;
}

// Error color for inactive
return COLOR_THEME.ERROR;
}
};

// Initialize status service
const statusService = new ValidatorStatusService();

function MinipoolCard({
data,
Expand All @@ -17,13 +142,11 @@ function MinipoolCard({
}): JSX.Element {
const [importToSignerDialogOpen, setImportToSignerDialogOpen] =
React.useState<boolean>(false);

const backgroundColor =
data.status.status === "Staking"
? "#81C784"
: data.status.status === "Dissolved"
? "#E57373"
: "#FFB74D";
const [showAddress, setShowAddress] = React.useState<boolean>(false);

// Get status information using the service
const minipoolStatusInfo = statusService.getMinipoolStatusInfo(data);
const validatorStatusInfo = statusService.getValidatorStatusInfo(data);

return (
<Card
Expand All @@ -36,16 +159,28 @@ function MinipoolCard({
>
<CardContent>
<div className="chip-container">
<Chip label={`#${data.validator.index}`} />
<div className="chip-with-clipboard-container">
<Tooltip title={showAddress ? "Minipool Address" : "Validator Index"}>
<Chip
label={showAddress ? shortenAddress(data.address) : `#${data.validator.index}`}
onClick={() => setShowAddress(!showAddress)}
sx={{ cursor: 'pointer' }}
/>
</Tooltip>
<CopyToClipboardButton
value={showAddress ? data.address : data.validator.index.toString()}
fontSize="small"
/>
</div>
<Chip
label={data.finalised ? "Finalised" : data.status.status}
sx={{ backgroundColor: data.finalised ? "#FFC107" : backgroundColor }}
label={minipoolStatusInfo.status}
sx={{ backgroundColor: minipoolStatusInfo.color }}
/>
</div>
<div className="validator-status">
<Chip
label={data.finalised ? "Exited" : (data.validator.active ? "Active" : "Inactive")}
sx={{ backgroundColor: data.finalised || data.validator.active ? "#81C784" : "#E57373" }}
label={validatorStatusInfo.status}
sx={{ backgroundColor: validatorStatusInfo.color }}
/>
</div>

Expand Down
6 changes: 6 additions & 0 deletions build/ui/src/components/Setup/minipool.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,17 @@
margin-bottom: 0.5rem;
}

.chip-with-clipboard-container {
display: flex;
align-items: center;
}

.explorer-button-container {
margin-top: 1rem;
}

.minipool-ether {
margin-top: 0.5rem !important;
margin-bottom: 0.5rem !important;
font-weight: bold !important;
}
Expand Down
5 changes: 5 additions & 0 deletions build/ui/src/utils/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@ export function enoughEthBalance(
return is8EthPool ? ethBalance >= minimum8Eth : ethBalance >= minimum16Eth;
}

export function ethBalanceGreaterThanMinStake(ethBalance: number): boolean {
const minStake = BigInt(32 * 10 ** 18);
return BigInt(ethBalance) > minStake;
}

export const escapeNewLine = (text: string) => text.replace(/(\\n)/g, "\n");