diff --git a/packages/apps/human-app/frontend/src/modules/operator/hooks/index.ts b/packages/apps/human-app/frontend/src/modules/operator/hooks/index.ts new file mode 100644 index 0000000000..72939ae5ae --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/operator/hooks/index.ts @@ -0,0 +1,3 @@ +export * from './use-disable-operator'; +export * from './use-get-keys'; +export * from './use-web3-signin'; diff --git a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/add-keys.page.tsx b/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/add-keys.page.tsx deleted file mode 100644 index ad3c82ef2a..0000000000 --- a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/add-keys.page.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { Grid } from '@mui/material'; -import type { UseFormReturn } from 'react-hook-form'; -import { t } from 'i18next'; -import { Link } from 'react-router-dom'; -import { - PageCardError, - PageCardLoader, - PageCard, -} from '@/shared/components/ui/page-card'; -import { getErrorMessageForError, jsonRpcErrorHandler } from '@/shared/errors'; -import type { EditEthKVStoreValuesMutationData } from '@/modules/operator/hooks/use-edit-existing-keys'; -import { useEditExistingKeysMutationState } from '@/modules/operator/hooks/use-edit-existing-keys'; -import type { GetEthKVStoreValuesSuccessResponse } from '@/modules/operator/hooks/use-get-keys'; -import { useGetKeys } from '@/modules/operator/hooks/use-get-keys'; -import { routerPaths } from '@/router/router-paths'; -import { ExistingKeysForm } from '@/modules/operator/components/sign-up/add-keys/existing-keys-form'; -import { PendingKeysForm } from '@/modules/operator/components/sign-up/add-keys/pending-keys-form'; -import { Button } from '@/shared/components/ui/button'; -import { Alert } from '@/shared/components/ui/alert'; - -export type UseFormResult = UseFormReturn< - GetEthKVStoreValuesSuccessResponse, - EditEthKVStoreValuesMutationData ->; - -export function AddKeysOperatorPage() { - const { - data: keysData, - isError: isGetKeysError, - error: getKeysError, - isPending: isGetKeysPending, - } = useGetKeys(); - const editExistingKeysMutationState = useEditExistingKeysMutationState(); - - const errorAlert = editExistingKeysMutationState?.error ? ( - - {getErrorMessageForError( - editExistingKeysMutationState.error, - jsonRpcErrorHandler - )} - - ) : undefined; - - if (isGetKeysError) { - return ( - - ); - } - - if (isGetKeysPending) { - return ; - } - - return ( - -
- - ); -} - -export function Form({ - keysData, -}: { - keysData: GetEthKVStoreValuesSuccessResponse; -}) { - const hasSomeNotEmptyKeys = - Object.values(keysData).filter(Boolean).length > 0; - const hasSomePendingKeys = - Object.values(keysData).filter((value) => { - /** - * This check is necessary because TS can't infer - * "undefined" from optional object's property - */ - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (value === undefined) { - return false; - } - - return value.length === 0; - }).length > 0; - - return ( - -
- {hasSomeNotEmptyKeys ? : null} - {hasSomePendingKeys ? : null} - {hasSomeNotEmptyKeys && !hasSomePendingKeys ? ( - - ) : null} -
-
- ); -} diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/add-keys-form.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/add-keys-form.tsx new file mode 100644 index 0000000000..ac96f9ab04 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/add-keys-form.tsx @@ -0,0 +1,53 @@ +import { Grid } from '@mui/material'; +import { t } from 'i18next'; +import { Link } from 'react-router-dom'; +import { routerPaths } from '@/router/router-paths'; +import { Button } from '@/shared/components/ui/button'; +import { type GetEthKVStoreValuesSuccessResponse } from '@/modules/operator/hooks'; +import { ExistingKeysForm } from './existing-keys-form'; +import { PendingKeysForm } from './pending-keys-form'; + +export function AddKeysForm({ + keysData, +}: Readonly<{ + keysData: GetEthKVStoreValuesSuccessResponse; +}>) { + const hasSomeNotEmptyKeys = Object.values(keysData).some(Boolean); + const hasSomePendingKeys = Object.values(keysData).some((value) => { + /** + * This check is necessary because TS can't infer + * "undefined" from optional object's property + */ + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (value === undefined) { + return false; + } + + return value.length === 0; + }); + + return ( + +
+ {hasSomeNotEmptyKeys ? : null} + {hasSomePendingKeys ? : null} + {hasSomeNotEmptyKeys && !hasSomePendingKeys ? ( + + ) : null} +
+
+ ); +} diff --git a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/edit-existing-keys-form.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/edit-existing-keys-form.tsx similarity index 96% rename from packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/edit-existing-keys-form.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/edit-existing-keys-form.tsx index 654bccfed8..1ce6275f3e 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/edit-existing-keys-form.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/edit-existing-keys-form.tsx @@ -14,11 +14,8 @@ import { MultiSelect } from '@/shared/components/data-entry/multi-select'; import { JOB_TYPES } from '@/shared/consts'; import type { GetEthKVStoreValuesSuccessResponse } from '@/modules/operator/hooks/use-get-keys'; import { useColorMode } from '@/shared/contexts/color-mode'; -import { - order, - sortFormKeys, -} from '@/modules/operator/components/sign-up/add-keys/sort-form'; import { PercentsInputMask } from '@/shared/components/data-entry/input-masks'; +import { sortFormKeys, STORE_KEYS_ORDER } from '../../utils'; const OPTIONS = [ Role.EXCHANGE_ORACLE, @@ -79,20 +76,21 @@ const formInputsConfig: Record = { /> ), }; + export function EditExistingKeysForm({ existingKeysInitialState, formButtonProps, -}: { +}: Readonly<{ existingKeysInitialState: GetEthKVStoreValuesSuccessResponse; formButtonProps: CustomButtonProps; -}) { +}>) { const { colorPalette } = useColorMode(); const { errors } = useFormState(); const noChangesError = errors.form?.message as string; const sortedKeys = sortFormKeys( Object.keys(existingKeysInitialState) as EthKVStoreKeyValues[], - order + STORE_KEYS_ORDER ); return ( diff --git a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/edit-pending-keys-form.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/edit-pending-keys-form.tsx similarity index 95% rename from packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/edit-pending-keys-form.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/edit-pending-keys-form.tsx index a58c5560ce..d837a860af 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/edit-pending-keys-form.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/edit-pending-keys-form.tsx @@ -10,11 +10,8 @@ import { Select } from '@/shared/components/data-entry/select'; import { MultiSelect } from '@/shared/components/data-entry/multi-select'; import { JOB_TYPES } from '@/shared/consts'; import type { GetEthKVStoreValuesSuccessResponse } from '@/modules/operator/hooks/use-get-keys'; -import { - order, - sortFormKeys, -} from '@/modules/operator/components/sign-up/add-keys/sort-form'; import { PercentsInputMask } from '@/shared/components/data-entry/input-masks'; +import { sortFormKeys, STORE_KEYS_ORDER } from '../../utils'; const OPTIONS = [ Role.EXCHANGE_ORACLE, @@ -78,12 +75,12 @@ const formInputsConfig: Record = { export function EditPendingKeysForm({ existingKeysInitialState, -}: { +}: Readonly<{ existingKeysInitialState: GetEthKVStoreValuesSuccessResponse; -}) { +}>) { const sortedKeys = sortFormKeys( Object.keys(existingKeysInitialState) as EthKVStoreKeyValues[], - order + STORE_KEYS_ORDER ); return ( diff --git a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/existing-keys-form.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/existing-keys-form.tsx similarity index 86% rename from packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/existing-keys-form.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/existing-keys-form.tsx index bb4e3a2e06..f393a97be5 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/existing-keys-form.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/existing-keys-form.tsx @@ -3,15 +3,15 @@ import { Grid } from '@mui/material'; import { zodResolver } from '@hookform/resolvers/zod'; import type { UseFormReturn } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form'; -import type { EditEthKVStoreValuesMutationData } from '@/modules/operator/hooks/use-edit-existing-keys'; -import { - getEditEthKVStoreValuesMutationSchema, - useEditExistingKeysMutation, -} from '@/modules/operator/hooks/use-edit-existing-keys'; -import { ExistingKeys } from '@/modules/operator/components/sign-up/add-keys/existing-keys'; -import { EditExistingKeysForm } from '@/modules/operator/components/sign-up/add-keys/edit-existing-keys-form'; import type { GetEthKVStoreValuesSuccessResponse } from '@/modules/operator/hooks/use-get-keys'; import { useResetMutationErrors } from '@/shared/hooks/use-reset-mutation-errors'; +import { useEditExistingKeysMutation } from '../../hooks'; +import { + type EditEthKVStoreValuesMutationData, + getEditEthKVStoreValuesMutationSchema, +} from '../../schema'; +import { EditExistingKeysForm } from './edit-existing-keys-form'; +import { ExistingKeys } from './existing-keys'; export type UseFormResult = UseFormReturn< GetEthKVStoreValuesSuccessResponse, @@ -20,9 +20,9 @@ export type UseFormResult = UseFormReturn< export function ExistingKeysForm({ keysData, -}: { +}: Readonly<{ keysData: GetEthKVStoreValuesSuccessResponse; -}) { +}>) { const [editMode, setEditMode] = useState(false); const existingKeysMutation = useEditExistingKeysMutation(); const pendingKeysMutation = useEditExistingKeysMutation(); diff --git a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/existing-keys.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/existing-keys.tsx similarity index 97% rename from packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/existing-keys.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/existing-keys.tsx index d617ab6d0a..f6113b1f7d 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/existing-keys.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/existing-keys.tsx @@ -11,11 +11,8 @@ import { EmptyPlaceholder } from '@/shared/components/ui/empty-placeholder'; import type { GetEthKVStoreValuesSuccessResponse } from '@/modules/operator/hooks/use-get-keys'; import { Chips } from '@/shared/components/ui/chips'; import { Chip } from '@/shared/components/ui/chip'; -import { - order, - sortFormKeys, -} from '@/modules/operator/components/sign-up/add-keys/sort-form'; import { useColorMode } from '@/shared/contexts/color-mode'; +import { sortFormKeys, STORE_KEYS_ORDER } from '../../utils'; const existingKeysConfig: Record< EthKVStoreKeyValues, @@ -84,17 +81,17 @@ const existingKeysConfig: Record< export function ExistingKeys({ openEditMode, existingKeysInitialState, -}: { +}: Readonly<{ openEditMode: () => void; existingKeysInitialState: GetEthKVStoreValuesSuccessResponse; -}) { +}>) { const { colorPalette } = useColorMode(); const { getValues } = useFormContext(); const formValues = getValues(); const sortedKeys = sortFormKeys( Object.keys(existingKeysInitialState) as EthKVStoreKeyValues[], - order + STORE_KEYS_ORDER ); return ( diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/index.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/index.ts new file mode 100644 index 0000000000..91e7474c70 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/index.ts @@ -0,0 +1 @@ +export * from './add-keys-form'; diff --git a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/pending-keys-form.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/pending-keys-form.tsx similarity index 86% rename from packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/pending-keys-form.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/pending-keys-form.tsx index 697fb84cb8..75bb5e1d2a 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/pending-keys-form.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-keys/pending-keys-form.tsx @@ -1,21 +1,21 @@ import { t } from 'i18next'; import { zodResolver } from '@hookform/resolvers/zod'; import { FormProvider, useForm } from 'react-hook-form'; -import type { EditEthKVStoreValuesMutationData } from '@/modules/operator/hooks/use-edit-existing-keys'; -import { - setEthKVStoreValuesMutationSchema, - useEditExistingKeysMutation, -} from '@/modules/operator/hooks/use-edit-existing-keys'; import { Button } from '@/shared/components/ui/button'; -import { EditPendingKeysForm } from '@/modules/operator/components/sign-up/add-keys/edit-pending-keys-form'; import type { GetEthKVStoreValuesSuccessResponse } from '@/modules/operator/hooks/use-get-keys'; import { useResetMutationErrors } from '@/shared/hooks/use-reset-mutation-errors'; +import { useEditExistingKeysMutation } from '../../hooks'; +import { + type EditEthKVStoreValuesMutationData, + setEthKVStoreValuesMutationSchema, +} from '../../schema'; +import { EditPendingKeysForm } from './edit-pending-keys-form'; export function PendingKeysForm({ keysData, -}: { +}: Readonly<{ keysData: GetEthKVStoreValuesSuccessResponse; -}) { +}>) { const pendingKeysMutation = useEditExistingKeysMutation(); const pendingKeysMethods = useForm< diff --git a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-stake/buttons.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-stake/buttons.tsx similarity index 98% rename from packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-stake/buttons.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/components/add-stake/buttons.tsx index 9849f41820..b3a4b84a83 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-stake/buttons.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/components/add-stake/buttons.tsx @@ -8,10 +8,10 @@ import { routerPaths } from '@/router/router-paths'; export function Buttons({ openForm, stakedAmount, -}: { +}: Readonly<{ openForm: () => void; stakedAmount?: bigint; -}) { +}>) { const isStaked = stakedAmount ? stakedAmount > BigInt(0) : false; return ( ) { + const addStakeMutation = useAddStake(); const methods = useForm({ defaultValues: { diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/index.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/index.ts new file mode 100644 index 0000000000..f4a81ea7f7 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/index.ts @@ -0,0 +1,6 @@ +export * from './use-add-stake-mutation-state'; +export * from './use-add-stake'; +export * from './use-edit-existing-keys'; +export * from './use-get-stacked-amount'; +export * from './use-human-token-decimals'; +export * from './use-web3-signup'; diff --git a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-add-stake-mutation-state.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-add-stake-mutation-state.ts similarity index 88% rename from packages/apps/human-app/frontend/src/modules/operator/hooks/use-add-stake-mutation-state.ts rename to packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-add-stake-mutation-state.ts index 0a18642b74..240b1bfaf9 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-add-stake-mutation-state.ts +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-add-stake-mutation-state.ts @@ -3,7 +3,7 @@ import { useMutationState } from '@tanstack/react-query'; import type { MutationState } from '@tanstack/react-query'; import { useConnectedWallet } from '@/shared/contexts/wallet-connect'; import type { ResponseError } from '@/shared/types/global.type'; -import { type AddStakeCallArguments } from '@/modules/operator/hooks/use-add-stake'; +import { type AddStakeCallArguments } from '../schema'; export function useAddStakeMutationState() { const { address } = useConnectedWallet(); diff --git a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-add-stake.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-add-stake.ts similarity index 74% rename from packages/apps/human-app/frontend/src/modules/operator/hooks/use-add-stake.ts rename to packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-add-stake.ts index 675b9b8850..f6ca7cd4fe 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-add-stake.ts +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-add-stake.ts @@ -1,6 +1,4 @@ -import { z } from 'zod'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { t } from 'i18next'; import { useNavigate } from 'react-router-dom'; import { ethers } from 'ethers'; import { stakingStake } from '@/modules/smart-contracts/Staking/staking-stake'; @@ -10,37 +8,8 @@ import { hmTokenApprove } from '@/modules/smart-contracts/HMToken/hm-token-appro import type { ContractCallArguments } from '@/modules/smart-contracts/types'; import { routerPaths } from '@/router/router-paths'; import { hmTokenAllowance } from '@/modules/smart-contracts/HMToken/hm-token-allowance'; -import { useHMTokenDecimals } from '@/modules/operator/hooks/use-human-token-decimals'; - -type AmountValidation = z.ZodEffects< - z.ZodEffects, - string, - string ->; -type AmountField = z.infer; - -export const addStakeAmountCallArgumentsSchema = ( - decimals: number -): AmountValidation => - z - .string() - .refine((amount) => !amount.startsWith('-')) - .refine( - (amount) => { - const decimalPart = amount.toString().split('.')[1]; - if (!decimalPart) return true; - return decimalPart.length <= decimals; - }, - { - message: t('operator.stakeForm.invalidDecimals', { - decimals, - }), - } - ); - -export interface AddStakeCallArguments { - amount: AmountField; -} +import { type AddStakeCallArguments } from '../schema'; +import { useHMTokenDecimals } from './use-human-token-decimals'; async function addStakeMutationFn( data: AddStakeCallArguments & { @@ -85,7 +54,7 @@ async function addStakeMutationFn( return data; } -export function useAddStakeMutation() { +export function useAddStake() { const { chainId, address, diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-edit-existing-keys.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-edit-existing-keys.ts new file mode 100644 index 0000000000..17c989fff0 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-edit-existing-keys.ts @@ -0,0 +1,83 @@ +import { + useMutation, + useMutationState, + useQueryClient, +} from '@tanstack/react-query'; +import last from 'lodash/last'; +import { useNavigate } from 'react-router-dom'; +import type { JsonRpcSigner } from 'ethers'; +import { routerPaths } from '@/router/router-paths'; +import { useConnectedWallet } from '@/shared/contexts/wallet-connect'; +import { ethKvStoreSetBulk } from '@/modules/smart-contracts/EthKVStore/eth-kv-store-set-bulk'; +import { getContractAddress } from '@/modules/smart-contracts/get-contract-address'; +import { type EditEthKVStoreValuesMutationData } from '../schema'; + +function editExistingKeysMutationFn( + formData: EditEthKVStoreValuesMutationData, + userData: { + accountAddress: string; + chainId: number; + signer?: JsonRpcSigner; + } +) { + const contractAddress = getContractAddress({ + contractName: 'EthKVStore', + }); + + const keys: string[] = []; + const values: string[] = []; + + Object.entries(formData).forEach(([formFieldName, formFieldValue]) => { + if (!formFieldValue) { + return; + } + keys.push(formFieldName); + values.push(formFieldValue.toString()); + }); + + return ethKvStoreSetBulk({ + keys, + values, + contractAddress, + chainId: userData.chainId, + signer: userData.signer, + }); +} + +export function useEditExistingKeysMutation() { + const { + address, + chainId, + web3ProviderMutation: { data: web3Data }, + } = useConnectedWallet(); + const queryClient = useQueryClient(); + const navigate = useNavigate(); + + return useMutation({ + mutationFn: (data: EditEthKVStoreValuesMutationData) => + editExistingKeysMutationFn(data, { + accountAddress: address, + chainId, + signer: web3Data?.signer, + }), + onSuccess: async () => { + navigate(routerPaths.operator.editExistingKeysSuccess); + await queryClient.invalidateQueries(); + }, + onError: async () => { + await queryClient.invalidateQueries(); + }, + mutationKey: ['editKeys', address], + }); +} + +export function useEditExistingKeysMutationState() { + const { address } = useConnectedWallet(); + + const state = useMutationState({ + filters: { mutationKey: ['editKeys', address] }, + select: (mutation) => mutation.state, + }); + + return last(state); +} diff --git a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-get-stacked-amount.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-get-stacked-amount.ts similarity index 67% rename from packages/apps/human-app/frontend/src/modules/operator/hooks/use-get-stacked-amount.ts rename to packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-get-stacked-amount.ts index 1b927f7850..eef5582096 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-get-stacked-amount.ts +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-get-stacked-amount.ts @@ -1,20 +1,8 @@ import { useQuery } from '@tanstack/react-query'; -import { ethers } from 'ethers'; -import { t } from 'i18next'; import { stakingGetStakedTokens } from '@/modules/smart-contracts/Staking/staking-get-staked-tokens'; import { useConnectedWallet } from '@/shared/contexts/wallet-connect'; import { getContractAddress } from '@/modules/smart-contracts/get-contract-address'; -export const stakedAmountFormatter = (amount: bigint) => { - const amountAsString = ethers.formatEther(amount); - - if (amountAsString.split('.')[1] === '0') { - // decimals part should be omitted - return `${amountAsString.replace('.0', '')} ${t('inputMasks.humanCurrencySuffix')}`; - } - return `${ethers.formatEther(amount)} ${t('inputMasks.humanCurrencySuffix')}`; -}; - export function useGetStakedAmount() { const { address, diff --git a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-human-token-decimals.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-human-token-decimals.ts similarity index 100% rename from packages/apps/human-app/frontend/src/modules/operator/hooks/use-human-token-decimals.ts rename to packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-human-token-decimals.ts diff --git a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-web3-signup.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-web3-signup.ts similarity index 100% rename from packages/apps/human-app/frontend/src/modules/operator/hooks/use-web3-signup.ts rename to packages/apps/human-app/frontend/src/modules/signup/operator/hooks/use-web3-signup.ts diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/index.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/index.ts new file mode 100644 index 0000000000..7929623807 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/index.ts @@ -0,0 +1 @@ +export * from './views'; diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/schema/add-stake-amount-call-arguments-schema.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/schema/add-stake-amount-call-arguments-schema.ts new file mode 100644 index 0000000000..b3259f2670 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/schema/add-stake-amount-call-arguments-schema.ts @@ -0,0 +1,33 @@ +import { z } from 'zod'; +import { t } from 'i18next'; + +type AmountValidation = z.ZodEffects< + z.ZodEffects, + string, + string +>; + +type AmountField = z.infer; + +export const addStakeAmountCallArgumentsSchema = ( + decimals: number +): AmountValidation => + z + .string() + .refine((amount) => !amount.startsWith('-')) + .refine( + (amount) => { + const decimalPart = amount.toString().split('.')[1]; + if (!decimalPart) return true; + return decimalPart.length <= decimals; + }, + { + message: t('operator.stakeForm.invalidDecimals', { + decimals, + }), + } + ); + +export interface AddStakeCallArguments { + amount: AmountField; +} diff --git a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-edit-existing-keys.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/schema/eth-kv-store-values-mutation-schema.ts similarity index 62% rename from packages/apps/human-app/frontend/src/modules/operator/hooks/use-edit-existing-keys.ts rename to packages/apps/human-app/frontend/src/modules/signup/operator/schema/eth-kv-store-values-mutation-schema.ts index 53b789008b..836d3216e7 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/hooks/use-edit-existing-keys.ts +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/schema/eth-kv-store-values-mutation-schema.ts @@ -1,22 +1,10 @@ -import { - useMutation, - useMutationState, - useQueryClient, -} from '@tanstack/react-query'; -import last from 'lodash/last'; -import { useNavigate } from 'react-router-dom'; -import type { JsonRpcSigner } from 'ethers'; import { z, ZodError } from 'zod'; import { t } from 'i18next'; -import { routerPaths } from '@/router/router-paths'; -import { useConnectedWallet } from '@/shared/contexts/wallet-connect'; import { EthKVStoreKeys, JobType, Role, } from '@/modules/smart-contracts/EthKVStore/config'; -import { ethKvStoreSetBulk } from '@/modules/smart-contracts/EthKVStore/eth-kv-store-set-bulk'; -import { getContractAddress } from '@/modules/smart-contracts/get-contract-address'; import type { GetEthKVStoreValuesSuccessResponse } from '@/modules/operator/hooks/use-get-keys'; import { urlDomainSchema } from '@/shared/schemas'; @@ -114,73 +102,3 @@ export const getEditEthKVStoreValuesMutationSchema = ( return fieldsThatHasChanges; }); }; - -function editExistingKeysMutationFn( - formData: EditEthKVStoreValuesMutationData, - userData: { - accountAddress: string; - chainId: number; - signer?: JsonRpcSigner; - } -) { - const contractAddress = getContractAddress({ - contractName: 'EthKVStore', - }); - - const keys: string[] = []; - const values: string[] = []; - - Object.entries(formData).forEach(([formFieldName, formFieldValue]) => { - if (!formFieldValue) { - return; - } - keys.push(formFieldName); - values.push(formFieldValue.toString()); - }); - - return ethKvStoreSetBulk({ - keys, - values, - contractAddress, - chainId: userData.chainId, - signer: userData.signer, - }); -} - -export function useEditExistingKeysMutation() { - const { - address, - chainId, - web3ProviderMutation: { data: web3Data }, - } = useConnectedWallet(); - const queryClient = useQueryClient(); - const navigate = useNavigate(); - - return useMutation({ - mutationFn: (data: EditEthKVStoreValuesMutationData) => - editExistingKeysMutationFn(data, { - accountAddress: address, - chainId, - signer: web3Data?.signer, - }), - onSuccess: async () => { - navigate(routerPaths.operator.editExistingKeysSuccess); - await queryClient.invalidateQueries(); - }, - onError: async () => { - await queryClient.invalidateQueries(); - }, - mutationKey: ['editKeys', address], - }); -} - -export function useEditExistingKeysMutationState() { - const { address } = useConnectedWallet(); - - const state = useMutationState({ - filters: { mutationKey: ['editKeys', address] }, - select: (mutation) => mutation.state, - }); - - return last(state); -} diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/schema/index.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/schema/index.ts new file mode 100644 index 0000000000..0eeb42e505 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/schema/index.ts @@ -0,0 +1,2 @@ +export * from './add-stake-amount-call-arguments-schema'; +export * from './eth-kv-store-values-mutation-schema'; diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/utils/index.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/utils/index.ts new file mode 100644 index 0000000000..8c9632fa1b --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/utils/index.ts @@ -0,0 +1,2 @@ +export * from './sort-form'; +export * from './staked-amount-formatter'; diff --git a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/sort-form.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/utils/sort-form.ts similarity index 89% rename from packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/sort-form.ts rename to packages/apps/human-app/frontend/src/modules/signup/operator/utils/sort-form.ts index 1e05ad1ff1..57693c0a40 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/components/sign-up/add-keys/sort-form.ts +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/utils/sort-form.ts @@ -7,7 +7,8 @@ export const sortFormKeys = ( ): EthKVStoreKeyValues[] => { return keys.sort((a, b) => order.indexOf(a) - order.indexOf(b)); }; -export const order: EthKVStoreKeyValues[] = [ + +export const STORE_KEYS_ORDER: EthKVStoreKeyValues[] = [ EthKVStoreKeys.Fee, EthKVStoreKeys.PublicKey, EthKVStoreKeys.Url, diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/utils/staked-amount-formatter.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/utils/staked-amount-formatter.ts new file mode 100644 index 0000000000..c63e113faf --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/utils/staked-amount-formatter.ts @@ -0,0 +1,12 @@ +import { ethers } from 'ethers'; +import { t } from 'i18next'; + +export const stakedAmountFormatter = (amount: bigint) => { + const amountAsString = ethers.formatEther(amount); + + if (amountAsString.split('.')[1] === '0') { + // decimals part should be omitted + return `${amountAsString.replace('.0', '')} ${t('inputMasks.humanCurrencySuffix')}`; + } + return `${ethers.formatEther(amount)} ${t('inputMasks.humanCurrencySuffix')}`; +}; diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-keys.page.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-keys.page.tsx new file mode 100644 index 0000000000..64b60237c5 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-keys.page.tsx @@ -0,0 +1,56 @@ +import type { UseFormReturn } from 'react-hook-form'; +import { t } from 'i18next'; +import { + PageCardError, + PageCardLoader, + PageCard, +} from '@/shared/components/ui/page-card'; +import { getErrorMessageForError, jsonRpcErrorHandler } from '@/shared/errors'; +import { Alert } from '@/shared/components/ui/alert'; +import { + type GetEthKVStoreValuesSuccessResponse, + useGetKeys, +} from '@/modules/operator/hooks'; +import { useEditExistingKeysMutationState } from '../hooks'; +import { type EditEthKVStoreValuesMutationData } from '../schema'; +import { AddKeysForm } from '../components/add-keys'; + +export type UseFormResult = UseFormReturn< + GetEthKVStoreValuesSuccessResponse, + EditEthKVStoreValuesMutationData +>; + +export function AddKeysOperatorPage() { + const { + data: keysData, + isError: isGetKeysError, + error: getKeysError, + isPending: isGetKeysPending, + } = useGetKeys(); + const editExistingKeysMutationState = useEditExistingKeysMutationState(); + + const errorAlert = editExistingKeysMutationState?.error ? ( + + {getErrorMessageForError( + editExistingKeysMutationState.error, + jsonRpcErrorHandler + )} + + ) : undefined; + + if (isGetKeysError) { + return ( + + ); + } + + if (isGetKeysPending) { + return ; + } + + return ( + + + + ); +} diff --git a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/add-stake.page.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-stake.page.tsx similarity index 81% rename from packages/apps/human-app/frontend/src/modules/operator/views/sign-up/add-stake.page.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/views/add-stake.page.tsx index 76fc5e9fc7..c70abdf43b 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/add-stake.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/views/add-stake.page.tsx @@ -8,17 +8,16 @@ import { PageCard, } from '@/shared/components/ui/page-card'; import { getErrorMessageForError } from '@/shared/errors'; -import { Buttons } from '@/modules/operator/components/sign-up/add-stake/buttons'; -import { StakeForm } from '@/modules/operator/components/sign-up/add-stake/stake-form'; import { Alert } from '@/shared/components/ui/alert'; -import { - stakedAmountFormatter, - useGetStakedAmount, -} from '@/modules/operator/hooks/use-get-stacked-amount'; -import { useAddStakeMutationState } from '@/modules/operator/hooks/use-add-stake-mutation-state'; -import { useHMTokenDecimals } from '@/modules/operator/hooks/use-human-token-decimals'; import { useColorMode } from '@/shared/contexts/color-mode'; import { onlyDarkModeColor } from '@/shared/styles/dark-color-palette'; +import { + useAddStakeMutationState, + useGetStakedAmount, + useHMTokenDecimals, +} from '../hooks'; +import { StakeForm, Buttons } from '../components/add-stake'; +import { stakedAmountFormatter } from '../utils'; export function AddStakeOperatorPage() { const { colorPalette, isDarkMode } = useColorMode(); @@ -29,7 +28,9 @@ export function AddStakeOperatorPage() { error: getStackedAmountError, isPending: isGetStakedAmountPending, } = useGetStakedAmount(); + const addStakeMutationState = useAddStakeMutationState(); + const { data: decimalsData, error: decimalsDataError, @@ -37,14 +38,16 @@ export function AddStakeOperatorPage() { } = useHMTokenDecimals(); const getAlert = () => { - switch (true) { - case Boolean(addStakeMutationState?.error): + if (!addStakeMutationState) return undefined; + + switch (addStakeMutationState.status) { + case 'error': return ( - {getErrorMessageForError(addStakeMutationState?.error)} + {getErrorMessageForError(addStakeMutationState.error)} ); - case addStakeMutationState?.status === 'success': + case 'success': return ( {t('operator.stakeForm.successAlert', { diff --git a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/connect-wallet.page.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/views/connect-wallet.page.tsx similarity index 75% rename from packages/apps/human-app/frontend/src/modules/operator/views/sign-up/connect-wallet.page.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/views/connect-wallet.page.tsx index 08787c134d..a6f89751fc 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/connect-wallet.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/views/connect-wallet.page.tsx @@ -24,23 +24,21 @@ export function ConnectWalletOperatorPage() { } = useWalletConnect(); const getAlert = () => { - switch (true) { - case web3ProviderStatus === 'error': - return ( - - {getErrorMessageForError(web3ProviderError)} - - ); - case isConnected: - return ( - - {t('operator.connectWallet.successAlert')} - - ); + if (web3ProviderStatus === 'error') + return ( + + {getErrorMessageForError(web3ProviderError)} + + ); - default: - return undefined; - } + if (isConnected) + return ( + + {t('operator.connectWallet.successAlert')} + + ); + + return undefined; }; if (isConnected) { diff --git a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/edit-existing-keys-success.page.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/views/edit-existing-keys-success.page.tsx similarity index 97% rename from packages/apps/human-app/frontend/src/modules/operator/views/sign-up/edit-existing-keys-success.page.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/views/edit-existing-keys-success.page.tsx index a541510587..4c8bfac94d 100644 --- a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/edit-existing-keys-success.page.tsx +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/views/edit-existing-keys-success.page.tsx @@ -7,7 +7,6 @@ import { } from '@/shared/components/ui/page-card'; import { Button } from '@/shared/components/ui/button'; import { getErrorMessageForError } from '@/shared/errors'; -import { useWeb3SignUp } from '@/modules/operator/hooks/use-web3-signup'; import type { SignatureData } from '@/api/hooks/use-prepare-signature'; import { PrepareSignatureType, @@ -15,6 +14,7 @@ import { } from '@/api/hooks/use-prepare-signature'; import { Alert } from '@/shared/components/ui/alert'; import { useConnectedWallet } from '@/shared/contexts/wallet-connect'; +import { useWeb3SignUp } from '../hooks'; export function EditExistingKeysSuccessPage() { const { address, signMessage } = useConnectedWallet(); diff --git a/packages/apps/human-app/frontend/src/modules/signup/operator/views/index.ts b/packages/apps/human-app/frontend/src/modules/signup/operator/views/index.ts new file mode 100644 index 0000000000..0205a5c729 --- /dev/null +++ b/packages/apps/human-app/frontend/src/modules/signup/operator/views/index.ts @@ -0,0 +1,5 @@ +export * from './add-keys.page'; +export * from './add-stake.page'; +export * from './connect-wallet.page'; +export * from './set-up-operator.page'; +export * from './edit-existing-keys-success.page'; diff --git a/packages/apps/human-app/frontend/src/modules/operator/views/sign-up/set-up-operator.page.tsx b/packages/apps/human-app/frontend/src/modules/signup/operator/views/set-up-operator.page.tsx similarity index 100% rename from packages/apps/human-app/frontend/src/modules/operator/views/sign-up/set-up-operator.page.tsx rename to packages/apps/human-app/frontend/src/modules/signup/operator/views/set-up-operator.page.tsx diff --git a/packages/apps/human-app/frontend/src/router/routes.tsx b/packages/apps/human-app/frontend/src/router/routes.tsx index 2f46ca7010..d914cdbee9 100644 --- a/packages/apps/human-app/frontend/src/router/routes.tsx +++ b/packages/apps/human-app/frontend/src/router/routes.tsx @@ -14,11 +14,6 @@ import { WorkHeaderIcon, } from '@/shared/components/ui/icons'; import type { PageHeaderProps } from '@/shared/components/layout/protected/page-header'; -import { SetUpOperatorPage } from '@/modules/operator/views/sign-up/set-up-operator.page'; -import { EditExistingKeysSuccessPage } from '@/modules/operator/views/sign-up/edit-existing-keys-success.page'; -import { AddKeysOperatorPage } from '@/modules/operator/views/sign-up/add-keys.page'; -import { AddStakeOperatorPage } from '@/modules/operator/views/sign-up/add-stake.page'; -import { ConnectWalletOperatorPage } from '@/modules/operator/views/sign-up/connect-wallet.page'; import { Playground } from '@/modules/playground/views/playground.page'; import { HcaptchaLabelingPage, @@ -35,6 +30,13 @@ import { WorkerProfilePage } from '@/modules/worker/profile'; import { SignUpWorkerPage } from '@/modules/signup/worker'; import { OperatorProfilePage } from '@/modules/operator/profile'; import { HomePage } from '@/modules/homepage'; +import { + AddKeysOperatorPage, + AddStakeOperatorPage, + ConnectWalletOperatorPage, + EditExistingKeysSuccessPage, + SetUpOperatorPage, +} from '@/modules/signup/operator'; export const unprotectedRoutes: RouteProps[] = [ { diff --git a/packages/apps/human-app/frontend/src/shared/i18n/en.json b/packages/apps/human-app/frontend/src/shared/i18n/en.json index 08b9509a74..cb936198bf 100644 --- a/packages/apps/human-app/frontend/src/shared/i18n/en.json +++ b/packages/apps/human-app/frontend/src/shared/i18n/en.json @@ -342,7 +342,7 @@ }, "connectWallet": { "title": "Setup Operator", - "description": "Please use the button bellow to connect your wallet and sign up as an operator.", + "description": "Please use the button below to connect your wallet and sign up as an operator.", "connect": "Connect Wallet", "disconnect": "Disconnect", "successAlert": "Wallet successfully connected", @@ -352,7 +352,7 @@ "title": "Setup Operator", "formHeader": "Stake", "label": "Staked amount", - "description": "Please add bellow the amount to stake", + "description": "Please add the amount to stake below", "actionBtn": "Add Stake", "nextBtn": "Next", "skipBtn": "Skip" @@ -362,7 +362,7 @@ "skipBtn": "Skip", "nextBtn": "Next", "label": "Stake amount", - "description": "Please add bellow the amount to stake", + "description": "Please add the amount to stake below", "successAlert": "You have successfully staked {{amount}} HMT", "invalidDecimals": "Incorrect decimal expansion. HMT token has {{decimals}} decimals" },