Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/BLS-Execution #166

Merged
merged 14 commits into from
Apr 18, 2023
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
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"quotes": ["error", "single"],
"no-duplicate-imports": "error",
"react/prop-types": 0,
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
},
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ build-storybook.log

# local-testnet
/local-testnet/testnet-data
/local-testnet/bls_to_execution_changes
/out
.env
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@uiw/react-textarea-code-editor": "^2.1.1",
"autoprefixer": "^9",
"axios": "^0.27.2",
"bootstrap-icons": "^1.9.1",
Expand Down
6 changes: 6 additions & 0 deletions src/api/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ export const fetchValidatorMetrics = async (
await axios.post(`${protocol}://${address}:${port}/lighthouse/ui/validator_metrics`, {
indices,
})

export const broadcastBlsChange = async ({ protocol, address, port }: Endpoint, data: any) =>
await axios.post(
`${protocol}://${address}:${port}/eth/v1/beacon/pool/bls_to_execution_changes`,
data,
)
116 changes: 116 additions & 0 deletions src/components/BlsExecutionModal/BlsExecutionModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import RodalModal from '../RodalModal/RodalModal'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { beaconNodeEndpoint, isBlsExecutionModal, isProcessBls } from '../../recoil/atoms'
import useMediaQuery from '../../hooks/useMediaQuery'
import Typography from '../Typography/Typography'
import CodeInput from '../CodeInput/CodeInput'
import ValidatorDisclosure from '../Disclosures/ValidatorDisclosure'
import { useState } from 'react'
import { MOCK_BLS_JSON, WithdrawalInfoLink } from '../../constants/constants'
import GradientHeader from '../GradientHeader/GradientHeader'
import { ButtonFace } from '../Button/Button'
import { useTranslation, Trans } from 'react-i18next'
import { broadcastBlsChange } from '../../api/beacon'
import { toast } from 'react-toastify'
import axios, { AxiosError } from 'axios'
import useLocalStorage from '../../hooks/useLocalStorage'
import { Storage } from '../../constants/enums'

const BlsExecutionModal = () => {
const { t } = useTranslation()
const beaconEndpoint = useRecoilValue(beaconNodeEndpoint)
const [isModal, toggleModal] = useRecoilState(isBlsExecutionModal)
const isTablet = useMediaQuery('(max-width: 1024px)')
const [blsJson, setJson] = useState(MOCK_BLS_JSON)
const setIsProcess = useSetRecoilState(isProcessBls)
const [, storeIsBlsProcessing] = useLocalStorage<boolean>(Storage.BLS_PROCESSING, false)

const closeModal = () => toggleModal(false)
const setJsonValue = (value: string) => setJson(value)

const handleError = (code?: number) => {
let message = t('error.unknownError', { type: 'BEACON' })

if (code === 400) {
message = t('error.executionFailure')
}

toast.error(message, {
position: 'top-right',
autoClose: 5000,
theme: 'colored',
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
})
}

const submitChange = async () => {
try {
const { status } = await broadcastBlsChange(beaconEndpoint, blsJson)

if (status != 200) {
handleError(status)
return
}

setIsProcess(true)
storeIsBlsProcessing(true)
} catch (e) {
if (axios.isAxiosError(e)) {
const axiosError = e as AxiosError

handleError(axiosError.response?.status)
}
}
}

return (
<RodalModal
isVisible={isModal}
styles={{
width: 'fit-content',
maxWidth: isTablet ? '448px' : '900px',
height: isTablet ? '540px' : 'max-content',
}}
onClose={closeModal}
>
<div>
<GradientHeader title={t('blsExecution.modal.title')} />
<div className='p-6 space-y-4'>
<Typography type='text-caption1'>
<Trans i18nKey='blsExecution.modal.subTitle'>
<br />
</Trans>
{' ---'}
</Typography>
<Typography className='w-3/4' type='text-caption1'>
{t('blsExecution.modal.description')}
</Typography>
<Typography className='w-3/4' type='text-caption1'>
<Trans i18nKey='blsExecution.modal.followLink'>
<a
className='text-blue-500 underline'
target='_blank'
rel='noreferrer'
href={WithdrawalInfoLink}
/>
</Trans>
</Typography>
<CodeInput value={blsJson} onChange={setJsonValue} />
</div>
{isModal && (
<div className='p-3 border-t-style100'>
<ValidatorDisclosure
onAccept={submitChange}
ctaType={ButtonFace.SECONDARY}
ctaText={t('blsExecution.modal.cta')}
/>
</div>
)}
</div>
</RodalModal>
)
}

export default BlsExecutionModal
31 changes: 31 additions & 0 deletions src/components/CodeInput/CodeInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import CodeEditor from '@uiw/react-textarea-code-editor'
import { FC } from 'react'

export interface CodeInputProps {
onChange: (value: string) => void
value: string
placeholder?: string
}

const CodeInput: FC<CodeInputProps> = ({ onChange, value, placeholder }) => {
return (
<div className='w-full max-h-48 overflow-scroll'>
<CodeEditor
rows={5}
className='dark:bg-dark750'
value={value}
language='json'
placeholder={placeholder}
onChange={(evn) => onChange(evn.target.value)}
padding={15}
style={{
fontSize: 12,
fontFamily:
'ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace',
}}
/>
</div>
)
}

export default CodeInput
10 changes: 6 additions & 4 deletions src/components/Disclosures/ValidatorDisclosure.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { useState } from 'react'
import ViewDisclosures from '../ViewDisclosures/ViewDisclosures'
import React, { FC, useState } from 'react'
import ViewDisclosures, { ViewDisclosuresProps } from '../ViewDisclosures/ViewDisclosures'
import DisclosureModal from '../DisclosureModal/DisclosureModal'
import validatorDisclosure from '../../assets/images/validatorDisclosure.png'
import Typography from '../Typography/Typography'
import { useTranslation } from 'react-i18next'
import useUiMode from '../../hooks/useUiMode'

const ValidatorDisclosure = () => {
type ValidatorDisclosureProps = Omit<ViewDisclosuresProps, 'onClick'>

const ValidatorDisclosure: FC<ValidatorDisclosureProps> = (props) => {
const { t } = useTranslation()
const { mode } = useUiMode()
const [isOpen, toggleModal] = useState(false)
Expand All @@ -16,7 +18,7 @@ const ValidatorDisclosure = () => {

return (
<>
<ViewDisclosures onClick={openModal} />
<ViewDisclosures {...props} onClick={openModal} />
<DisclosureModal
mode={mode}
backgroundImage={validatorDisclosure}
Expand Down
38 changes: 38 additions & 0 deletions src/components/GradientHeader/GradientHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Waves from '../../assets/images/waves.png'
import { FC } from 'react'
import addClassString from '../../utilities/addClassString'
import Typography from '../Typography/Typography'

export interface GradientHeaderProps {
className?: string
title?: string
}

const GradientHeader: FC<GradientHeaderProps> = ({ className, title }) => {
const classes = addClassString('w-full h-36 relative', [className])

return (
<div className={classes}>
<div
className='w-full h-full bg-no-repeat bg-right opacity-10'
style={{ backgroundImage: `url(${Waves})` }}
/>
<div className='absolute top-0 left-0 w-3/4 h-full bg-gradient-to-r from-white dark:from-dark750 via-white dark:via-dark750 to-transparent' />
{title && (
<div className='absolute top-0 left-0 w-full h-full flex items-center p-5 z-20'>
<Typography
type='text-subtitle1'
color='text-transparent'
darkMode='text-transparent'
className='primary-gradient-text capitalize'
fontWeight='font-light'
>
{title}
</Typography>
</div>
)}
</div>
)
}

export default GradientHeader
9 changes: 8 additions & 1 deletion src/components/SessionAuthModal/SessionAuthModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const SessionAuthModal: FC<SessionAuthModalProps> = ({

const handleError = () => {
playErrorAnim()
setPassword('')
setCount((prevState) => prevState + 1)
}

Expand All @@ -86,6 +87,7 @@ const SessionAuthModal: FC<SessionAuthModalProps> = ({
return
}
setCount(0)
setPassword('')
onSuccess(token)
} catch (e) {
handleError()
Expand Down Expand Up @@ -129,9 +131,14 @@ const SessionAuthModal: FC<SessionAuthModalProps> = ({
</>
)

const closeAuthModal = () => {
onClose?.()
setPassword('')
}

return (
<>
<RodalModal onClose={onClose} isVisible={isOpen}>
<RodalModal onClose={closeAuthModal} isVisible={isOpen}>
<div className='p-4'>
<div className='border-b-style500 pb-4 mb-4'>
<Typography
Expand Down
2 changes: 1 addition & 1 deletion src/components/ToolTip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const Tooltip: FC<TooltipProps> = ({
<div id={id} className={classes} data-tooltip-content={text}>
{children}
<TooltipComponent
className='shadow-xl'
className='shadow-xl z-50'
place={place}
style={{
maxWidth,
Expand Down
Loading