Commit 57a1d38f authored by tom's avatar tom

refactor second step

parent a4276b6d
......@@ -197,7 +197,7 @@ const ContractCode = ({ addressHash }: Props) => {
<RawDataSnippet
data={ data.creation_bytecode }
title="Contract creation code"
rightSlot={ data.is_verified ? null : verificationButton }
rightSlot={ data.is_verified || data.is_self_destructed ? null : verificationButton }
beforeSlot={ data.is_self_destructed ? (
<Alert status="info" whiteSpace="pre-wrap" mb={ 3 }>
Contracts that self destruct in their constructors have no contract code published and cannot be verified.
......
......@@ -24,6 +24,10 @@ const AddressVerificationModal = ({ isOpen, onClose }: Props) => {
setStepIndex((prev) => prev + 1);
}, []);
const handleGoToThirdStep = React.useCallback(() => {
setStepIndex((prev) => prev + 1);
}, []);
const handleGoToPrevStep = React.useCallback(() => {
setStepIndex((prev) => prev - 1);
}, []);
......@@ -35,7 +39,7 @@ const AddressVerificationModal = ({ isOpen, onClose }: Props) => {
const steps = [
{ title: 'Verify new address ownership', content: <AddressVerificationStepAddress onContinue={ handleGoToSecondStep }/> },
{ title: 'Sign message', content: <AddressVerificationStepSignature { ...data }/> },
{ title: 'Copy and sign message', content: <AddressVerificationStepSignature { ...data } onContinue={ handleGoToThirdStep }/> },
{ title: 'Congrats! Address is verified.', content: <AddressVerificationStepSuccess onShowListClick={ handleClose } onAddTokenClick={ handleClose }/> },
];
const step = steps[stepIndex];
......
......@@ -3,17 +3,19 @@ import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import type { AddressVerificationFormSecondStepFields } from '../types';
import type { AddressVerificationFormSecondStepFields, RootFields } from '../types';
import InputPlaceholder from 'ui/shared/InputPlaceholder';
type Fields = RootFields & AddressVerificationFormSecondStepFields;
interface Props {
formState: FormState<AddressVerificationFormSecondStepFields>;
control: Control<AddressVerificationFormSecondStepFields>;
formState: FormState<Fields>;
control: Control<Fields>;
}
const AddressVerificationFieldMessage = ({ formState, control }: Props) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<AddressVerificationFormSecondStepFields, 'message'>}) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'message'>}) => {
const error = 'message' in formState.errors ? formState.errors.message : undefined;
return (
......@@ -22,14 +24,14 @@ const AddressVerificationFieldMessage = ({ formState, control }: Props) => {
{ ...field }
required
isInvalid={ Boolean(error) }
isDisabled={ formState.isSubmitting }
isDisabled
autoComplete="off"
maxH="105px"
/>
<InputPlaceholder text="Message to sign" error={ error }/>
</FormControl>
);
}, [ formState.errors, formState.isSubmitting ]);
}, [ formState.errors ]);
return (
<Controller
......
......@@ -3,19 +3,21 @@ import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import type { AddressVerificationFormSecondStepFields } from '../types';
import type { AddressVerificationFormSecondStepFields, RootFields } from '../types';
import { SIGNATURE_REGEXP } from 'lib/validations/signature';
import InputPlaceholder from 'ui/shared/InputPlaceholder';
type Fields = RootFields & AddressVerificationFormSecondStepFields;
interface Props {
formState: FormState<AddressVerificationFormSecondStepFields>;
control: Control<AddressVerificationFormSecondStepFields>;
formState: FormState<Fields>;
control: Control<Fields>;
}
const AddressVerificationFieldSignature = ({ formState, control }: Props) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<AddressVerificationFormSecondStepFields, 'signature'>}) => {
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'signature'>}) => {
const error = 'signature' in formState.errors ? formState.errors.signature : undefined;
return (
......
......@@ -4,7 +4,7 @@ import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import type {
AddressCheckResponseError,
AddressVerificationResponseError,
AddressCheckResponseSuccess,
AddressCheckStatusSuccess,
AddressVerificationFormFirstStepFields,
......@@ -12,6 +12,7 @@ import type {
} from '../types';
import appConfig from 'configs/app/config';
import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
import AddressVerificationFieldAddress from '../fields/AddressVerificationFieldAddress';
......@@ -35,10 +36,11 @@ const AddressVerificationStepAddress = ({ onContinue }: Props) => {
}, [ address, clearErrors ]);
const onFormSubmit: SubmitHandler<Fields> = React.useCallback(async(data) => {
try {
const body = {
contractAddress: data.address,
};
const response = await apiFetch<'address_verification', AddressCheckResponseSuccess, AddressCheckResponseError>('address_verification', {
const response = await apiFetch<'address_verification', AddressCheckResponseSuccess, AddressVerificationResponseError>('address_verification', {
fetchParams: { method: 'POST', body },
pathParams: { chainId: appConfig.network.id, type: ':prepare' },
});
......@@ -65,6 +67,11 @@ const AddressVerificationStepAddress = ({ onContinue }: Props) => {
}
onContinue({ ...response.result, address: data.address });
} catch (_error) {
const error = _error as ResourceError<AddressVerificationResponseError>;
setError('root', { type: 'manual', message: error.payload?.message || 'Oops! Something went wrong' });
}
}, [ apiFetch, onContinue, setError ]);
const onSubmit = handleSubmit(onFormSubmit);
......
import { Alert, Box, Button, Flex, Radio, RadioGroup } from '@chakra-ui/react';
import { Alert, Box, Button, chakra, Flex, Link, Radio, RadioGroup } from '@chakra-ui/react';
import React from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import { useSignMessage } from 'wagmi';
import type { AddressVerificationFormSecondStepFields, AddressCheckStatusSuccess, AddressVerificationFormFirstStepFields } from '../types';
import type {
AddressVerificationFormSecondStepFields,
AddressCheckStatusSuccess,
AddressVerificationFormFirstStepFields,
RootFields,
AddressVerificationResponseError,
AddressValidationResponseSuccess,
} from '../types';
import appConfig from 'configs/app/config';
import type { ResourceError } from 'lib/api/resources';
......@@ -13,35 +20,88 @@ import useApiFetch from 'lib/api/useApiFetch';
import AddressVerificationFieldMessage from '../fields/AddressVerificationFieldMessage';
import AddressVerificationFieldSignature from '../fields/AddressVerificationFieldSignature';
interface Props extends AddressVerificationFormFirstStepFields, AddressCheckStatusSuccess{}
type Fields = RootFields & AddressVerificationFormSecondStepFields;
const AddressVerificationStepSignature = ({ address, signingMessage }: Props) => {
interface Props extends AddressVerificationFormFirstStepFields, AddressCheckStatusSuccess{
onContinue: () => void;
}
const AddressVerificationStepSignature = ({ address, signingMessage, contractCreator, contractOwner, onContinue }: Props) => {
const [ signMethod, setSignMethod ] = React.useState<'wallet' | 'manually'>('wallet');
const [ error, setError ] = React.useState('');
const formApi = useForm<AddressVerificationFormSecondStepFields>({
const formApi = useForm<Fields>({
mode: 'onBlur',
defaultValues: {
message: signingMessage,
},
});
const { handleSubmit, formState, control, setValue, getValues } = formApi;
const { handleSubmit, formState, control, setValue, getValues, setError, clearErrors, watch } = formApi;
const apiFetch = useApiFetch();
const signature = watch('signature');
React.useEffect(() => {
clearErrors('root');
}, [ clearErrors, signature ]);
const onFormSubmit: SubmitHandler<Fields> = React.useCallback(async(data) => {
try {
const body = {
contractAddress: address,
message: data.message,
signature: data.signature,
};
const response = await apiFetch<'address_verification', AddressValidationResponseSuccess, AddressVerificationResponseError>('address_verification', {
fetchParams: { method: 'POST', body },
pathParams: { chainId: appConfig.network.id, type: ':verify' },
});
if (response.status !== 'SUCCESS') {
switch (response.status) {
case 'INVALID_SIGNATURE_ERROR': {
return setError('root', { type: 'manual', message: 'Invalid signature' });
}
case 'VALIDITY_EXPIRED_ERROR': {
return setError('root', { type: 'manual', message: 'Message validity expired' });
}
case 'INVALID_SIGNER_ERROR': {
const message = `Invalid signer ${ response.invalidSigner.signer }. Expected: ${ response.invalidSigner.validAddresses.join(', ') }.`;
return setError('root', { type: 'manual', message });
}
case 'UNKNOWN_STATUS': {
return setError('root', { type: 'manual', message: 'Oops! Something went wrong' });
}
default: {
return setError('root', { type: 'manual', message: response.payload?.message || 'Oops! Something went wrong' });
}
}
}
onContinue();
} catch (_error: unknown) {
const error = _error as ResourceError<AddressVerificationResponseError>;
setError('root', { type: 'manual', message: error.payload?.message || 'Oops! Something went wrong' });
}
}, [ address, apiFetch, onContinue, setError ]);
const onSubmit = handleSubmit(onFormSubmit);
const { signMessage, isLoading: isSigning } = useSignMessage({
onSuccess: (data) => {
setValue('signature', data);
onSubmit();
},
onError: (error) => {
setError((error as Error)?.message || 'Something went wrong');
return setError('root', { type: 'manual', message: (error as Error)?.message || 'Oops! Something went wrong' });
},
});
const handleSignMethodChange = React.useCallback((value: typeof signMethod) => {
setSignMethod(value);
setError('');
}, []);
clearErrors('root');
}, [ clearErrors ]);
const handleWeb3SignClick = React.useCallback(() => {
const message = getValues('message');
......@@ -49,34 +109,32 @@ const AddressVerificationStepSignature = ({ address, signingMessage }: Props) =>
}, [ getValues, signMessage ]);
const handleManualSignClick = React.useCallback(() => {
}, []);
const onFormSubmit: SubmitHandler<AddressVerificationFormSecondStepFields> = React.useCallback(async(data) => {
const body = {
contractAddress: address,
message: data.message,
signature: data.signature,
};
try {
await apiFetch('address_verification', {
fetchParams: { method: 'POST', body },
pathParams: { chainId: appConfig.network.id, type: ':verify' },
});
} catch (error: unknown) {
const _error = error as ResourceError<{message: string}>;
setError(_error.payload?.message || 'Oops! Something went wrong');
}
}, [ address, apiFetch ]);
const onSubmit = handleSubmit(onFormSubmit);
onSubmit();
}, [ onSubmit ]);
return (
<form noValidate onSubmit={ onSubmit }>
{ error && <Alert status="warning" mb={ 6 }>{ error }</Alert> }
{ formState.errors.root?.type === 'manual' && <Alert status="warning" mb={ 6 }>{ formState.errors.root.message }</Alert> }
<Box mb={ 8 }>
Please select the address to sign and copy the message below and sign it using the Blockscout sign message provider of your choice...
<span>Please select the address below you will use to sign, copy the message, and sign it using your preferred method. </span>
<Link>Additional instructions</Link>
</Box>
{ (contractOwner || contractCreator) && (
<Flex flexDir="column" rowGap={ 4 } mb={ 8 }>
{ contractCreator && (
<Box>
<chakra.span fontWeight={ 600 }>Contract creator: </chakra.span>
<chakra.span>{ contractCreator }</chakra.span>
</Box>
) }
{ contractOwner && (
<Box>
<chakra.span fontWeight={ 600 }>Contract owner: </chakra.span>
<chakra.span>{ contractOwner }</chakra.span>
</Box>
) }
</Flex>
) }
<Flex rowGap={ 5 } flexDir="column">
<AddressVerificationFieldMessage formState={ formState } control={ control }/>
<RadioGroup onChange={ handleSignMethodChange } value={ signMethod } display="flex" flexDir="column" rowGap={ 4 }>
......
......@@ -26,7 +26,29 @@ export type AddressCheckResponseSuccess = {
{ status: 'SOURCE_CODE_NOT_VERIFIED_ERROR' } |
{ status: 'INVALID_ADDRESS_ERROR' };
export interface AddressCheckResponseError {
export interface AddressVerificationResponseError {
code: number;
message: string;
}
export type AddressValidationResponseSuccess = {
status: 'SUCCESS';
result: {
verifiedAddress: {
chainId: string;
contractAddress: string;
userId: string;
verifiedDate: string;
};
};
} |
{
status: 'INVALID_SIGNER_ERROR';
invalidSigner: {
signer: string;
validAddresses: Array<string>;
};
} |
{ status: 'VALIDITY_EXPIRED_ERROR' } |
{ status: 'INVALID_SIGNATURE_ERROR' } |
{ status: 'UNKNOWN_STATUS' }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment