Skip to content

Commit

Permalink
Feat/self consolidate (#317)
Browse files Browse the repository at this point in the history
  • Loading branch information
rickimoore authored Feb 22, 2025
1 parent a0d36d8 commit cddc7f7
Show file tree
Hide file tree
Showing 19 changed files with 314 additions and 155 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"autoprefixer": "^10.4.19",
"axios": "^0.27.2",
"chart.js": "^4.2.0",
"clsx": "^2.1.1",
"crypto-js": "^4.1.1",
"dotenv": "^16.4.5",
"eslint-plugin-prettier": "^5.2.3",
Expand Down
17 changes: 9 additions & 8 deletions src/components/CheckBox/CheckBox.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import clsx from "clsx";
import React, { FC, InputHTMLAttributes } from 'react'
import addClassString from '../../../utilities/addClassString'

export interface CheckBoxProps extends InputHTMLAttributes<HTMLInputElement> {
label?: string
containerClassName?: string
labelStyle?: string
inputClassName?: string
checkboxBorderClasses?: string
}

const CheckBox: FC<CheckBoxProps> = ({
Expand All @@ -14,16 +15,16 @@ const CheckBox: FC<CheckBoxProps> = ({
containerClassName,
labelStyle,
inputClassName,
checkboxBorderClasses,
...inputProps
}) => {
const containerClasses = addClassString('flex items-center cursor-pointer', [containerClassName])
const inputClasses = addClassString(
'w-5 h-5 border border-gray-300 cursor-pointer accent-primary bg-transparent border-style500 rounded focus:ring-purple-500 dark:focus:ring-purple-600 dark:ring-offset-gray-800 dark:border-gray-600',
[inputClassName],
const containerClasses = clsx('flex items-center cursor-pointer', containerClassName)
const inputClasses = clsx(
'w-5 h-5 cursor-pointer accent-primary bg-transparent rounded',
'focus:ring-purple-500 dark:focus:ring-purple-600 dark:ring-offset-gray-800', inputClassName,
checkboxBorderClasses || 'border border-gray-300 border-style500 dark:border-gray-600'
)
const labelClasses = addClassString('ml-2 cursor-pointer text-gray-900 dark:text-gray-300', [
labelStyle || 'text-sm font-medium',
])
const labelClasses = clsx('ml-2 cursor-pointer text-gray-900 dark:text-gray-300', labelStyle || 'text-sm font-medium')

return (
<div className={containerClasses}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ConsolidateValidator/Consolidate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import WalletActionGuard from '../WalletActionGuard/WalletActionGuard'
export interface ConsolidateViewProps {
targetPubKey: string
sourceValidator: ValidatorInfo
queueLength?: bigint | undefined
queueLength: bigint | TxHash
chainId: number
bufferPercentage: bigint
onSubmitRequest: (request: ConsolidationTx) => void
Expand Down
41 changes: 41 additions & 0 deletions src/components/PillText/PillText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import clsx from "clsx";
import {FC, useMemo} from "react";
import Tooltip from "../ToolTip/Tooltip";
import Typography, {TypographyColor} from "../Typography/Typography";

export interface PillTextProps {
id: string
isActive?: boolean
toolTipText: string
textPrefix: string
displayText: string
textColor?: TypographyColor | undefined
textDarkMode?: string
}

const PillText:FC<PillTextProps> = ({isActive, id, toolTipText, textPrefix, displayText, textColor, textDarkMode }) => {
const credentialPillClasses = clsx(
'py-1 px-2 rounded flex items-center space-x-2',
isActive ? 'bg-primary' : 'bg-dark100 dark:bg-dark700'
)

const toolTipStyle = useMemo(() => ({ fontSize: '11px' }), [])

return (
<Tooltip
place="top-start"
style={toolTipStyle}
id={id}
text={toolTipText}
>
<div className={credentialPillClasses}>
<Typography color={textColor} darkMode={textDarkMode} type="text-caption1.5">{textPrefix}:</Typography>
<Typography color={textColor} darkMode={textDarkMode} className="hidden @425:block" type="text-caption1.5">
{displayText}
</Typography>
</div>
</Tooltip>
)
}

export default PillText
25 changes: 15 additions & 10 deletions src/components/ToolTip/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC } from 'react'
import clsx from "clsx";
import React, {FC, useMemo} from 'react'
import { Tooltip as RTTooltip } from 'react-tooltip'
import addClassString from '../../../utilities/addClassString'
import { UiMode } from '../../constants/enums'
import useUiMode from '../../hooks/useUiMode'
import { OptionalString } from '../../types'
Expand Down Expand Up @@ -32,19 +32,24 @@ const Tooltip: FC<TooltipProps> = ({
const { mode } = useUiMode()
const isDark = (toolTipMode || mode) === UiMode.DARK

const containerClasses = clsx(className, cursor)
const toolTipClasses = clsx('shadow-xl z-50', tooltipClassName)

const toolTipStyles = useMemo(() => ({
maxWidth,
backgroundColor: isDark ? '#7C5FEB' : '#FFF',
color: isDark ? 'white' : 'black',
...style}
), [maxWidth, isDark, style])

return (
<div data-tooltip-id={id} className={`${className} ${cursor}`} data-tooltip-content={text}>
<div data-tooltip-id={id} className={containerClasses} data-tooltip-content={text}>
{children}
<RTTooltip
id={id}
place={place}
className={addClassString('shadow-xl z-50', [tooltipClassName])}
style={{
maxWidth,
backgroundColor: isDark ? '#7C5FEB' : '#FFF',
color: isDark ? 'white' : 'black',
...style,
}}
className={toolTipClasses}
style={toolTipStyles}
{...rest}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const ConsolidateView: FC<ConsolidateViewProps> = ({ validators, chainId }) => {
const { t } = useTranslation()
const [targetValidator, setTargetValidator] = useState<ValidatorInfo | undefined>(undefined)
const [sourceValidators, setSourceValidators] = useState<ValidatorInfo[]>([])
const activeValidators = useMemo(() => {
return validators.filter(({ status }) => status.includes('active') && !status.includes('exit'))
const eligibleValidators = useMemo(() => {
return validators.filter(({ status, withdrawalAddress }) => status.includes('active') && !status.includes('exit') && withdrawalAddress && (withdrawalAddress.startsWith('0x01') || withdrawalAddress.startsWith('0x02')))
}, [validators])
const steps = [
t('validatorManagement.consolidateView.steps.selectTarget'),
Expand All @@ -41,14 +41,14 @@ const ConsolidateView: FC<ConsolidateViewProps> = ({ validators, chainId }) => {
onNext={incrementStep}
targetValidator={targetValidator}
onSelect={updateTargetValidator}
validators={activeValidators}
validators={eligibleValidators}
/>
{step > 0 && (
<SelectSourceStep
onNext={incrementStep}
onBack={decrementStep}
onSelectTargetValidators={updateSourceValidators}
validators={activeValidators}
validators={eligibleValidators}
targetValidator={targetValidator}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { FC } from 'react'
import {FC, useMemo} from 'react'
import formatEthAddress from '../../../../../../utilities/formatEthAddress'
import { ValidatorInfo } from '../../../../../types/validator'
import CheckBox from '../../../../CheckBox/CheckBox'
import Tooltip from '../../../../ToolTip/Tooltip'
import Typography from '../../../../Typography/Typography'
import WithdrawalAddressPill from "../../../../WithdrawalAddress/WithdrawalAddressPill";

export interface SelectSourceRowProps {
source: ValidatorInfo
Expand All @@ -12,35 +13,44 @@ export interface SelectSourceRowProps {
}

const SelectSourceRow: FC<SelectSourceRowProps> = ({ source, isSelected, onSelect }) => {
const { pubKey, balance, name } = source
const { pubKey, balance, name, withdrawalAddress } = source
const selectSource = () => onSelect(source)
const formattedBalance = Math.round(balance)
const formattedPubKey = formatEthAddress(pubKey)

const tooltipStyles = useMemo(() => ({ fontSize: '11px' }), [])

return (
<div
onClick={selectSource}
className='w-full p-4 cursor-pointer dark:hover:bg-dark750 hover:bg-dark25 flex items-center justify-between'
>
<div className='flex items-center space-x-2'>
<div className='flex items-center space-x-3 border-r-style pr-3'>
<div className='flex items-center space-x-3 border-r-style pr-4'>
<CheckBox id={pubKey} readOnly checked={isSelected} />
<div className='h-8 w-8 rounded-full bg-gradient-to-r from-primary to-tertiary' />
<div className='h-6 w-6 hidden xl:block rounded-full bg-gradient-to-r from-primary to-tertiary' />
<Typography type='text-caption'>{name}</Typography>
</div>
<Tooltip
place='top-start'
style={{ fontSize: '11px' }}
id={`tool-source-${pubKey}`}
text={pubKey}
>
<Typography className='hidden @425:block' type='text-caption'>
{formattedPubKey}
</Typography>
</Tooltip>
<div className="border-r-style hidden md:block lg:hidden xl:block self-stretch flex items-center px-4">
<Tooltip
place='top-start'
style={tooltipStyles}
id={`tool-source-${pubKey}`}
text={pubKey}
>
<Typography type='text-caption'>
{formattedPubKey}
</Typography>
</Tooltip>
</div>
{withdrawalAddress ? (
<div className="pl-4 hidden md:block">
<WithdrawalAddressPill id={`${pubKey}-pill-text`} address={withdrawalAddress}/>
</div>
) : null}
</div>
<div>
<Typography type='text-caption'>{formattedBalance} ETH</Typography>
<Typography color="text-primary" darkMode="dark:text-primary" type='text-caption'>{formattedBalance} ETH</Typography>
</div>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FC, useMemo, useState } from 'react'
import {FC, useCallback, useMemo, useState} from 'react'
import { useTranslation } from 'react-i18next'
import addClassString from "../../../../../../utilities/addClassString";
import { ValidatorInfo } from '../../../../../types/validator'
import CheckBox from '../../../../CheckBox/CheckBox'
import Typography from '../../../../Typography/Typography'
Expand All @@ -24,12 +25,18 @@ const SelectSourceStep: FC<SelectSourceStepProps> = ({
}) => {
const { t } = useTranslation()
const [isSelectAll, setIsSelectAll] = useState(false)
const [isSelfConsolidate, setIsSelfConsolidate] = useState(false)
const [selectedSources, setSelectedSources] = useState<ValidatorInfo[]>([])
const canSelfConsolidate = targetValidator?.withdrawalAddress?.includes('0x01')

const availableSourceValidators = useMemo<ValidatorInfo[]>(() => {
return validators.filter(({ pubKey }) => pubKey !== targetValidator?.pubKey)
}, [validators, targetValidator])

const availableSelectedValidators = useMemo<ValidatorInfo[]>(() => {
return selectedSources.filter(({ pubKey }) => pubKey !== targetValidator?.pubKey)
}, [selectedSources, targetValidator])

const isAll = useMemo(() => {
return isSelectAll && selectedSources.length === availableSourceValidators.length
}, [isSelectAll, selectedSources, availableSourceValidators])
Expand All @@ -56,17 +63,27 @@ const SelectSourceStep: FC<SelectSourceStepProps> = ({
setIsSelectAll(!isAll)
}

const removeSource = (pubKey: string) =>
setSelectedSources((prev) => prev.filter((item) => item.pubKey !== pubKey))
const removeSource = useCallback((pubKey: string) => setSelectedSources((prev) => prev.filter((item) => item.pubKey !== pubKey)), [])

const stepBack = () => {
setSelectedSources([])
onBack()
}

const toggleSelfConsolidation = () => {
setIsSelfConsolidate(prev => !prev)
if(targetValidator) {
setSelectedSources(isSelfConsolidate ? [] : [targetValidator])
}
}

const eligibleValidatorListClasses = addClassString('flex flex-col border-style', [
isSelfConsolidate && 'opacity-20 pointer-events-none'
])

return (
<div className='w-full lg:h-full space-y-8 lg:space-y-0 flex flex-col lg:flex-row pt-4'>
<div className='flex-1 max-w-lg lg:max-w-none order-2 lg:order-1 pt-8 lg:pt-0 flex flex-col space-y-8'>
<div className='flex-1 @1600:max-w-2xl order-2 lg:order-1 pt-8 lg:pt-0 flex flex-col space-y-8'>
<div className='space-y-2'>
<Typography type='text-subtitle2'>
{t('validatorManagement.consolidateView.selectSources.title')}
Expand All @@ -75,7 +92,21 @@ const SelectSourceStep: FC<SelectSourceStepProps> = ({
{t('validatorManagement.consolidateView.selectSources.text')}
</Typography>
</div>
<div className='flex flex-col border-style'>
{canSelfConsolidate && (
<div className="border-style bg-primary100 p-4 flex space-x-4">
<CheckBox
id='self-consolidate'
checked={isSelfConsolidate}
checkboxBorderClasses="border border-gray-900 border-style500 dark:border-gray-400"
onChange={toggleSelfConsolidation}
/>
<div>
<Typography isBold type="text-caption1">{t('validatorManagement.consolidateView.selfConsolidate')}</Typography>
<Typography type="text-caption1">{t('validatorManagement.consolidateView.selfConsolidateHelperText')}</Typography>
</div>
</div>
)}
<div className={eligibleValidatorListClasses}>
<div className='px-4 py-3 border-b-style flex justify-between'>
<div className='flex items-center space-x-2'>
<i className='bi bi-list-ul text-black dark:text-dark500 text-xl' />
Expand All @@ -89,7 +120,7 @@ const SelectSourceStep: FC<SelectSourceStepProps> = ({
onChange={toggleIsSelectAll}
/>
</div>
<div className='h-full max-h-[348px] overflow-scroll'>
<div className={`h-full overflow-scroll ${canSelfConsolidate ? 'max-h-[248px]' : 'max-h-[348px]'}`}>
{availableSourceValidators.map((source) => (
<SelectSourceRow
key={source.pubKey}
Expand All @@ -103,12 +134,12 @@ const SelectSourceStep: FC<SelectSourceStepProps> = ({
</div>
</div>
</div>
<div className='w-48 h-full hidden lg:flex order-2 flex-col items-center justify-center'>
<div className='w-16 @1440:w-32 @1540:w-48 h-full hidden lg:flex order-2 flex-col items-center justify-center'>
<div className='w-10 flex items-center justify-center rounded h-10 bg-primary_10'>
<i className='bi-arrow-right font-bold text-primary' />
</div>
</div>
<div className='flex-1 max-w-lg lg:max-w-none order-1 lg:order-3 max-w-2xl space-y-6'>
<div className='flex-1 order-1 lg:order-3 lg:max-w-xl space-y-6'>
<div className='space-y-2 max-w-lg'>
<Typography type='text-subtitle2'>{t('primaryValidator')}</Typography>
<Typography type='text-caption'>
Expand All @@ -117,7 +148,7 @@ const SelectSourceStep: FC<SelectSourceStepProps> = ({
</div>
{targetValidator ? (
<SelectionDisplay
selectedSources={selectedSources}
selectedSources={availableSelectedValidators}
onRemoveSource={removeSource}
targetValidator={targetValidator}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const SelectedChip: FC<SelectedChipProps> = ({ validator, onRemove }) => {

return (
<div className='bg-primary_10 mr-4 mb-4 flex items-center rounded p-1 space-x-2'>
<div className='h-4 w-4 rounded-full bg-gradient-to-r from-primary to-tertiary' />
<div className='h-4 w-4 hidden lg:block rounded-full bg-gradient-to-r from-primary to-tertiary' />
<Typography type='text-caption2'>{name}</Typography>
<i
onClick={removeSource}
Expand Down
Loading

0 comments on commit cddc7f7

Please sign in to comment.