Skip to content
Draft
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
5 changes: 5 additions & 0 deletions app/dashboard/validators/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Typography from '../../../src/components/Typography/Typography'
import AddValidatorView from '../../../src/components/ValidatorManagement/AddValidatorView/AddValidatorView'
import ConsolidateView from '../../../src/components/ValidatorManagement/ConsolidateView/ConsolidateView'
import CreateValidatorView from '../../../src/components/ValidatorManagement/CreateValidatorView/CreateValidatorView'
import ImportValidatorView from '../../../src/components/ValidatorManagement/ImportValidatorView/ImportValidatorView'
import MainView from '../../../src/components/ValidatorManagement/MainView'
import ValidatorModal from '../../../src/components/ValidatorModal/ValidatorModal'
import ValidatorSummary from '../../../src/components/ValidatorSummary/ValidatorSummary'
Expand Down Expand Up @@ -288,6 +289,8 @@ const Main: FC<MainProps> = (props) => {
return t('validatorManagement.titles.add')
case ValidatorManagementView.CONSOLIDATE:
return 'Consolidate'
case ValidatorManagementView.IMPORT:
return 'Import Validator'
default:
return t('validatorManagement.titles.main')
}
Expand All @@ -310,6 +313,8 @@ const Main: FC<MainProps> = (props) => {
validators={validatorStates}
/>
)
case ValidatorManagementView.IMPORT:
return <ImportValidatorView />
default:
return (
<MainView
Expand Down
2 changes: 1 addition & 1 deletion backend/src/beacon/beacon.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class BeaconService {
url: `${this.beaconUrl}/eth/v1/node/syncing`,
}),
this.utilsService.sendHttpRequest({
url: `${this.beaconUrl}/lighthouse/eth1/syncing`,
url: `${this.beaconUrl}/lighthouse/syncing`,
}),
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const AddValidatorView: FC<AddValidatorViewProps> = ({ onChangeView }) => {
title: t('validatorManagement.addValidator.options.import.title'),
subTitle: t('validatorManagement.addValidator.options.import.subTitle'),
caption: t('validatorManagement.addValidator.options.import.caption'),
isDisabled: true,
isRecommended: false,
SVG: BlockChainSvg,
view: ValidatorManagementView.IMPORT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import StepOptions from '../StepOptions'

export interface MnemonicPhraseProps extends InputHTMLAttributes<HTMLTextAreaElement> {
onNextStep: () => void
onBackStep: () => void
onBackStep?: () => void
isActive: boolean
blsModule: IBls
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ImportView } from '../../../constants/enums'
import CreateKeystoreView from './views/CreateKeystoreView/CreateKeystoreView'
import SelectImportTypeView from './views/SelectImportTypeView'
import UploadKeystoreView from './views/UploadKeystoreView'

const ImportValidatorView = () => {
const { t } = useTranslation()
const [view, setView] = useState<ImportView>(ImportView.SELECT)

const moveToView = (nextView: ImportView) => setView(nextView)

if (view === ImportView.CREATE) {
return <CreateKeystoreView />
}

if (view === ImportView.UPLOAD) {
return <UploadKeystoreView />
}

return <SelectImportTypeView onChangeView={moveToView} />
}

export default ImportValidatorView
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { ChangeEvent, useCallback, useState } from 'react'
import { useRecoilValue } from 'recoil'
import { blsModuleAtom } from '../../../../../recoil/atoms'
import HorizontalStepper from '../../../../HorizontalStepper/HorizontalStepper'
import Spinner from '../../../../Spinner/Spinner'
import MnemonicPhrase from '../../../CreateValidatorView/Steps/MnemonicPhrase'
import ConfirmImport from './steps/ConfirmImport'
import ImportedKeystoreAuthentication from './steps/ImportedKeystoreAuthentication'
import ImportedMnemonicIndex from './steps/ImportedMnemonicIndex'
import ImportSuggestedFee from './steps/ImportSuggestedFee'

const CreateKeystoreView = () => {
const blsModule = useRecoilValue(blsModuleAtom)
const [keyStoreAuth, setKeyStoreAuth] = useState<string | undefined>('')
const [suggestedFee, setSuggestedFee] = useState<string | undefined>('')
const [keyPhrase, setKeyPhrase] = useState<string | undefined>('')
const [mnemonicIndex, setIndex] = useState<string | undefined>('')

const setCredential = (value: string | undefined) => setSuggestedFee(value)
const steps = ['Enter Mnemonic', 'Set Index', 'Set Authentication', 'Set Fee Recipient', 'Upload']

const setPhrase = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
setKeyPhrase(e.target.value)
}, [])

const onIndexChange = (index: string | undefined) => {
setIndex(index)
}

const onStoreKeyStoreAuth = (value: string | undefined) => setKeyStoreAuth(value)

return blsModule ? (
<HorizontalStepper steps={steps}>
{({ incrementStep, decrementStep, step }) =>
(
<>
<div className='py-8'>
<MnemonicPhrase
isActive={step === 0}
onNextStep={incrementStep}
value={keyPhrase}
onChange={setPhrase}
blsModule={blsModule}
/>
</div>
<ImportedMnemonicIndex
onNextStep={incrementStep}
onBackStep={decrementStep}
onSetIndex={onIndexChange}
mnemonicIndex={mnemonicIndex}
/>
<ImportedKeystoreAuthentication
onStoreAuth={onStoreKeyStoreAuth}
onNextStep={incrementStep}
onBackStep={decrementStep}
/>
<ImportSuggestedFee
address={suggestedFee}
onSetAddress={setCredential}
onNextStep={incrementStep}
onBackStep={decrementStep}
/>
{!!mnemonicIndex && !!keyPhrase ? (
<ConfirmImport
blsModule={blsModule}
suggestedFeeRecipient={suggestedFee}
password={keyStoreAuth}
index={mnemonicIndex}
keyPhrase={keyPhrase}
/>
) : null}
</>
) as any
}
</HorizontalStepper>
) : (
<div className='w-full h-full flex items-center justify-center'>
<Spinner />
</div>
)
}

export default CreateKeystoreView
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { IBls } from '@chainsafe/bls/types'
import { FC, useMemo } from 'react'
import useChainSafeKeygen from '../../../../../../hooks/useChainSafeKeygen'

export interface ConfirmImportProps {
keyPhrase: string
index: string
password: string | undefined
suggestedFeeRecipient: string | undefined
blsModule: IBls
}

const ConfirmImport: FC<ConfirmImportProps> = ({
keyPhrase,
index,
suggestedFeeRecipient,
password,
blsModule,
}) => {
const { deriveEIP2334SubKey, generateSigningPubKey } = useChainSafeKeygen(blsModule)

const eip2334SubKey = useMemo(() => {
return deriveEIP2334SubKey(keyPhrase)
}, [keyPhrase])

const pubKey = useMemo(() => {
return generateSigningPubKey(eip2334SubKey, Number(index))
}, [index])

return <div className='py-8'>{pubKey}</div>
}

export default ConfirmImport
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { FC, useState } from 'react'
import CredentialInput from '../../../../../ValidatorCredentialRow/CredentialInput'
import StepOptions, { StepOptionsProps } from '../../../../CreateValidatorView/StepOptions'

export interface ImportSuggestedFeeProps extends Omit<StepOptionsProps, 'isDisabledNext'> {
address: string | undefined
onSetAddress: (value: string | undefined) => void
}

const ImportSuggestedFee: FC<ImportSuggestedFeeProps> = ({
address,
onSetAddress,
onBackStep,
onNextStep,
}) => {
const [isVerified, setIsVerified] = useState(false)
const verifyWithdrawalCredential = (value: boolean) => setIsVerified(value)

return (
<div className='py-8'>
<CredentialInput
id='suggestedFeeInput'
value={address}
label='Suggested Fee Recipient'
onChange={onSetAddress}
onVerify={verifyWithdrawalCredential}
isVerifiedCredentials={isVerified}
/>
<StepOptions onBackStep={onBackStep} onNextStep={onNextStep} isDisabledNext={!address} />
</div>
)
}

export default ImportSuggestedFee
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import usePasswordConfirmation from '../../../../../../hooks/usePasswordConfirmation'
import Input from '../../../../../Input/Input'
import StepOptions, { StepOptionsProps } from '../../../../CreateValidatorView/StepOptions'

export interface ImportedKeystoreAuthenticationProps
extends Omit<StepOptionsProps, 'isDisabledNext'> {
onStoreAuth: (value: string) => void
}

const ImportedKeystoreAuthentication: FC<ImportedKeystoreAuthenticationProps> = ({
onBackStep,
onNextStep,
onStoreAuth,
}) => {
const { t } = useTranslation()
const { password, error, confirmationError, isValid, storeConfirmationPassword, storePassword } =
usePasswordConfirmation()
const moveToNextStep = () => {
onStoreAuth(password)
onNextStep?.()
}

return (
<div className='py-8'>
<div className='relative'>
{isValid && (
<i className='bi bi-check text-success text-caption 2xl:text-subtitle3 absolute top-2 -left-5' />
)}
<Input
placeholder={t('password')}
error={error}
className='text-caption1'
inputStyle='secondary'
type='password'
onChange={storePassword}
/>
</div>
<div className='relative'>
{isValid && (
<i className='bi bi-check text-success text-caption 2xl:text-subtitle3 absolute top-2 -left-5' />
)}
<Input
placeholder={t('confirmPassword')}
error={confirmationError}
inputStyle='secondary'
type='password'
className='text-caption1'
onChange={storeConfirmationPassword}
/>
</div>
<StepOptions onBackStep={onBackStep} onNextStep={moveToNextStep} isDisabledNext={!isValid} />
</div>
)
}

export default ImportedKeystoreAuthentication
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ChangeEvent, FC } from 'react'
import { useTranslation } from 'react-i18next'
import { MAX_MNEMONIC_INDEX } from '../../../../../../constants/constants'
import Typography from '../../../../../Typography/Typography'
import StepOptions from '../../../../CreateValidatorView/StepOptions'

export interface ImportedMnemonicIndexProps {
mnemonicIndex: string | undefined
onSetIndex: (index: string | undefined) => void
onNextStep: () => void
onBackStep: () => void
}

const ImportedMnemonicIndex: FC<ImportedMnemonicIndexProps> = ({
mnemonicIndex,
onSetIndex,
onBackStep,
onNextStep,
}) => {
const { t } = useTranslation()

const handleIndexChange = (event: ChangeEvent<HTMLInputElement>) => {
onSetIndex(event.target.value)
}

return (
<div className='py-8'>
<div>
<Typography type='text-caption1'>
{t('validatorManagement.mnemonicIndexing.title')} --
</Typography>
<Typography type='text-subtitle2' fontWeight='font-light'>
{t('validatorManagement.mnemonicIndexing.subTitle')}
</Typography>
</div>
<div className='flex w-full max-w-[450px]'>
<input
onChange={handleIndexChange}
min={0}
value={mnemonicIndex}
max={MAX_MNEMONIC_INDEX}
className='w-full text-dark900 dark:text-dark300 dark:bg-dark600_20 font-openSauce text-caption1 p-2 outline-none border-style'
type='number'
/>
</div>
<StepOptions
onBackStep={onBackStep}
onNextStep={onNextStep}
isDisabledNext={!mnemonicIndex}
/>
</div>
)
}

export default ImportedMnemonicIndex
Loading