diff --git a/build/ui/src/components/Setup/MinipoolCard.tsx b/build/ui/src/components/Setup/MinipoolCard.tsx index c3f873c..ed8673f 100644 --- a/build/ui/src/components/Setup/MinipoolCard.tsx +++ b/build/ui/src/components/Setup/MinipoolCard.tsx @@ -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, @@ -17,13 +142,11 @@ function MinipoolCard({ }): JSX.Element { const [importToSignerDialogOpen, setImportToSignerDialogOpen] = React.useState(false); - - const backgroundColor = - data.status.status === "Staking" - ? "#81C784" - : data.status.status === "Dissolved" - ? "#E57373" - : "#FFB74D"; + const [showAddress, setShowAddress] = React.useState(false); + + // Get status information using the service + const minipoolStatusInfo = statusService.getMinipoolStatusInfo(data); + const validatorStatusInfo = statusService.getValidatorStatusInfo(data); return (
- +
+ + setShowAddress(!showAddress)} + sx={{ cursor: 'pointer' }} + /> + + +
diff --git a/build/ui/src/components/Setup/minipool.css b/build/ui/src/components/Setup/minipool.css index 2736bc6..2d08b3c 100644 --- a/build/ui/src/components/Setup/minipool.css +++ b/build/ui/src/components/Setup/minipool.css @@ -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; } diff --git a/build/ui/src/utils/Utils.ts b/build/ui/src/utils/Utils.ts index 4476538..1762f6b 100644 --- a/build/ui/src/utils/Utils.ts +++ b/build/ui/src/utils/Utils.ts @@ -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");